Example #1
0
class Illustrated(models.Model):

    image = fields.ImageField(
            blank=True,
            max_length=127,
            upload_to=image_upload_to,
            height_field='image_height',
            width_field='image_width',
            verbose_name=_('Image'))
    image_height = fields.IntegerField(
            blank=True,
            editable=False,
            min_value=0,
            null=True,
            verbose_name=_('Image Height'))
    image_width = fields.IntegerField(
            blank=True,
            editable=False,
            min_value=0,
            null=True,
            verbose_name=_('Image Width'))

    class Meta:
        abstract = True

    def __str__(self):
        return os.path.basename(self.image.name)

    def get_upload_path(self, filename):
        foldername = getattr(self._meta, 'folder_name', None)
        if not foldername:
            msg = 'You must set a ``folder_name`` in the model Meta class.'
            raise ImproperlyConfigured(msg)
        else:
            return os.path.join(foldername, filename[0], filename)
Example #2
0
class AbstractPageView(models.Model):

    visit = models.ForeignKey('metrics.Visit',
                              on_delete=models.CASCADE,
                              related_name='page_views',
                              verbose_name=_('Visit'))
    page = models.ForeignKey('metrics.Page',
                             on_delete=models.CASCADE,
                             related_name='page_views',
                             verbose_name=_('Page'))
    previous_page_id = fields.IntegerField(blank=True,
                                           null=True,
                                           verbose_name=_('Previous Page ID'))
    next_page_id = fields.IntegerField(blank=True,
                                       null=True,
                                       verbose_name=_('Next Page ID'))
    status_code = fields.SmallIntegerField(verbose_name=_('Status Code'))
    date = models.DateTimeField(verbose_name=_('Date'))
    load_time = fields.FloatField(verbose_name=_('Load Time'))

    class Meta:
        abstract = True
        ordering = ['-date']
        verbose_name = _('Page View')
        verbose_name_plural = _('Page Views')

    _next_page_cache = Undefined
    _previous_page_cache = Undefined

    def get_next_page(self):
        if self._next_page_cache is Undefined:
            if self.next_page_id is None:
                self._next_page_cache = None
            else:
                self._next_page_cache = Page.objects.get(pk=self.next_page_id)

        return self._next_page_cache

    get_next_page.short_description = _('Next Page')

    def get_previous_page(self):
        if self._previous_page_cache is Undefined:
            if self.previous_page_id is None:
                self._previous_page_cache = None
            else:
                self._previous_page_cache = Page.objects.get(
                    pk=self.previous_page_id)

        return self._previous_page_cache

    get_previous_page.short_description = _('Previous Page')

    next_page = property(get_next_page)
    previous_page = property(get_previous_page)
Example #3
0
class Parameter(Nestable):

    parent = ParentForeignKey('self',
                              null=True,
                              on_delete=models.CASCADE,
                              related_name='children',
                              verbose_name=_('Parent'))
    index = fields.IntegerField(null=True,
                                blank=True,
                                min_value=0,
                                verbose_name=_('Index'))
    name = fields.CharField(unique=True, max_length=63, verbose_name=_('Name'))
    token = fields.CharField(
        max_length=255,
        verbose_name=_('Token'),
        help_text=_('Used to check the User-Agent strings.'))
    regex = fields.BooleanField(
        default=False,
        verbose_name=_('Regular Expression'),
        help_text=_('Check this if your token is a regular expression.'))

    objects = ParameterManager()

    class Meta:
        abstract = True

    def __str__(self):
        return self.name

    @staticmethod
    def autocomplete_search_fields():
        return ('name__icontains', )

    def verify(self, user_agent_string):
        token = self.token.lower()
        ua = user_agent_string.lower()
        if self.regex:
            return (re.search(token, ua) is not None)
        else:
            return (token in ua)
