示例#1
0
class TabCategory(Model):
    name = CharField(max_length=22)
    icon = ResizedImageField(_('Icon'), 32, 32, upload_to='shields/icons')

    class Meta:
        db_table = 'extra_tabcategory'

    def __unicode__(self):
        return self.name
示例#2
0
class ReleaseStatus(Model):
    """For non-released (finalised) Releases, what stage are we at"""
    STYLES = (('blue', _('Blue')), )
    name = CharField(max_length=32)
    desc = CharField(_('Description'), max_length=128)
    style = CharField(max_length=32, choices=STYLES, **null)
    icon = ResizedImageField(**upload_to('icons', 32, 32))

    def __str__(self):
        return self.name
示例#3
0
class Report(Model):
    """A project should always have at least one update with its primary description"""

    project = ForeignKey(Project, related_name='updates')
    description = TextField(_("Description"),
                            validators=[MaxLengthValidator(12288)])
    image = ResizedImageField(_("Image"),
                              max_height=400,
                              max_width=400,
                              upload_to=os.path.join('project', 'update',
                                                     '%Y'),
                              **null)

    creator = ForeignKey(settings.AUTH_USER_MODEL, default=get_user)
    created = DateTimeField(auto_now_add=True, db_index=True)
    edited = DateTimeField(auto_now=True)

    def __str__(self):
        return self.description
示例#4
0
class Project(Model):
    """A project details work that needs to be done"""

    DIFFICULTIES = (
        (0, _('Unknown')),
        (1, _('Easy')),
        (2, _('Moderate')),
        (3, _('Hard')),
        (4, _('Very hard')),
    )

    LOGO = os.path.join(settings.STATIC_URL, 'images', 'project_logo.png')
    BANNER = os.path.join(settings.STATIC_URL, 'images', 'project_banner.png')

    difficulty = IntegerField(_('Difficulty'), choices=DIFFICULTIES, default=2)
    title = CharField(_('Title'), max_length=100)
    pitch = CharField(_('Short Summary'), max_length=255, **null)
    slug = SlugField(unique=True)
    description = TextField(_('Description'),
                            validators=[MaxLengthValidator(50192)],
                            **null)

    banner = ResizedImageField(_("Banner (920x120)"),
                               max_height=120,
                               max_width=920,
                               min_height=90,
                               min_width=600,
                               upload_to=os.path.join('project', 'banner'),
                               default=BANNER)
    logo = ResizedImageField(_("Logo (150x150)"),
                             max_height=150,
                             max_width=150,
                             min_height=150,
                             min_width=150,
                             upload_to=os.path.join('project', 'logo'),
                             default=LOGO)

    duration = IntegerField(_('Expected Duration in Days'), default=0)
    started = DateTimeField(**null)
    finished = DateTimeField(**null)

    created = DateTimeField(auto_now_add=True, db_index=True)
    edited = DateTimeField(auto_now=True)

    proposer = ForeignKey(settings.AUTH_USER_MODEL,
                          related_name='proposed_projects',
                          default=get_user)
    manager = ForeignKey(settings.AUTH_USER_MODEL,
                         related_name='manages_projects',
                         **null)
    reviewer = ForeignKey(settings.AUTH_USER_MODEL,
                          related_name='reviews_projects',
                          **null)
    second = ForeignKey(settings.AUTH_USER_MODEL,
                        related_name='seconds_projects',
                        **null)

    project_type = ForeignKey(ProjectType)

    is_fundable = BooleanField(default=False)
    is_approved = BooleanField(_('Pre-approved'), default=False)

    criteria = ManyToManyField('Criteria', blank=True)

    def __str__(self):
        return self.title

    def save(self, **kwargs):
        if not self.slug and self.title:
            self.slug = slugify(self.title)
        Model.save(self, **kwargs)

    def progress(self):
        """Returns a float, percentage of completed deliverable items"""
        count = self.deliverables.all().count()
        if count:
            done = self.deliverables.filter(finished__isnull=False).count()
            if done > 0:
                return (done / float(count)) * 100.0
        return self.finished and 100.0 or 0.0

    def get_absolute_url(self):
        return reverse('project', kwargs={'slug': self.slug})

    def get_status(self):
        """Returns a (preliminary) status number / string tuple for displaying in templates
      possible status include: proposed (needs review), application phase (free to take),
      in progress, finished. The number could be used for CSS classing."""

        if self.manager is None:
            return (1, _("Proposed"))
        elif self.started is None:
            return (2, _("Application Phase"))
        elif self.started is not None:
            return (3, _("In Progress"))
        elif self.finished is not None:
            return (4, _("Completed"))
        else:
            return (0, _("Undetermined"))

    def get_expected_enddate(self):
        if self.started is not None:
            return self.started + datetime.timedelta(days=self.duration)
        else:
            return datetime.datetime.now() + datetime.timedelta(
                days=self.duration)
