Exemple #1
0
class AppHookConfig(models.Model):
    """
    This is the generic (abstract) model that holds the configurations for each AppHookConfig
    concrete model
    """
    type = models.CharField(
        _('Type'),
        max_length=100,
    )
    namespace = models.CharField(
        _('Instance namespace'),
        default=None,
        max_length=100,
        unique=True,
    )
    app_data = AppDataField()

    cmsapp = None

    class Meta:
        verbose_name = _('Apphook config')
        verbose_name_plural = _('Apphook configs')
        unique_together = ('type', 'namespace')
        abstract = True

    def save(self, *args, **kwargs):
        self.type = '%s.%s' % (self.__class__.__module__,
                               self.__class__.__name__)
        super(AppHookConfig, self).save(*args, **kwargs)

    def __str__(self):
        if self.cmsapp:
            return '%s / %s' % (self.cmsapp.name, self.namespace)
        else:
            return '%s / %s' % (self.type, self.namespace)

    def __getattr__(self, item):
        """
        This allows to access config form attribute as normal model fields

        :param item:
        :return:
        """
        try:
            return getattr(self.app_data.config, item)
        except Exception:
            raise AttributeError('attribute %s not found' % item)
class FlatComment(models.Model):
    site = SiteForeignKey(default=Site.objects.get_current)

    content_type = ContentTypeForeignKey()
    object_id = models.CharField(max_length=255)
    content_object = CachedGenericForeignKey('content_type', 'object_id')

    comment = models.TextField()

    submit_date = models.DateTimeField(default=None)
    user = CachedForeignKey(User)
    is_public = models.BooleanField(default=True)

    app_data = AppDataField()

    def _comment_list(self, reversed=False):
        if not hasattr(self, '__comment_list'):
            self.__comment_list = CommentList(self.content_type,
                                              self.object_id, reversed)
        return self.__comment_list

    def post(self, request=None):
        return self._comment_list().post_comment(self, request)

    def moderate(self, user=None, commit=True):
        return self._comment_list().moderate_comment(self, user, commit)

    def get_absolute_url(self, reversed=False):
        return '%s?p=%d' % (resolver.reverse(
            self.content_object,
            'comments-list'), self._comment_list(reversed).page_index(self.pk))

    def delete(self):
        self.moderate()
        super(FlatComment, self).delete()

    def save(self, **kwargs):
        if self.submit_date is None:
            self.submit_date = timezone.now()
        super(FlatComment, self).save(**kwargs)
Exemple #3
0
class Result(models.Model):
    quiz = CachedForeignKey(Quiz)
    choice = models.IntegerField()

    photo = CachedForeignKey(Photo,
                             blank=True,
                             null=True,
                             on_delete=models.SET_NULL)
    title = models.CharField(max_length=200)
    text = models.TextField()

    # generic JSON field to store app specific data
    app_data = AppDataField(default='{}', editable=False)

    class Meta:
        unique_together = ((
            'quiz',
            'choice',
        ), )

    def get_absolute_url(self):
        return resolver.reverse(self.quiz, 'quiz-result', choice=self.choice)