Example #4
0
class AbstractConnection(Logged):

    name = fields.CharField(max_length=63, unique=True, verbose_name=_('Name'))

    host = fields.CharField(max_length=255,
                            verbose_name=_('Host'),
                            help_text=_('Address of the SMTP server.'))
    port = fields.IntegerField(default=25, min_value=0, verbose_name=_('Port'))
    username = fields.CharField(max_length=255, verbose_name=_('Username'))
    password = fields.EncryptedCharField(max_length=255,
                                         verbose_name=_('Password'))

    is_secure = fields.BooleanField(
        default=False,
        verbose_name=_('Use TLS?'),
        help_text=
        _('Whether to use a secure connection when talking to the SMTP server.'
          ))
    is_logged = fields.BooleanField(
        default=False,
        verbose_name=_('Store Mails?'),
        help_text=_('Whether to store a copy of each sent mail.'))

    objects = models.Manager()
    cache = LookupTable()

    class Meta:
        abstract = True
        ordering = ['name']
        verbose_name = _('E-mail Connection')
        verbose_name_plural = _('Connections')

    _connection = None

    def __str__(self):
        return '{0} ({1})'.format(self.name, self.host)

    def save(self, **kwargs):
        """
        Saves the record back to the database. Take into account that this
        closes the network connection if it was open.
        """
        self.close()
        self._connection = None
        super(AbstractConnection, self).save(**kwargs)

    # CUSTOM METHODS

    def close(self):
        """
        Closes the connection to the email server.
        """
        if self._connection is not None:
            self._connection.close()

    def open(self):
        """
        Ensures we have a connection to the email server. Returns whether or
        not a new connection was required (True or False).
        """
        return self.smtp_connection.open()

    def send_messages(self, email_messages):
        """
        Sends one or more EmailMessage objects and returns the number of email
        messages sent.
        """
        return self.smtp_connection.send_messages(email_messages)

    # PROPERTIES

    @property
    def smtp_connection(self):
        """
        Returns an instance of the SMTP email backend. This instance uses the
        authentication credentials set in the record to connect the SMTP server.
        """
        if self._connection is None:
            if self.is_logged:
                backend = 'yepes.contrib.emails.backends.LoggedSmtpBackend'
            else:
                backend = 'yepes.contrib.emails.backends.SmtpBackend'

            self._connection = mail.get_connection(
                backend, **{
                    'host': self.host,
                    'port': self.port,
                    'username': self.username,
                    'password': self.password,
                    'use_tls': self.is_secure,
                })
        return self._connection

    # GRAPPELLI SETTINGS

    @staticmethod
    def autocomplete_search_fields():
        return ('host__icontains', )