示例#5
0
class Release(Model):
    """A release of inkscape"""
    parent = ForeignKey('self', related_name='children', **null)
    version = CharField(_('Version'),
                        max_length=16,
                        db_index=True,
                        unique=True)
    codename = CharField(_('Codename'), max_length=32, db_index=True, **null)

    release_notes = TextField(_('Release notes'), **null)
    release_date = DateField(_('Release date'), db_index=True, **null)
    status = ForeignKey(ReleaseStatus, **null)

    edited = DateTimeField(_('Last edited'), auto_now=True)
    created = DateTimeField(_('Date created'),
                            auto_now_add=True,
                            db_index=True)
    background = ResizedImageField(**upload_to('background', 960, 360))

    manager = ForeignKey(
        User,
        verbose_name=_("Manager"),
        related_name='releases',
        help_text=_("Looks after the release schedule and release meetings."),
        **null)
    reviewer = ForeignKey(
        User,
        verbose_name=_("Reviewer"),
        related_name='rev_releases',
        help_text=_("Reviewers help to make sure the release is working."),
        **null)
    bug_manager = ForeignKey(
        User,
        verbose_name=_("Bug Manager"),
        related_name='bug_releases',
        help_text=_("Manages critical bugs and decides what needs fixing."),
        **null)
    translation_manager = ForeignKey(
        User,
        verbose_name=_("Translation Manager"),
        related_name='tr_releases',
        help_text=_(
            "Translation managers look after all translations for the release."
        ),
        **null)

    objects = ReleaseQuerySet.as_manager()

    class Meta:
        ordering = '-release_date',
        get_latest_by = 'release_date'

    def __str__(self):
        if not self.codename:
            return "Inkscape %s" % self.version
        return "Inkscape %s (%s)" % (self.version, self.codename)

    def get_absolute_url(self):
        return reverse('releases:release', kwargs={'version': self.version})

    def breadcrumb_parent(self):
        return self.parent if self.parent else Release.objects.all()

    def is_prerelease(self):
        """Returns True if this child release happened before parent release"""
        (par, dat) = (self.parent, self.release_date)
        return par and dat and (not par.release_date or par.release_date > dat)

    def get_notes(self):
        """Returns a translated release notes"""
        lang = get_language()
        if not lang or lang == DEFAULT_LANG:
            return self.release_notes
        try:
            return self.translations.get(language=lang).translated_notes
        except ReleaseTranslation.DoesNotExist:
            return self.release_notes

    @property
    def revisions(self):
        return Release.objects.filter(Q(parent_id=self.pk) | Q(id=self.pk))

    @property
    def latest(self):
        return self.revisions.order_by('-release_date')[0]

    def responsible_people(self):
        """Quick list of all responsible people with labels"""
        for key in ('manager', 'reviewer', 'translation_manager',
                    'bug_manager'):
            yield (getattr(Release, key).field.verbose_name,
                   getattr(Release, key).field.help_text, getattr(self, key))