Exemple #4
0
class Photo(models.Model):
    """
    Represents original (unformated) photo uploaded by user. Used as source
    object for all the formatting stuff and to keep the metadata common to
    all related ``FormatedPhoto`` objects.
    """
    title = models.CharField(_('Title'), max_length=200)
    description = models.TextField(_('Description'), blank=True)
    slug = models.SlugField(_('Slug'), max_length=255)
    # save it to YYYY/MM/DD structure
    image = models.ImageField(_('Image'), upload_to=upload_to,
        max_length=255, height_field='height', width_field='width')
    width = models.PositiveIntegerField(editable=False)
    height = models.PositiveIntegerField(editable=False)

    # important area
    important_top = models.PositiveIntegerField(null=True, blank=True)
    important_left = models.PositiveIntegerField(null=True, blank=True)
    important_bottom = models.PositiveIntegerField(null=True, blank=True)
    important_right = models.PositiveIntegerField(null=True, blank=True)

    # Authors and Sources
    authors = models.ManyToManyField(Author, verbose_name=_('Authors'), related_name='photo_set')
    source = models.ForeignKey(Source, blank=True, null=True, verbose_name=_('Source'), on_delete=models.SET_NULL)

    created = models.DateTimeField(auto_now_add=True)

    # generic JSON field to store app cpecific data
    app_data = AppDataField()

    class Meta:
        verbose_name = _('Photo')
        verbose_name_plural = _('Photos')

    def __unicode__(self):
        return self.title

    def get_absolute_url(self):
        return self.image.url

    def get_image_info(self):
        return {
            'url': self.image.url,
            'width': self.width,
            'height': self.height,
        }

    def _get_image(self):
        if not hasattr(self, '_pil_image'):
            self.image.open()
            self._pil_image = Image.open(self.image)
        return self._pil_image

    def save(self, **kwargs):
        """Overrides models.Model.save.

        - Generates slug.
        - Saves image file.
        """
        if not self.width or not self.height:
            self.width, self.height = self.image.width, self.image.height

        # prefill the slug with the ID, it requires double save
        if not self.id:
            img = self.image

            # store dummy values first...
            w, h = self.width, self.height
            self.image = ''
            self.width, self.height = w, h
            self.slug = ''

            super(Photo, self).save(force_insert=True)

            # ... so that we can generate the slug
            self.slug = str(self.id) + '-' + slugify(self.title)
            # truncate slug in order to fit in an ImageField and/or paths in Redirects
            self.slug = self.slug[:64]
            # .. tha will be used in the image's upload_to function
            self.image = img
            # and the image will be saved properly
            super(Photo, self).save(force_update=True)
        else:
            try:
                old = Photo.objects.get(pk=self.pk)

                force_update = True
                # delete formatedphotos if new image was uploaded
                if old.image != self.image:
                    for f_photo in self.formatedphoto_set.all():
                        f_photo.delete()
            except Photo.DoesNotExist:
                # somebody is just trying to create new model with given PK
                force_update = False

            super(Photo, self).save(force_update=force_update)

    def ratio(self):
        "Return photo's width to height ratio"
        if self.height:
            return float(self.width) / self.height
        else:
            return None

    def get_formated_photo(self, format):
        "Return formated photo"
        return FormatedPhoto.objects.get_photo_in_format(self, format)
Exemple #5
0
class Publishable(models.Model):
    app_data = AppDataField()
Exemple #6
0
class Category(models.Model):
    app_data = AppDataField()