Example #5
0
class AbstractAttachment(Logged, Calculated):

    guid = fields.GuidField(editable=False,
                            verbose_name=_('Global Unique Identifier'))

    title = fields.CharField(max_length=63, verbose_name=_('Title'))
    caption = fields.CharField(max_length=255,
                               blank=True,
                               verbose_name=_('Caption'))
    alt = fields.CharField(max_length=127,
                           blank=True,
                           verbose_name=_('Alternate Text'))
    description = fields.TextField(blank=True, verbose_name=_('Description'))

    file = models.FileField(blank=True,
                            max_length=127,
                            upload_to=file_upload_to,
                            verbose_name=_('File'))
    external_file = models.URLField(blank=True,
                                    max_length=127,
                                    verbose_name=_('External File'))

    size = fields.IntegerField(blank=True,
                               calculated=True,
                               min_value=0,
                               null=True,
                               verbose_name=_('Size'))
    mime_type = fields.CharField(blank=True,
                                 calculated=True,
                                 max_length=31,
                                 null=True,
                                 verbose_name=_('MIME Type'))
    height = fields.IntegerField(blank=True,
                                 calculated=True,
                                 min_value=0,
                                 null=True,
                                 verbose_name=_('Height'))
    width = fields.IntegerField(blank=True,
                                calculated=True,
                                min_value=0,
                                null=True,
                                verbose_name=_('Width'))

    category = models.ForeignKey('AttachmentCategory',
                                 blank=True,
                                 null=True,
                                 on_delete=models.PROTECT,
                                 related_name='attachments',
                                 verbose_name=_('Category'))

    class Meta:
        abstract = True
        folder_name = 'attachments'
        ordering = ['title']
        verbose_name = _('Attachment')
        verbose_name_plural = _('Attachments')

    def __str__(self):
        return self.title

    def clean(self):
        super(AbstractAttachment, self).clean()
        if not self.file and not self.external_file:
            msg = _(
                'You must upload a file or set the URL of an external file.')
            raise ValidationError({'file': msg})

    def delete(self, *args, **kwargs):
        self.file.delete(save=False)
        return super(AbstractAttachment, self).delete(*args, **kwargs)

    def get_upload_path(self, filename):
        if self.title:
            _, extension = os.path.splitext(filename)
            return slugify(self.title, ascii=True).replace('-',
                                                           '_') + extension
        else:
            return filename

    # CUSTOM METHODS

    def calculate_height(self):
        if not self.file or not self.is_image:
            return None
        else:
            return self.image.height

    def calculate_mime_type(self):
        if self.file and magic is not None:
            file_type = magic.from_buffer(self.file.read(1024), mime=True)
            if file_type is not None:
                return force_text(file_type)

        file_name = self.get_file_name()
        file_type, _ = mimetypes.guess_type(file_name)
        if file_type is not None:
            return force_text(file_type)

        if not self.file and magic is not None:
            file_type = magic.from_buffer(urlopen(file_name).read(1024),
                                          mime=True)
            if file_type is not None:
                return force_text(file_type)

        return None

    def calculate_size(self):
        if not self.file:
            return None
        else:
            return self.file.size

    def calculate_width(self):
        if not self.file or not self.is_image:
            return None
        else:
            return self.image.width

    def get_audio_tag(self, **attrs):
        wrap = attrs.pop('wrap', False)

        attrs['src'] = self.get_file_url()
        attrs.setdefault('controls', True)
        attrs.setdefault('preload', 'none')
        content = self.get_file_link(text=(self.alt or self.title))
        tag = make_double_tag('audio', content, attrs)
        if wrap:
            tag = make_double_tag('div', tag, {'class': 'audio-wrap'})

        return tag

    def get_display_size(self):
        if self.size is None:
            return ''

        bytes = self.size
        if bytes < 1024:
            return '{0} B'.format(number_format(bytes))

        kb = (bytes / 1024)
        if kb < 1024:
            return '{0} KB'.format(number_format(kb, 1))

        mb = (kb / 1024)
        if mb < 1024:
            return '{0} MB'.format(number_format(mb, 1))

        gb = (mb / 1024)
        return '{0} GB'.format(number_format(gb, 1))

    get_display_size.admin_order_field = 'size'
    get_display_size.short_description = _('Size')

    def get_file_link(self, **attrs):
        attrs['href'] = self.get_file_url()
        attrs.setdefault('download', True)
        content = attrs.pop('text', self.title)
        return make_double_tag('a', content, attrs)

    def get_file_url(self):
        if not self.file:
            return self.external_file
        else:
            return self.file.url

    get_file_url.short_description = _('File')

    def get_file_name(self):
        if not self.file:
            return self.external_file
        else:
            return self.file.name

    get_file_name.short_description = _('File')

    def get_tag(self, **attrs):
        if self.is_audio:
            return self.get_audio_tag(**attrs)
        if self.is_image:
            return self.get_image_tag(**attrs)
        if self.is_video:
            return self.get_video_tag(**attrs)

        if self.is_external:
            url = self.get_file_url()
            if 'youtube' in url or 'vimeo' in url:
                return self.get_iframe_tag()

        return self.get_file_link(**attrs)

    def get_iframe_tag(self, **attrs):
        wrap = attrs.pop('wrap', False)

        attrs['src'] = self.get_file_url()
        attrs.setdefault('width', self.width or 640)
        attrs.setdefault('height', self.height or 360)
        attrs.setdefault('frameborder', 0)
        attrs.setdefault('webkitallowfullscreen', True)
        attrs.setdefault('mozallowfullscreen', True)
        attrs.setdefault('allowfullscreen', True)
        content = self.get_file_link(text=(self.alt or self.title))
        tag = make_double_tag('iframe', content, attrs)
        if wrap:
            tag = make_double_tag('div', tag, {'class': 'iframe-wrap'})

        return tag

    def get_image_tag(self, **attrs):
        wrap = attrs.pop('wrap', False)

        attrs['src'] = self.get_file_url()
        if self.width:
            attrs.setdefault('width', self.width)
        if self.height:
            attrs.setdefault('height', self.height)

        attrs.setdefault('alt', self.alt or self.title)
        tag = make_single_tag('img', attrs)
        if wrap:
            tag = make_double_tag('div', tag, {'class': 'image-wrap'})

        return tag

    def get_video_tag(self, **attrs):
        wrap = attrs.pop('wrap', False)

        attrs['src'] = self.get_file_url()
        attrs.setdefault('width', self.width or 640)
        attrs.setdefault('height', self.height or 360)
        attrs.setdefault('controls', True)
        attrs.setdefault('preload', 'metadata')
        content = self.get_file_link(text=(self.alt or self.title))
        tag = make_double_tag('video', content, attrs)
        if wrap:
            tag = make_double_tag('div', tag, {'class': 'video-wrap'})

        return tag

    # PROPERTIES

    @cached_property
    def image(self):
        if not self.file:
            return None
        else:
            return SourceFile(self.file, self.file.name, self.file.storage)

    @cached_property
    def is_audio(self):
        file_url = self.get_file_url()
        if file_url and file_url.endswith(settings.AUDIO_EXTENSIONS):
            return True

        if self.mime_type and self.mime_type.startswith('audio'):
            return True

        return False

    @cached_property
    def is_external(self):
        return not self.file

    @cached_property
    def is_image(self):
        file_url = self.get_file_url()
        if file_url and file_url.endswith(settings.IMAGE_EXTENSIONS):
            return True

        if self.mime_type and self.mime_type.startswith('image'):
            return True

        return False

    @cached_property
    def is_video(self):
        file_url = self.get_file_url()
        if file_url and file_url.endswith(settings.VIDEO_EXTENSIONS):
            return True

        if self.mime_type and self.mime_type.startswith('video'):
            return True

        return False