示例#6
0
class Platform(Model):
    """A list of all platforms we release to"""
    name = CharField(_('Name'), max_length=64)
    desc = CharField(_('Description'), max_length=255)
    parent = ForeignKey('self',
                        related_name='children',
                        verbose_name=_("Parent Platform"),
                        **null)
    manager = ForeignKey(User, verbose_name=_("Platform Manager"), **null)
    codename = CharField(max_length=255, **null)
    order = PositiveIntegerField(default=0)

    match_family = CharField(max_length=32,
                             db_index=True,
                             help_text=_('User agent os match, whole string.'),
                             **null)
    match_version = CharField(
        max_length=32,
        db_index=True,
        help_text=
        _('User agent os version partial match, e.g. |10|11| will match both version 10 and version 11, must have pipes at start and end of string.'
          ),
        **null)
    match_bits = PositiveIntegerField(db_index=True,
                                      choices=((32, '32bit'), (64, '64bit')),
                                      **null)

    icon = ResizedImageField(**upload_to('icons', 32, 32))
    image = ResizedImageField(**upload_to('icons', 256, 256))

    uuid = lambda self: slugify(self.name)
    tab_name = lambda self: self.name
    tab_text = lambda self: self.desc
    tab_cat = lambda self: {'icon': self.icon}
    root = lambda self: self.ancestors()[-1]
    depth = lambda self: len(self.ancestors) - 1

    class Meta:
        ordering = '-order', 'codename'

    def save(self, **kwargs):
        codename = "/".join([slugify(anc.name)
                             for anc in self.ancestors()][::-1])
        if self.codename != codename:
            self.codename = codename
            if self.pk:
                for child in self.children.all():
                    child.save()
        return super(Platform, self).save(**kwargs)

    def get_manager(self):
        if self.manager:
            return self.manager
        if self.parent:
            return self.parent.get_manager()
        return None

    def ancestors(self, _to=None):
        _to = _to or [self]
        if self.parent and self.parent not in _to:
            # Prevent infinite loops getting parents
            _to.append(self.parent)
            self.parent.ancestors(_to)
        return _to

    def descendants(self, _from=None):
        _from = _from or []
        for child in self.children.all():
            if child in _from:
                # Prevent infinite loops getting children
                continue
            _from.append(child)
            child.descendants(_from)
        return _from

    def __str__(self):
        return self.codename.replace('/', ' : ').replace('_', ' ').title()