Exemple #7
0
class Publishable(models.Model):
    """
    Base class for all objects that can be published in Ella.
    """
    box_class = staticmethod(PublishableBox)

    content_type = ContentTypeForeignKey(editable=False)
    target = CachedGenericForeignKey('content_type', 'id')

    category = CategoryForeignKey(verbose_name=_('Category'))

    # Titles
    title = models.CharField(_('Title'), max_length=255)
    slug = models.SlugField(_('Slug'),
                            max_length=255,
                            validators=[validate_slug])

    # Authors and Sources
    authors = models.ManyToManyField(Author, verbose_name=_('Authors'))
    source = CachedForeignKey(Source,
                              blank=True,
                              null=True,
                              verbose_name=_('Source'),
                              on_delete=models.SET_NULL)

    # Main Photo
    photo = CachedForeignKey('photos.Photo',
                             blank=True,
                             null=True,
                             on_delete=models.SET_NULL,
                             verbose_name=_('Photo'))

    # Description
    description = models.TextField(_('Description'), blank=True)

    # Publish data
    published = models.BooleanField(_('Published'))
    publish_from = models.DateTimeField(
        _('Publish from'),
        default=core_settings.PUBLISH_FROM_WHEN_EMPTY,
        db_index=True)
    publish_to = models.DateTimeField(_("End of visibility"),
                                      null=True,
                                      blank=True)
    static = models.BooleanField(_('static'), default=False)

    # Last updated
    last_updated = models.DateTimeField(_('Last updated'), blank=True)

    # generic JSON field to store app cpecific data
    app_data = AppDataField(default='{}', editable=False)

    # has the content_published signal been sent for this instance?
    announced = models.BooleanField(help_text='Publish signal sent',
                                    default=False,
                                    editable=False)

    objects = PublishableManager()

    class Meta:
        app_label = 'core'
        verbose_name = _('Publishable object')
        verbose_name_plural = _('Publishable objects')

    def __unicode__(self):
        return self.title

    def __eq__(self, other):
        return isinstance(other, Publishable) and self.pk == other.pk

    def get_absolute_url(self, domain=False):
        " Get object's URL. "
        category = self.category

        kwargs = {
            'slug': self.slug,
        }

        if self.static:
            kwargs['id'] = self.pk
            if category.tree_parent_id:
                kwargs['category'] = category.tree_path
                url = reverse('static_detail', kwargs=kwargs)
            else:
                url = reverse('home_static_detail', kwargs=kwargs)
        else:
            publish_from = localize(self.publish_from)
            kwargs.update({
                'year': publish_from.year,
                'month': publish_from.month,
                'day': publish_from.day,
            })
            if category.tree_parent_id:
                kwargs['category'] = category.tree_path
                url = reverse('object_detail', kwargs=kwargs)
            else:
                url = reverse('home_object_detail', kwargs=kwargs)

        if category.site_id != settings.SITE_ID or domain:
            return 'http://' + category.site.domain + url
        return url

    def get_domain_url(self):
        return self.get_absolute_url(domain=True)

    def clean(self):
        if self.static or not self.published:
            return

        # fields are missing, validating uniqueness is pointless
        if not self.category_id or not self.publish_from or not self.slug:
            return

        qset = self.__class__.objects.filter(
            category=self.category,
            published=True,
            publish_from__day=self.publish_from.day,
            publish_from__month=self.publish_from.month,
            publish_from__year=self.publish_from.year,
            slug=self.slug)

        if self.pk:
            qset = qset.exclude(pk=self.pk)

        if qset:
            raise ValidationError(
                _('Another %s already published at this URL.') %
                self._meta.verbose_name)

    def save(self, **kwargs):
        # update the content_type if it isn't already set
        if not self.content_type_id:
            self.content_type = ContentType.objects.get_for_model(self)
        send_signal = None
        old_self = None
        if self.pk:
            try:
                old_self = self.__class__.objects.get(pk=self.pk)
            except Publishable.DoesNotExist:
                pass

        if old_self:
            old_path = old_self.get_absolute_url()
            new_path = self.get_absolute_url()

            # detect change in URL and not a static one
            if old_path != new_path and new_path and not old_self.static:
                # and create a redirect
                redirect = Redirect.objects.get_or_create(
                    old_path=old_path, site=self.category.site)[0]
                redirect.new_path = new_path
                redirect.save(force_update=True)
                # also update all potentially already existing redirects
                Redirect.objects.filter(new_path=old_path).exclude(
                    pk=redirect.pk).update(new_path=new_path)

            # detect change in publication status
            if old_self.is_published() != self.is_published():
                if self.is_published():
                    send_signal = content_published
                    self.announced = True
                else:
                    send_signal = content_unpublished
                    self.announced = False

            # @note: We also need to check for `published` flag even if both
            # old and new self `is_published()` method returns false.
            # This method can report false since we might be in time *before*
            # publication should take place but we still need to fire signal
            # that content has been unpublished.
            if old_self.published != self.published and self.published is False:
                send_signal = content_unpublished
                self.announced = False

            # changed publish_from and last_updated was default, change it too
            if old_self.last_updated == old_self.publish_from and self.last_updated == old_self.last_updated:
                self.last_updated = self.publish_from

            #TODO: shift Listing in case publish_(to|from) changes
        # published, send the proper signal
        elif self.is_published():
            send_signal = content_published
            self.announced = True

        if not self.last_updated:
            self.last_updated = self.publish_from

        super(Publishable, self).save(**kwargs)

        if send_signal:
            send_signal.send(sender=self.__class__, publishable=self)

    def delete(self):
        url = self.get_absolute_url()
        Redirect.objects.filter(new_path=url).delete()
        if self.announced:
            content_unpublished.send(sender=self.__class__, publishable=self)
        return super(Publishable, self).delete()

    def is_published(self):
        "Return True if the Publishable is currently active."
        cur_time = now()
        return self.published and cur_time > self.publish_from and \
            (self.publish_to is None or cur_time < self.publish_to)