Example #6
0
class AbstractConfiguration(Logged):

    ALGORITHM_CHOICES = (
        ('undefined', 'undefined'),
        ('sample', 'sample'),
        ('liquid', 'liquid'),
    ) + tuple(
        (algorithm, algorithm)
        for algorithm in AVAILABLE_ALGORITHMS if algorithm != 'undefined')
    FORMAT_CHOICES = (
        ('GIF', 'GIF'),
        ('JPEG', 'JPEG'),
        ('PNG8', 'PNG8'),
        ('PNG64', 'PNG64'),
        ('WEBP', 'WEBP'),
    )
    GRAVITY_CHOICES = (
        ('north_west', _('Northwest')),
        ('north', _('North')),
        ('north_east', _('Northeast')),
        ('west', _('West')),
        ('center', _('Center')),
        ('east', _('East')),
        ('south_west', _('Southwest')),
        ('south', _('South')),
        ('south_east', _('Southeast')),
    )
    MODE_CHOICES = (
        ('scale', _('Scale')),
        ('fit', _('Fit')),
        ('limit', _('Fit without enlarging')),
        ('fill', _('Fill')),
        ('lfill', _('Fill without enlarging')),
        ('pad', _('Pad')),
        ('lpad', _('Pad without enlarging')),
        ('crop', _('Crop')),
    )
    key = fields.IdentifierField(max_length=63,
                                 unique=True,
                                 verbose_name=_('Key'))

    width = fields.IntegerField(min_value=0, verbose_name=_('Width'))
    height = fields.IntegerField(min_value=0, verbose_name=_('Height'))
    background = fields.ColorField(blank=True,
                                   null=True,
                                   verbose_name=_('Background'))

    mode = fields.CharField(choices=MODE_CHOICES,
                            default='limit',
                            max_length=15,
                            verbose_name=_('Crop Mode'))
    algorithm = fields.CharField(choices=ALGORITHM_CHOICES,
                                 default='undefined',
                                 max_length=15,
                                 verbose_name=_('Resizing Algorithm'))
    gravity = fields.CharField(choices=GRAVITY_CHOICES,
                               default='center',
                               max_length=15,
                               verbose_name=_('Gravity'))

    format = fields.CharField(choices=FORMAT_CHOICES,
                              default='JPEG',
                              max_length=15,
                              verbose_name=_('Format'))
    quality = fields.IntegerField(default=85,
                                  max_value=100,
                                  min_value=1,
                                  verbose_name=_('Quality'))

    objects = models.Manager()
    cache = LookupTable(['key'])

    class Meta:
        abstract = True
        ordering = ['key']
        verbose_name = _('Thumbnail Configuration')
        verbose_name_plural = _('Configurations')

    def __str__(self):
        return self.key

    @staticmethod
    def autocomplete_search_fields():
        return ('key__icontains', )

    def clean(self):
        if not self.width and not self.height:
            raise ValidationError()