示例#7
0
class Gallery(Model):
    GALLERY_STATUSES = (
        (None, 'No Status'),
        (' ', 'Casual Wish'),
        ('1', 'Draft'),
        ('2', 'Proposal'),
        ('3', 'Reviewed Proposal'),
        ('+', 'Under Development'),
        ('=', 'Complete'),
        ('-', 'Rejected'),
    )
    user = ForeignKey(settings.AUTH_USER_MODEL,
                      related_name='galleries',
                      default=get_user)
    group = ForeignKey(Group, related_name='galleries', **null)
    category = ForeignKey(Category, related_name='galleries', **null)

    name = CharField(max_length=64)
    slug = SlugField(max_length=70)
    desc = TextField(_('Description'),
                     validators=[MaxLengthValidator(50192)],
                     **null)
    thumbnail = ResizedImageField(_('Thumbnail'), 190, 190, **upto('thumb'))
    status = CharField(max_length=1,
                       db_index=True,
                       choices=GALLERY_STATUSES,
                       **null)

    items = ManyToManyField(Resource, related_name='galleries', blank=True)

    contest_submit = DateField(
        help_text=_('Start a contest in this gallery on this date (UTC).'),
        **null)
    contest_voting = DateField(
        help_text=_('Finish the submissions and start voting (UTC).'), **null)
    contest_count = DateField(
        help_text=_('Voting is finished, but the votes are being counted.'),
        **null)
    contest_finish = DateField(help_text=_(
        'Finish the contest, voting closed, winner announced (UTC).'),
                               **null)

    _is_generic = lambda self, a, b: a and a <= now().date() < b
    is_contest = property(lambda self: bool(self.contest_submit))
    is_pending = property(lambda self: self.contest_submit and self.
                          contest_submit > now().date())
    is_submitting = property(lambda self: self._is_generic(
        self.contest_submit, self.contest_voting))
    is_voting = property(lambda self: self._is_generic(self.contest_voting, (
        self.contest_count or self.contest_finish)))
    is_counting = property(
        lambda self: self._is_generic(self.contest_count, self.contest_finish))
    is_finished = property(lambda self: self.contest_finish and self.
                           contest_finish <= now().date())

    objects = GalleryQuerySet.as_manager()

    def __unicode__(self):
        if self.category:
            return self.name
        elif self.group:
            return _(u"%(gallery_name)s (for group %(group_name)s)") \
                  % {'gallery_name': self.name, 'group_name': unicode(self.group)}
        return  _(u"%(gallery_name)s (by %(user_name)s)") \
                  % {'gallery_name': self.name, 'user_name': unicode(self.user)}

    def __str__(self):
        return unicode(self).encode('utf8')

    def tag_cloud(self):
        """Returns a cloud collection"""
        return Tag.objects.filter(
            resources__galleries__id=self.pk).as_cloud('resources')

    @property
    def votes(self):
        """Returns a queryset of Votes for this category"""
        return Vote.objects.filter(resource__galleries=self.pk)

    def save(self, *args, **kwargs):
        set_slug(self)
        super(Gallery, self).save(*args, **kwargs)

    def get_absolute_url(self):
        if self.category:
            return reverse('resources',
                           kwargs={
                               'category': self.category.slug,
                               'galleries': self.slug,
                           })
        if self.group:
            try:
                return reverse('resources',
                               kwargs={
                                   'team': self.group.team.slug,
                                   'galleries': self.slug,
                               })
            except Team.DoesNotExist:
                pass
        elif self.slug:
            return reverse('resources',
                           kwargs={
                               'username': self.user.username,
                               'galleries': self.slug,
                           })
        return reverse('resources', kwargs={'gallery_id': self.pk})

    @property
    def winners(self):
        """Return the resource with the most votes"""
        if self.is_contest and self.is_finished:
            if self.contest_count is None:
                item = self.items.latest('liked')
                item.extra_status = Resource.CONTEST_WINNER
                item.save()
                self.contest_count = self.contest_finish
            return self.items.filter(extra_status=Resource.CONTEST_WINNER)
        return None

    @property
    def value(self):
        return self.slug

    @property
    def parent(self):
        if self.category:
            return self.category
        return (self.group or self.user).galleries.all()

    def is_visible(self):
        return self.items.for_user(get_user()).count() or self.is_editable()

    def is_editable(self):
        user = get_user()
        return user and (not user.id is None) and (
            self.user == user or user.is_superuser \
              or (user.groups.count() and self.group in user.groups.all()))

    def thumbnail_url(self):
        if self.thumbnail:
            return self.thumbnail.url
        for item in self.items.all():
            if item.is_visible():
                return item.icon_url()
        return static('images', 'folder.svg')

    def __len__(self):
        return self.items.count()