Exemple #8
0
class FlatComment(models.Model):
    site = SiteForeignKey(default=Site.objects.get_current)

    content_type = ContentTypeForeignKey()
    object_id = models.CharField(max_length=255)
    content_object = CachedGenericForeignKey('content_type', 'object_id')

    comment = models.TextField()

    submit_date = models.DateTimeField(default=None)
    user = CachedForeignKey(User)
    is_public = models.BooleanField(default=True)

    app_data = AppDataField()

    def _comment_list(self, reversed=False):
        if not hasattr(self, '__comment_list'):
            self.__comment_list = CommentList(self.content_type, self.object_id, reversed)
        return self.__comment_list

    def post(self, request=None):
        return self._comment_list().post_comment(self, request)

    def moderate(self, user=None, commit=True):
        return self._comment_list().moderate_comment(self, user, commit)

    def get_absolute_url(self, reversed=False):
        return '%s?p=%d' % (
                resolver.reverse(self.content_object, 'comments-list'),
                self._comment_list(reversed).page_index(self.pk)
            )

    def delete(self):
        self.moderate()
        super(FlatComment, self).delete()

    def save(self, **kwargs):
        if self.submit_date is None:
            self.submit_date = timezone.now()
        super(FlatComment, self).save(**kwargs)

    def has_edit_timer(self):
        '''has_edit_timer() -> bool
        '''
        return EDIT_TIMER_ENABLED

    def is_edit_timer_expired(self):
        '''is_edit_timer_expired() -> bool
        Check whether the comment is still within the allowed edit time
        from the creation time.
        '''
        age = datetime.now(self.submit_date.tzinfo) - self.submit_date
        if age >= timedelta(minutes=EDIT_TIMER_MINUTES):
            return True
        return False
        
    def get_remaining_edit_time(self):
        '''get_remaining_edit_time() -> str
        Returns the remaining edit time from comment creation. The returned
        string is formatted as HH:MM:SS, e.g. 0:01:23
        '''
        age = datetime.now(self.submit_date.tzinfo) - self.submit_date
        edit_time = timedelta(minutes=EDIT_TIMER_MINUTES)
        if age >= edit_time:
            return '0:00:00'
        seconds = edit_time.total_seconds() - age.total_seconds()
        remaining = timedelta(seconds=seconds)
        text = str(remaining)
        text = text.split('.')[0]
        return text
Exemple #9
0
class AlternateRegistryModel(models.Model):
    alternate_registry = NamespaceRegistry()
    app_data = AppDataField(app_registry=alternate_registry)
Exemple #10
0
class Author(models.Model):
    publishable = models.ForeignKey(Publishable, on_delete=models.CASCADE)
    app_data = AppDataField()