Example #7
0
class Orderable(six.with_metaclass(OrderableBase, models.Model)):
    """
    Abstract model that provides a custom ordering integer field similar to
    using Meta's ``order_with_respect_to``, since to date (Django 1.2) this
    doesn't work with ``ForeignKey('self')``.

    We may also want this feature for models that aren't ordered with respect
    to a particular field.

    """
    # Do not set ``db_index`` here because ``order_with_respect_to``.
    index = fields.IntegerField(blank=True,
                                min_value=0,
                                verbose_name=_('Index'))

    class Meta:
        abstract = True

    _next = Undefined
    _previous = Undefined

    def get_queryset(self):
        """
        Returns a dict to use as a filter for ordering operations containing
        the original `Meta.order_with_respect_to` value if provided.
        """
        qs = self.__class__._default_manager.get_queryset()
        if self._meta.filter_field:
            name = self._meta.filter_field
            value = getattr(self, name)
            return qs.filter(**{name: value})
        else:
            return qs

    def delete(self, *args, **kwargs):
        """
        Update the ordering values for siblings.
        """
        if self.index is not None:
            qs = self.get_queryset().filter(index__gt=self.index)
            qs.update(index=F('index') - 1)

        super(Orderable, self).delete(*args, **kwargs)

    def get_next_in_order(self):
        if self._next is Undefined:
            qs = self.get_queryset()
            qs = qs.filter(index__gt=self.index)
            qs = qs.order_by('index')
            self._next = qs.first()

        return self._next

    def get_previous_in_order(self):
        if self._previous is Undefined:
            qs = self.get_queryset()
            qs = qs.filter(index__lt=self.index)
            qs = qs.order_by('index')
            self._previous = qs.last()

        return self._previous

    def save(self, **kwargs):
        """
        Set the initial ordering value.
        """
        updated_fields = kwargs.get('update_fields', ())
        new_record = (kwargs.get('force_insert', False)
                      or not (self._get_pk_val() or updated_fields))

        self._next = Undefined
        self._previous = Undefined
        if new_record and not self.index:
            qs = self.get_queryset()
            self.index = qs.filter(index__isnull=False).count()

        super(Orderable, self).save(**kwargs)