示例#8
0
class Resource(Model):
    """This is a resource with an uploaded file"""
    owner_field = 'user'
    is_resource = True

    ENDORSE_NONE = 0
    ENDORSE_HASH = 1
    ENDORSE_SIGN = 5
    ENDORSE_AUTH = 10

    CONTEST_WINNER = 1
    CONTEST_RUNNER_UP = 2
    CONTEST_CONSIDER = 3
    EXTRA_CHOICES = (
        (None, _('No extra status')),
        (CONTEST_WINNER, _('Winner')),
        (CONTEST_RUNNER_UP, _('Runner Up')),
        (CONTEST_CONSIDER, _('Next Round')),
    )
    EXTRA_CSS = ['', 'ribbon winner', 'ribbon runnerup', 'ribbon consider']

    user = ForeignKey(settings.AUTH_USER_MODEL,
                      related_name='resources',
                      default=get_user)
    name = CharField(max_length=64)
    slug = SlugField(max_length=70)
    desc = TextField(_('Description'),
                     validators=[MaxLengthValidator(50192)],
                     **null)
    category = ForeignKey(Category,
                          verbose_name=_("Category"),
                          related_name='items',
                          **null)
    tags = ManyToManyField(Tag,
                           verbose_name=_("Tags"),
                           related_name='resources',
                           blank=True)

    created = DateTimeField(**null)
    edited = DateTimeField(**null)  # End of copyright, last file-edit/updated.
    published = BooleanField(default=False)

    thumbnail = ResizedImageField(_('Thumbnail'), 190, 190, **upto('thumb'))
    rendering = ResizedImageField(_('Rendering'), 780, 600, **upto('render'))

    link = URLField(_('External Link'), **null)
    liked = PositiveIntegerField(default=0)
    viewed = PositiveIntegerField(default=0)
    downed = PositiveIntegerField(_('Downloaded'), default=0)
    fullview = PositiveIntegerField(_('Full Views'), default=0)

    media_type = CharField(_('File Type'), max_length=128, **null)
    media_x = IntegerField(**null)
    media_y = IntegerField(**null)

    extra_status = PositiveSmallIntegerField(choices=EXTRA_CHOICES, **null)
    extra_css = property(lambda self: self.EXTRA_CSS[self.extra_status])

    # ======== ITEMS FROM RESOURCEFILE =========== #
    download = FileField(_('Consumable File'),
                         storage=resource_storage,
                         **upto('file', blank=True))

    license = ForeignKey(License,
                         verbose_name=_("License"),
                         on_delete=SET_NULL,
                         **null)
    owner = BooleanField(_('Permission'), choices=OWNS, default=True)
    owner_name = CharField(_('Owner\'s Name'), max_length=128, **null)

    signature = FileField(_('Signature/Checksum'), **upto('sigs'))
    verified = BooleanField(default=False)
    mirror = BooleanField(default=False)
    embed = BooleanField(default=False)

    checked_by = ForeignKey(settings.AUTH_USER_MODEL,
                            related_name='resource_checks',
                            **null)
    checked_sig = FileField(_('Counter Signature'), **upto('sigs'))

    objects = ResourceManager()

    class Meta:
        get_latest_by = 'created'

    def __unicode__(self):
        return self.name

    def __str__(self):
        return self.name.encode('utf8')

    def summary_string(self):
        return _("%(file_title)s by %(file_author)s (%(years)s)") \
                  % {'file_title': self.name, 'file_author': self.user, 'years': self.years}

    @classmethod
    def from_db(cls, db, field_names, values):
        db_instance = super(Resource, cls).from_db(db, field_names, values)
        # cache old value for link
        db_instance._old_link = values[field_names.index('link')]
        return db_instance

    @property
    def parent(self):
        if self.is_pasted:
            cat = self.category
            cat._parent = self.user.resources.all()
            return cat
        galleries = self.galleries.all()
        if galleries:
            return galleries[0]
        return self.user.resources.all()

    def description(self):
        if not self.desc:
            return '-'
        if '[[...]]' in self.desc:
            return self.desc.split('[[...]]')[0]
        return self.desc[:1000]

    def read_more(self):
        if not self.desc:
            return False
        return len(self.desc) > 1000 or '[[...]]' in self.desc

    def save(self, **kwargs):

        if self.download and not self.download._committed:
            # There is a download file and it has been changed

            if self.pk:
                # Save the old download file in a revision
                ResourceRevision.from_resource(self)

            self.verified = False
            self.edited = now()
            if hasattr(self, '_mime'):
                delattr(self, '_mime')

            try:
                self.media_type = str(self.file.mime)
                (self.media_x, self.media_y) = self.file.media_coords
            except ValueError:
                # Text file is corrupt, treat it as a binary
                self.media_type = 'application/octet-stream'

        # the signature on an existing resource has changed
        elif self.signature and not self.signature._committed:
            self.verified = False

        # mark as edited for link-only resources when they are added
        # or when link changes
        if not self.download and ((self._state.adding and self.link) or \
        (not self._state.adding and hasattr(self, '_old_link') and self._old_link != self.link)):
            self.edited = now()

        signal = False
        if not self.created and self.published:
            self.created = now()
            signal = True

        set_slug(self)
        ret = super(Resource, self).save(**kwargs)

        if signal:
            from .alert import post_publish
            post_publish.send(sender=Resource, instance=self)

        return ret

    def filename(self):
        return os.path.basename(self.download.name)

    def rendering_name(self):
        return os.path.basename(self.rendering.name)

    @property
    def file(self):
        if not hasattr(self, '_fileEx'):
            mime = MimeType(filename=self.download.path)
            self._fileEx = FileEx(self.download.file, mime)
        return self._fileEx

    def signature_type(self):
        return self.signature.name.rsplit('.', 1)[-1]

    def _verify(self, _type):
        if _type == 'sig':  # GPG Signature
            return gpg_verify(self.user, self.signature, self.download)
        return hash_verify(_type, self.signature, self.download)

    def endorsement(self):
        if not self.signature:
            return self.ENDORSE_NONE
        sig_type = self.signature_type()
        if not self.verified:
            self.verified = self._verify(sig_type)
            self.save()
        if self.verified and sig_type == 'sig':
            if self.user.has_perm('resource.change_resourcemirror'):
                return self.ENDORSE_AUTH
            return self.ENDORSE_SIGN
        return self.verified and self.ENDORSE_HASH or self.ENDORSE_NONE

    def rendering_url(self):
        if self.rendering:
            return self.rendering.url
        if self.download and self.mime().is_image():
            return self.download.url
        if self.thumbnail and os.path.exists(self.thumbnail.path):
            return self.thumbnail.url
        return self.icon_url()

    def thumbnail_url(self):
        """Returns a 190px thumbnail either from the thumbnail,
           the image itself or the mimetype icon"""
        if self.thumbnail and os.path.exists(self.thumbnail.path):
            return self.thumbnail.url
        if self.rendering and os.path.exists(self.rendering.path):
            return self.rendering.url
        if self.download and self.mime().is_image() \
              and os.path.exists(self.download.path) \
              and self.download.size < settings.MAX_PREVIEW_SIZE:
            return self.download.url
        return self.icon_url()

    def icon_url(self):
        if not self.download:
            icon = "broken"
            if self.link:
                icon = "video" if self.is_video else "link"
            return self.mime().static(icon)
        return self.mime().icon()

    def as_lines(self):
        """Returns the contents as text"""
        return syntaxer(self.as_text(), self.mime())

    def as_line_preview(self):
        """Returns a few lines of text"""
        return syntaxer(self.as_text(20), self.mime())

    def as_text(self, lines=None):
        return self.file.as_text(lines=lines)

    @property
    def is_pasted(self):
        # Using pk is 1 is NOT idea, XXX find a better way.
        return self.category_id and self.category_id == 1

    def get_absolute_url(self):
        if self.is_pasted:
            return reverse('pasted_item', args=[str(self.pk)])
        if self.slug:
            return reverse('resource',
                           kwargs={
                               'username': self.user.username,
                               'slug': self.slug
                           })
        return reverse('resource', kwargs={'pk': self.pk})

    @property
    def years(self):
        if self.created and self.edited \
          and self.created.year != self.edited.year:
            return "%d-%d" % (self.created.year, self.edited.year)
        if self.edited:
            return str(self.edited.year)
        if self.created is None:
            self.created = now()
            self.save()
        return str(self.created.year)

    def is_visible(self):
        return get_user(
        ).pk == self.user_id or self.published and self.is_available()

    def is_available(self):
        return not self.download or os.path.exists(self.download.path)

    def voted(self):
        return self.votes.filter(voter_id=get_user().pk).first()

    @property
    def is_new(self):
        return not self.category

    @property
    def is_video(self):
        return bool(self.video)

    @property
    def video(self):
        return video_embed(self.link)

    @property
    def next(self):
        """Get the next item in the gallery which needs information"""
        return Resource.objects.filter(category__isnull=True, user_id=self.user.pk)\
                    .exclude(pk=self.pk).latest('created')

    @property
    def gallery(self):
        try:
            return self.galleries.all()[0]
        except IndexError:
            return None

    @cached
    def mime(self):
        """Returns an encapsulated media_type as a MimeType object"""
        return MimeType(self.media_type or 'application/unknown')

    def link_from(self):
        """Returns the domain name or useful name if known for link"""
        try:
            domain = '.'.join(self.link.split("/")[2].split('.')[-2:])
            return DOMAINS.get(domain, domain)
        except Exception:
            return 'unknown'