Exemple #11
0
class Category(models.Model):
    """
    ``Category`` is the **basic building block of Ella-based sites**. All the
    published content is divided into categories - every ``Publishable`` object
    has a ``ForeignKey`` to it's primary ``Category``. Primary category is then
    used to build up object's URL when using `Category.get_absolute_url` method.
    Besides that, objects can be published in other categories (aka "secondary"
    categories) via ``Listing``.

    Every site has exactly one root category (without a parent) that serve's as
    the sites's homepage.
    """
    template_choices = tuple((x, _(y)) for x, y in core_settings.CATEGORY_TEMPLATES)

    title = models.CharField(_("Title"), max_length=200)
    description = models.TextField(_("Description"), blank=True, help_text=_(
        'Description which can be used in link titles, syndication etc.'))
    content = models.TextField(_('Content'), default='', blank=True, help_text=_(
        'Optional content to use when rendering this category.'))
    template = models.CharField(_('Template'), max_length=100, help_text=_(
        'Template to use to render detail page of this category.'),
        choices=template_choices, default=template_choices[0][0])
    slug = models.SlugField(_('Slug'), max_length=255, validators=[category_slug_validator])
    tree_parent = CategoryForeignKey(null=True, blank=True,
        verbose_name=_("Parent category"))
    tree_path = models.CharField(verbose_name=_("Path from root category"),
        max_length=255, editable=False)
    site = SiteForeignKey()

    # generic JSON field to store app cpecific data
    app_data = AppDataField(_('Custom meta data'),
        help_text=_('If you need to define custom data for '
        'category objects, you can use this field to do so.'))

    objects = CategoryManager()

    class Meta:
        app_label = 'core'
        unique_together = (('site', 'tree_path'),)
        verbose_name = _('Category')
        verbose_name_plural = _('Categories')

    def __unicode__(self):
        return '%s/%s' % (self.site.name, self.tree_path)

    def save(self, **kwargs):
        "Override save() to construct tree_path based on the category's parent."
        old_tree_path = self.tree_path
        if self.tree_parent:
            if self.tree_parent.tree_path:
                self.tree_path = '%s/%s' % (self.tree_parent.tree_path, self.slug)
            else:
                self.tree_path = self.slug
        else:
            self.tree_path = ''
        Category.objects.clear_cache()
        super(Category, self).save(**kwargs)
        if old_tree_path != self.tree_path:
            # the tree_path has changed, update children
            children = Category.objects.filter(tree_parent=self)
            for child in children:
                child.save(force_update=True)

    def get_root_category(self):
        if '/' not in self.tree_path:
            return self
        path = self.tree_path.split('/')[0]
        return Category.objects.get_by_tree_path(path)

    def get_children(self, recursive=False):
        return Category.objects.get_children(self, recursive)

    @property
    def path(self):
        """
        Returns tree path of the category. Tree path is string that describes
        the whole path from the category root to the position of this category.

        @see: Category.tree_path
        """
        if self.tree_parent_id:
            return self.tree_path
        else:
            return self.slug

    def get_absolute_url(self):
        """
        Returns absolute URL for the category.
        """
        if not self.tree_parent_id:
            url = reverse('root_homepage')
        else:
            url = reverse('category_detail', kwargs={'category' : self.tree_path})
        if self.site_id != settings.SITE_ID:
            # prepend the domain if it doesn't match current Site
            return 'http://' + self.site.domain + url
        return url

    def draw_title(self):
        """
        Returns title indented by *&nbsp;* elements that can be used to show
        users a category tree.

        Examples:

        **Category with no direct parent (the category root)**
            TITLE

        **Category with one parent**
            &nsbp;TITLE

        **Category on third level of the tree**
            &nbsp;&nbsp;TITLE
        """
        return mark_safe(('&nbsp;&nbsp;' * self.tree_path.count('/')) + self.title)
    draw_title.allow_tags = True
Exemple #12
0
class Post(models.Model):

    title = models.CharField(_('Title'), max_length=255)
    slug = models.CharField(
        _('Slug'),
        max_length=255,
        unique=True,
        blank=True,
        help_text=_('Used in the URL. If changed, the URL will change. '
                    'Clean it to have it re-created.'))
    language = models.CharField(
        _('language'),
        max_length=5,
        null=True,
        blank=True,
        choices=settings.LANGUAGES,
        help_text=_('leave empty to display in all languages'))
    key_visual = FilerImageField(verbose_name=_('Key Visual'),
                                 blank=True,
                                 null=True)
    lead_in = HTMLField(
        _('Lead-in'),
        help_text=
        _('Will be displayed in lists, and at the start of the detail page (in bold)'
          ))
    content = PlaceholderField('aldryn_blog_post_content',
                               related_name='aldryn_blog_posts')
    author = models.ForeignKey(to=AUTH_USER_MODEL, verbose_name=_('Author'))
    coauthors = models.ManyToManyField(to=AUTH_USER_MODEL,
                                       verbose_name=_('Co-Authors'),
                                       null=True,
                                       blank=True,
                                       related_name='aldryn_blog_coauthors')
    publication_start = models.DateTimeField(
        _('Published Since'),
        default=timezone.now,
        help_text=_('Used in the URL. If changed, the URL will change.'))
    publication_end = models.DateTimeField(_('Published Until'),
                                           null=True,
                                           blank=True)
    category = models.ForeignKey(Category,
                                 verbose_name=_('Category'),
                                 null=True,
                                 blank=True)

    objects = RelatedManager()
    published = PublishedManager()
    tags = TaggableManager(blank=True)
    app_data = AppDataField()

    def __unicode__(self):
        return self.title

    def get_absolute_url(self):
        kwargs = {
            'year': self.publication_start.year,
            'month': self.publication_start.month,
            'day': self.publication_start.day,
            'slug': self.slug
        }
        if self.language and not getattr(
                settings, 'ALDRYN_BLOG_SHOW_ALL_LANGUAGES', False):
            with override(self.language):
                return reverse('aldryn_blog:post-detail', kwargs=kwargs)
        return reverse('aldryn_blog:post-detail', kwargs=kwargs)

    class Meta:
        ordering = ['-publication_start']

    def save(self, **kwargs):
        if not self.slug:
            self.slug = slugify(self.title)
        return super(Post, self).save(**kwargs)

    def get_author_slug(self):
        # FIXME: This is a potential performance hogger
        return get_slug_for_user(self.author)