Example #8
0
class BaseComment(Nestable, Logged, Linked):

    parent = ParentForeignKey('self',
                              blank=True,
                              null=True,
                              related_name='children',
                              verbose_name=_('Parent'))

    author_name = fields.CharField(blank=False,
                                   max_length=63,
                                   verbose_name=_('Name'))
    author_email = fields.EmailField(blank=False,
                                     max_length=63,
                                     verbose_name=_('Email Address'))
    author_url = models.URLField(blank=True,
                                 max_length=127,
                                 verbose_name=_('URL'))

    ip_address = models.GenericIPAddressField(blank=True,
                                              null=True,
                                              protocol='both',
                                              unpack_ipv4=True,
                                              verbose_name=_('IP Address'))
    user_agent = fields.TextField(blank=True, verbose_name=_('User Agent'))

    karma = fields.IntegerField(default=0, blank=True, verbose_name=_('Karma'))
    status = fields.CachedForeignKey('CommentStatus',
                                     on_delete=models.PROTECT,
                                     related_name='comments',
                                     verbose_name=_('Status'))
    is_published = fields.BooleanField(editable=False,
                                       default=True,
                                       verbose_name=_('Is Published?'))

    objects = CommentManager()

    class Meta:
        abstract = True
        index_together = [('status', 'creation_date')]
        ordering = ['-creation_date']
        permissions = [('can_moderate', _('Can moderate comments'))]
        verbose_name = _('Comment')
        verbose_name_plural = _('Comments')

    _author_email = Undefined
    _author_name = Undefined

    def __init__(self, *args, **kwargs):
        super(BaseComment, self).__init__(*args, **kwargs)
        self.old_status_id = self.status_id

    def __str__(self):
        args = (
            self.pk,
            self.get_author_name(),
        )
        return '#{0} {1}'.format(*args)

    def save(self, **kwargs):
        updated_fields = kwargs.get('update_fields', ())
        new_record = (kwargs.get('force_insert', False)
                      or not (self.pk or updated_fields))

        if (not updated_fields
                and (new_record or self.status_id != self.old_status_id)):
            status = self.get_status()
            self.is_published = status.publish_comment
            self.old_status_id = status.id

        self._author_email = Undefined
        self._author_name = Undefined
        super(BaseComment, self).save(**kwargs)

    def get_absolute_url(self):
        if not hasattr(self, 'post'):
            msg = ('{cls} is missing a post. Define {cls}.post, or override '
                   '{cls}.get_absolute_url().')
            raise ImproperlyConfigured(msg.format(cls=self.__class__.__name__))
        else:
            return '#'.join((self.post.get_absolute_url(), self.get_anchor()))

    # CUSTOM METHODS

    def get_anchor(self):
        return settings.COMMENT_ANCHOR_PATTERN.format(**self.get_anchor_data())

    def get_anchor_data(self):
        data = {
            'id': self.id,
            'parent_id': self.parent_id,
        }
        if hasattr(self, 'post_id'):
            data['post_id'] = self.post_id
        elif hasattr(self, 'post'):
            data['post_id'] = self.post.pk

        return data

    def get_author_email(self):
        if self._author_email is Undefined:
            if not hasattr(self, 'author') or self.author is None:
                self._author_email = self.author_email
            else:
                self._author_email = self.author.email

        return self._author_email

    get_author_email.short_description = _('Email Address')

    def get_author_link(self):
        url = self.get_author_url()
        name = conditional_escape(self.get_author_name())
        if url:
            return mark_safe('<a href="{0}" rel="nofollow">{1}</a>'.format(
                url, name))
        else:
            return mark_safe(name)

    get_author_link.short_description = _('Author')

    def get_author_name(self):
        if self._author_name is Undefined:
            if not hasattr(self, 'author') or self.author is None:
                self._author_name = self.author_name
            else:
                self._author_name = (self.author.get_full_name()
                                     or self.author.get_username())
        return self._author_name

    get_author_name.short_description = _('Name')

    def get_author_url(self):
        return self.author_url

    get_author_url.short_description = _('URL')

    def get_children(self, limit=None, status=None, order_by=None):
        qs = self.children.all()
        qs = qs.prefetch_related('author')
        if status is None:
            qs = qs.published()
        elif isinstance(status, six.string_types):
            qs = qs.filter(status__api_id=status)
        else:
            qs = qs.filter(status=status)

        if order_by is None:
            qs = qs.order_by('-creation_date')
        else:
            qs = qs.order_by(order_by)

        if limit:
            qs = qs[:limit]

        return qs

    def get_content(self):
        status = self.get_status()
        if status.comment_replacement:
            return mark_safe(status.comment_replacement)
        elif hasattr(self, 'content_html'):
            return mark_safe(self.content_html)
        elif hasattr(self, 'content'):
            return mark_safe(linebreaks(self.content, autoescape=True))
        else:
            msg = ('{cls} is missing a content. Define {cls}.content_html, '
                   '{cls}.content, or override {cls}.get_content().')
            raise ImproperlyConfigured(msg.format(cls=self.__class__.__name__))

    get_content.short_description = _('Content')

    def get_date(self):
        return self.creation_date

    get_date.short_description = _('Date')

    def get_excerpt(self, max_words=20, end_text='...'):
        content = self.get_content()
        if hasattr(content, '__html__'):
            # The __html__ attribute means the content was previously
            # marked as safe, so can include HTML tags.
            truncator = Truncator(content.__html__())
            if end_text == '...':
                end_text = '&hellip;'

            return mark_safe(truncator.words(max_words, end_text, html=True))
        else:
            return Truncator(content).words(max_words, end_text, html=False)

    get_excerpt.short_description = _('Excerpt')

    def get_link(self):
        url = self.get_absolute_url()
        text = ugettext('{author} on {post}').format(
            **{
                'author': self.get_author_name(),
                'post': self.get_post(),
            })
        return mark_safe('<a href="{0}">{1}</a>'.format(url, escape(text)))

    def get_status(self):
        return self.status

    get_excerpt.short_description = _('Status')