示例#9
0
class User(AbstractUser):
    bio   = TextField(_('Bio'), validators=[MaxLengthValidator(4096)], **null)
    photo = ResizedImageField(_('Photograph (square)'), null=True, blank=True,
              upload_to='photos', max_width=190, max_height=190)
    language = CharField(_('Default Language'), max_length=8, choices=settings.LANGUAGES, **null)

    ircnick = CharField(_('IRC Nickname'), max_length=20, **null)
    ircpass = CharField(_('Freenode Password (optional)'), max_length=128, **null)

    dauser  = CharField(_('deviantArt User'), max_length=64, **null)
    ocuser  = CharField(_('Openclipart User'), max_length=64, **null)
    tbruser = CharField(_('Tumblr User'), max_length=64, **null)
    gpg_key = TextField(_('GPG Public Key'),
        help_text=_('<strong>Signing and Checksums for Uploads</strong><br/> '
                    'Either fill in a valid GPG key, so you can sign your uploads, '
                    'or just enter any text to activate the upload validation feature '
                    'which verifies your uploads by comparing checksums.<br/>'
                    '<strong>Usage in file upload/editing form:</strong><br/>'
                    'If you have submitted a GPG key, you can upload a *.sig file, '
                    'and your upload can be verified. You can also submit these checksum file types:<br/>'
                    '*.md5, *.sha1, *.sha224, *.sha256, *.sha384 or *.sha512'),
        validators=[MaxLengthValidator(262144)], **null)

    last_seen = DateTimeField(**null)
    visits    = IntegerField(default=0)

    def __str__(self):
        return self.name

    @property
    def name(self):
        if self.first_name or self.last_name:
            return self.get_full_name()
        return self.username

    class Meta:
        permissions = [
            ("use_irc", _("IRC Chat Training Complete")),
            ("website_cla_agreed", _("Agree to Website License")),
        ]
        db_table = 'auth_user'

    def get_ircnick(self):
        if not self.ircnick:
            return self.username
        return self.ircnick

    def photo_url(self):
        if self.photo:
            return self.photo.url
        return None

    def photo_preview(self):
        if self.photo:
            return '<img src="%s" style="max-width: 200px; max-height: 250px;"/>' % self.photo.url
        # Return an embedded svg, it's easier than dealing with static files.
        return """
        <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="200" height="250">
           <path style="stroke:#6c6c6c;stroke-width:.5px;fill:#ece8e6;"
           d="m1.2 1.2v248h27.6c-9.1-43 8-102 40.9-123-49.5-101 111-99.9 61.5 1.18 36.6 35.4 48.6 78.1 39.1 122h28.5v-248z"
           /></svg>"""
    photo_preview.allow_tags = True

    def quota(self):
        from resources.models import Quota
        groups = Q(group__in=self.groups.all()) | Q(group__isnull=True)
        quotas = Quota.objects.filter(groups)
        if quotas.count():
            return quotas.aggregate(Max('size'))['size__max'] * 1024
        return 0

    def get_absolute_url(self):
        if not self.username:
            return '/'
        return reverse('view_profile', kwargs={'username':self.username})

    def is_moderator(self):
        return self.has_perm("moderation.can_moderate")

    def visited_by(self, by_user):
        if by_user != self:
            self.visits += 1
            self.save(update_fields=['visits'])

    @property
    def teams(self):
        return Team.objects.filter(group__in=self.groups.all())

    def viewer_is_subscribed(self):
        from cms.utils.permissions import get_current_user as get_user
        user = get_user()
        if user.is_authenticated():
	    return bool(self.resources.subscriptions().get(user=user.pk))
        return False