Exemple #13
0
class Group(TranslationHelperMixin, TranslatedAutoSlugifyMixin,
            TranslatableModel):
    slug_source_field_name = 'name'
    translations = TranslatedFields(
        name=models.CharField(_('name'), max_length=255,
                              help_text=_("Provide this group's name.")),
        description=HTMLField(_('description'), blank=True),
        slug=models.SlugField(
            _('slug'), max_length=255, default='',
            blank=True,
            help_text=_("Leave blank to auto-generate a unique slug.")),
    )
    address = models.TextField(
        verbose_name=_('address'), blank=True)
    postal_code = models.CharField(
        verbose_name=_('postal code'), max_length=20, blank=True)
    city = models.CharField(
        verbose_name=_('city'), max_length=255, blank=True)
    phone = models.CharField(
        verbose_name=_('phone'), 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)
    sorting = models.PositiveSmallIntegerField(
        verbose_name=_('sorting field'), default=1,
        help_text=_('first with low value'))
    custom_fields_settings = JSONField(blank=True, null=True)
    custom_fields = JSONField(blank=True, null=True)

    #app config fields and mithods
    type = models.CharField(
        _('Type'),
        max_length=100,
        default='aldryn_people.Group',
    )
    namespace = models.CharField(
        _('Instance namespace'),
        #default=None,
        max_length=100,
        unique=True,
    )
    app_data = AppDataField()

    cmsapp = None

    def save(self, *args, **kwargs):
        self.type = '%s.%s' % (
            self.__class__.__module__, self.__class__.__name__)
        if not self.namespace or self.namespace.startswith('202'):
            self.namespace = 'group-%s' % slugify(self.safe_translation_getter('name'))
        super(Group, self).save(*args, **kwargs)

    #def __str__(self):
        #if self.cmsapp:
            #return '%s / %s' % (self.cmsapp.name, self.namespace)
        #else:
            #return '%s / %s' % (self.type, self.namespace)

    #def __getattr__(self, item):
        #"""
        #This allows to access config form attribute as normal model fields

        #:param item:
        #:return:
        #"""
        #try:
            #return getattr(self.app_data.config, item)
        #except Exception:
            #raise AttributeError('attribute %s not found' % item)

    @property
    def company_name(self):
        warnings.warn(
            '"Group.company_name" has been refactored to "Group.name"',
            DeprecationWarning
        )
        return self.safe_translation_getter('name')

    @property
    def company_description(self):
        warnings.warn(
            '"Group.company_description" has been refactored to '
            '"Group.description"',
            DeprecationWarning
        )
        return self.safe_translation_getter('description')

    class Meta:
        verbose_name = _('Group')
        verbose_name_plural = _('Groups')
        unique_together = ('type', 'namespace')

    def __str__(self):
        return self.safe_translation_getter(
            'name', default=_('Group: {0}').format(self.pk))

    def get_absolute_url(self, language=None):
        if not language:
            language = get_current_language() or get_default_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):
            return reverse('%s:group-detail' % DEFAULT_APP_NAMESPACE, kwargs=kwargs)

    def get_public_url(self, language=None):
        return self.get_absolute_url(language)
Exemple #14
0
class Author(models.Model):
    publishable = models.ForeignKey(Publishable)
    app_data = AppDataField()
Exemple #15
0
class MyModel(models.Model):
    title = models.CharField(max_length=255, blank=True, default='')
    description = models.TextField(blank=True, default='')
    is_awesome = models.BooleanField(default=False)

    app_data = AppDataField()