# HG changeset patch # User Brian Neal # Date 1272750839 0 # Node ID b4305e18d3afd490614fc557fdb6d0910a6c29ed # Parent 40e5903903e164bcc44153c9a5b43a6297cfa459 Resolve ticket #74. Add user badges. Some extra credit was done here: also refactored how pending news, links, and downloads are handled. diff -r 40e5903903e1 -r b4305e18d3af gpp/bio/admin.py --- a/gpp/bio/admin.py Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/bio/admin.py Sat May 01 21:53:59 2010 +0000 @@ -4,13 +4,17 @@ import datetime from django.contrib import admin -from bio.models import UserProfile -from bio.models import UserProfileFlag + import bio.models from comments.models import Comment from forums.tools import delete_user_posts +class BadgeOwnerInline(admin.TabularInline): + model = bio.models.BadgeOwnership + extra = 1 + + class UserProfileAdmin(admin.ModelAdmin): search_fields = ('user__username', 'user__first_name', 'user__last_name', 'user__email') @@ -18,6 +22,7 @@ list_display = ('__unicode__', 'get_status_display', 'status_date') list_filter = ('status', ) date_hierarchy = 'status_date' + inlines = (BadgeOwnerInline, ) actions = ( 'mark_active', 'mark_resigned', @@ -93,5 +98,11 @@ list_display = ('__unicode__', 'flag_date', 'get_profile_url') -admin.site.register(UserProfile, UserProfileAdmin) -admin.site.register(UserProfileFlag, UserProfileFlagAdmin) +class BadgeAdmin(admin.ModelAdmin): + list_display = ('name', 'html', 'order', 'numeric_id', 'description') + list_editable = ('order', 'numeric_id') + + +admin.site.register(bio.models.UserProfile, UserProfileAdmin) +admin.site.register(bio.models.UserProfileFlag, UserProfileFlagAdmin) +admin.site.register(bio.models.Badge, BadgeAdmin) diff -r 40e5903903e1 -r b4305e18d3af gpp/bio/badges.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/bio/badges.py Sat May 01 21:53:59 2010 +0000 @@ -0,0 +1,35 @@ +"""This module contains user profile badge-related functionality.""" + +from bio.models import Badge +from bio.models import BadgeOwnership + + +# Numeric ID's for badges that are awarded for user actions: +(CONTRIBUTOR_PIN, CALENDAR_PIN, NEWS_PIN, LINK_PIN, DOWNLOAD_PIN, + SECURITY_PIN) = range(6) + + +def award_badge(badge_id, user): + """This function awards the badge specified by badge_id + to the given user. If the user already has the badge, + the badge count is incremented by one. + """ + import logging + try: + badge = Badge.objects.get(numeric_id=badge_id) + except Badge.DoesNotExist: + logging.error("Can't award badge with numeric_id = %d" % badge_id) + return + + profile = user.get_profile() + + # Does the user already have badges of this type? + try: + bo = BadgeOwnership.objects.get(profile=profile, badge=badge) + except BadgeOwnership.DoesNotExist: + # No badge of this type, yet + bo = BadgeOwnership(profile=profile, badge=badge, count=1) + else: + # Already have this badge + bo.count += 1 + bo.save() diff -r 40e5903903e1 -r b4305e18d3af gpp/bio/models.py --- a/gpp/bio/models.py Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/bio/models.py Sat May 01 21:53:59 2010 +0000 @@ -23,6 +23,33 @@ (STA_SPAMMER, "Spammer"), ) + +class Badge(models.Model): + """This model represents badges that users can earn.""" + image = models.ImageField(upload_to='badges') + name = models.CharField(max_length=64) + description = models.TextField(blank=True) + order = models.IntegerField() + numeric_id = models.IntegerField(db_index=True) + + class Meta: + ordering = ('order', ) + + def __unicode__(self): + return self.name + + def get_absolute_url(self): + return self.image.url + + def html(self): + """Returns a HTML img tag representation of the badge.""" + if self.image: + return u'%s' % ( + self.get_absolute_url(), self.name, self.name) + return u'' + html.allow_tags = True + + def avatar_file_path_for_user(username, filename): return os.path.join(settings.AVATAR_DIR, 'users', username, filename) @@ -52,6 +79,7 @@ status = models.IntegerField(default=STA_ACTIVE, choices=USER_STATUS_CHOICES) status_date = models.DateTimeField(auto_now_add=True) + badges = models.ManyToManyField(Badge, through="BadgeOwnership") def __unicode__(self): return self.user.username @@ -70,6 +98,13 @@ def get_absolute_url(self): return ('bio-view_profile', (), {'username': self.user.username}) + def badge_ownership(self): + if hasattr(self, '_badges'): + return self._badges + self._badges = BadgeOwnership.objects.filter(profile=self).select_related( + "badge") + return self._badges + class UserProfileFlag(models.Model): """This model represents a user flagging a profile as inappropriate.""" @@ -87,3 +122,27 @@ def get_profile_url(self): return 'Profile' % self.profile.get_absolute_url() get_profile_url.allow_tags = True + + +class BadgeOwnership(models.Model): + """This model represents the ownership of badges by users.""" + profile = models.ForeignKey(UserProfile) + badge = models.ForeignKey(Badge) + count = models.IntegerField(default=1) + + class Meta: + verbose_name_plural = "badge ownership" + ordering = ('badge__order', ) + + def __unicode__(self): + if self.count == 1: + return u"%s owns 1 %s" % (self.profile.user.username, + self.badge.name) + else: + return u"%s owns %d %s badges" % (self.profile.user.username, + self.count, self.badge.name) + + def badge_count_str(self): + if self.count == 1: + return u"1 %s" % self.badge.name + return u"%d %ss" % (self.count, self.badge.name) diff -r 40e5903903e1 -r b4305e18d3af gpp/bio/signals.py --- a/gpp/bio/signals.py Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/bio/signals.py Sat May 01 21:53:59 2010 +0000 @@ -3,7 +3,14 @@ """ from django.db.models.signals import post_save from django.contrib.auth.models import User + +import bio.badges from bio.models import UserProfile +from donations.models import Donation +from weblinks.models import Link +from downloads.models import Download +from news.models import Story + def on_user_save(sender, **kwargs): """ @@ -14,9 +21,51 @@ created = kwargs['created'] if created: user = kwargs['instance'] - profile = UserProfile() + profile = bio.models.UserProfile() profile.user = user profile.save() +def on_donation_save(sender, **kwargs): + """This function is called after a Donation is saved. + If the Donation was newly created and not anonymous, + award the user a contributor pin. + """ + if kwargs['created']: + donation = kwargs['instance'] + if not donation.is_anonymous and donation.user: + bio.badges.award_badge(bio.badges.CONTRIBUTOR_PIN, donation.user) + + +def on_link_save(sender, **kwargs): + """This function is called after a Link is saved. If the Link was newly + created, award the user a link pin. + """ + if kwargs['created']: + link = kwargs['instance'] + bio.badges.award_badge(bio.badges.LINK_PIN, link.user) + + +def on_download_save(sender, **kwargs): + """This function is called after a Download is saved. If the Download was + newly created, award the user a download pin. + """ + if kwargs['created']: + download = kwargs['instance'] + bio.badges.award_badge(bio.badges.DOWNLOAD_PIN, download.user) + + +def on_story_save(sender, **kwargs): + """This function is called after a Story is saved. If the Story was + newly created, award the user a news pin. + """ + if kwargs['created']: + story = kwargs['instance'] + bio.badges.award_badge(bio.badges.NEWS_PIN, story.submitter) + + post_save.connect(on_user_save, sender=User) +post_save.connect(on_donation_save, sender=Donation) +post_save.connect(on_link_save, sender=Link) +post_save.connect(on_download_save, sender=Download) +post_save.connect(on_story_save, sender=Story) diff -r 40e5903903e1 -r b4305e18d3af gpp/bio/views.py --- a/gpp/bio/views.py Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/bio/views.py Sat May 01 21:53:59 2010 +0000 @@ -21,6 +21,7 @@ from bio.models import UserProfile from bio.models import UserProfileFlag +from bio.models import BadgeOwnership from bio.forms import UploadAvatarForm from bio.forms import EditUserForm from bio.forms import EditUserProfileForm @@ -70,12 +71,15 @@ @login_required def my_profile(request): profile = request.user.get_profile() + badge_collection = BadgeOwnership.objects.filter( + profile=profile).select_related("badge") return render_to_response('bio/view_profile.html', { 'subject': request.user, 'profile': profile, 'hide_email': False, 'this_is_me': True, + 'badge_collection': badge_collection, }, context_instance = RequestContext(request)) @@ -89,15 +93,17 @@ return HttpResponseRedirect(reverse('bio.views.my_profile')) profile = user.get_profile() + hide_email = profile.hide_email - # work around MySQL's handling of Boolean - hide_email = bool(profile.hide_email) + badge_collection = BadgeOwnership.objects.filter( + profile=profile).select_related("badge") return render_to_response('bio/view_profile.html', { 'subject': user, 'profile': profile, 'hide_email': hide_email, 'this_is_me': False, + 'badge_collection': badge_collection, }, context_instance = RequestContext(request)) diff -r 40e5903903e1 -r b4305e18d3af gpp/comments/admin.py --- a/gpp/comments/admin.py Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/comments/admin.py Sat May 01 21:53:59 2010 +0000 @@ -4,6 +4,8 @@ from django.contrib import admin from comments.models import Comment from comments.models import CommentFlag +import bio.badges + class CommentAdmin(admin.ModelAdmin): fieldsets = ( @@ -25,8 +27,21 @@ search_fields = ('comment', 'user__username', 'ip_address') raw_id_fields = ('user', 'content_type') + class CommentFlagAdmin(admin.ModelAdmin): list_display = ('__unicode__', 'flag_date', 'get_comment_url') + actions = ('accept_flags', ) + + def accept_flags(self, request, qs): + """This admin action awards a security pin to the user who reported + the comment and then deletes the flagged comment object. + """ + for flag in qs: + bio.badges.award_badge(bio.badges.SECURITY_PIN, flag.user) + flag.delete() + + accept_flags.short_description = "Accept selected comment flags" + admin.site.register(Comment, CommentAdmin) admin.site.register(CommentFlag, CommentFlagAdmin) diff -r 40e5903903e1 -r b4305e18d3af gpp/core/templatetags/custom_admin_tags.py --- a/gpp/core/templatetags/custom_admin_tags.py Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/core/templatetags/custom_admin_tags.py Sat May 01 21:53:59 2010 +0000 @@ -6,11 +6,11 @@ from bio.models import UserProfileFlag from comments.models import CommentFlag -from downloads.models import Download +from downloads.models import PendingDownload from forums.models import FlaggedPost from gcalendar.models import Event from news.models import PendingStory -from weblinks.models import Link, FlaggedLink +from weblinks.models import PendingLink, FlaggedLink from shoutbox.models import ShoutFlag @@ -25,14 +25,14 @@ """ flagged_profiles = UserProfileFlag.objects.count() flagged_comments = CommentFlag.objects.count() - new_downloads = Download.objects.filter(is_public=False).count() + new_downloads = PendingDownload.objects.count() flagged_posts = FlaggedPost.objects.count() event_requests = Event.objects.filter( Q(status=Event.NEW) | Q(status=Event.EDIT_REQ) | Q(status=Event.DEL_REQ)).count() new_stories = PendingStory.objects.count() - new_links = Link.objects.filter(is_public=False).count() + new_links = PendingLink.objects.count() broken_links = FlaggedLink.objects.count() flagged_shouts = ShoutFlag.objects.count() diff -r 40e5903903e1 -r b4305e18d3af gpp/downloads/admin.py --- a/gpp/downloads/admin.py Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/downloads/admin.py Sat May 01 21:53:59 2010 +0000 @@ -1,9 +1,12 @@ """ This file contains the automatic admin site definitions for the downloads models. """ +import datetime + from django.contrib import admin from django.conf import settings +from downloads.models import PendingDownload from downloads.models import Download from downloads.models import Category from downloads.models import AllowedExtension @@ -15,6 +18,39 @@ readonly_fields = ('count', ) +class PendingDownloadAdmin(admin.ModelAdmin): + exclude = ('html', ) + list_display = ('title', 'user', 'category', 'date_added', 'ip_address', 'size') + ordering = ('date_added', ) + raw_id_fields = ('user', ) + + actions = ('approve_downloads', ) + + def approve_downloads(self, request, qs): + for pending_dl in qs: + dl = Download( + title=pending_dl.title, + category=pending_dl.category, + description=pending_dl.description, + html=pending_dl.html, + file=pending_dl.file, + user=pending_dl.user, + date_added=datetime.datetime.now(), + ip_address=pending_dl.ip_address, + hits=0, + average_score=0.0, + total_votes=0, + is_public=True) + dl.save() + + # If we don't do this, the actual file will be deleted when + # the pending download is deleted. + pending_dl.file = None + pending_dl.delete() + + approve_downloads.short_description = "Approve selected downloads" + + class DownloadAdmin(admin.ModelAdmin): exclude = ('html', ) list_display = ('title', 'user', 'category', 'date_added', 'ip_address', @@ -33,6 +69,7 @@ date_hierarchy = 'vote_date' +admin.site.register(PendingDownload, PendingDownloadAdmin) admin.site.register(Download, DownloadAdmin) admin.site.register(Category, CategoryAdmin) admin.site.register(AllowedExtension) diff -r 40e5903903e1 -r b4305e18d3af gpp/downloads/forms.py --- a/gpp/downloads/forms.py Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/downloads/forms.py Sat May 01 21:53:59 2010 +0000 @@ -6,7 +6,7 @@ from django import forms from django.conf import settings -from downloads.models import Download +from downloads.models import PendingDownload from downloads.models import AllowedExtension @@ -34,7 +34,7 @@ raise forms.ValidationError('The file extension "%s" is not allowed.' % ext) class Meta: - model = Download + model = PendingDownload fields = ('title', 'category', 'description', 'file') class Media: diff -r 40e5903903e1 -r b4305e18d3af gpp/downloads/models.py --- a/gpp/downloads/models.py Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/downloads/models.py Sat May 01 21:53:59 2010 +0000 @@ -45,16 +45,44 @@ is_public=True).select_related() -class Download(models.Model): - """Model to represent a download.""" +class DownloadBase(models.Model): + """Abstract model to collect common download fields.""" title = models.CharField(max_length=128) category = models.ForeignKey(Category) description = models.TextField() html = models.TextField(blank=True) file = models.FileField(upload_to=download_path) user = models.ForeignKey(User) - date_added = models.DateTimeField(auto_now_add=True) + date_added = models.DateTimeField() ip_address = models.IPAddressField('IP Address') + + class Meta: + abstract = True + + def size(self): + return filesizeformat(self.file.size) + + +class PendingDownload(DownloadBase): + """This model represents pending downloads created by users. These pending + downloads must be approved by an admin before they turn into "real" + Downloads and are visible on site. + """ + class Meta: + ordering = ('date_added', ) + + def __unicode__(self): + return self.title + + def save(self, *args, **kwargs): + if not self.pk: + self.date_added = datetime.datetime.now() + self.html = site_markup(self.description) + super(PendingDownload, self).save(*args, **kwargs) + + +class Download(DownloadBase): + """Model to represent a download.""" hits = models.IntegerField(default=0) average_score = models.FloatField(default=0.0) total_votes = models.IntegerField(default=0) @@ -83,9 +111,6 @@ self.average_score = total_score / self.total_votes return self.average_score - def size(self): - return filesizeformat(self.file.size) - class AllowedExtensionManager(models.Manager): def get_extension_list(self): diff -r 40e5903903e1 -r b4305e18d3af gpp/downloads/views.py --- a/gpp/downloads/views.py Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/downloads/views.py Sat May 01 21:53:59 2010 +0000 @@ -161,7 +161,6 @@ dl = form.save(commit=False) dl.user = request.user dl.ip_address = request.META.get('REMOTE_ADDR', None) - dl.is_public = False dl.save() email_admins('New download for approval', """Hello, diff -r 40e5903903e1 -r b4305e18d3af gpp/forums/admin.py --- a/gpp/forums/admin.py Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/forums/admin.py Sat May 01 21:53:59 2010 +0000 @@ -9,6 +9,7 @@ from forums.models import Post from forums.models import FlaggedPost from forums.models import TopicLastVisit +import bio.badges class CategoryAdmin(admin.ModelAdmin): @@ -47,6 +48,17 @@ class FlaggedPostAdmin(admin.ModelAdmin): list_display = ('__unicode__', 'flag_date', 'get_post_url') + actions = ('accept_flags', ) + + def accept_flags(self, request, qs): + """This admin action awards a security pin to the user who reported + the post and then deletes the flagged post object. + """ + for flag in qs: + bio.badges.award_badge(bio.badges.SECURITY_PIN, flag.user) + flag.delete() + + accept_flags.short_description = "Accept selected flagged posts" class TopicLastVisitAdmin(admin.ModelAdmin): diff -r 40e5903903e1 -r b4305e18d3af gpp/gcalendar/admin.py --- a/gpp/gcalendar/admin.py Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/gcalendar/admin.py Sat May 01 21:53:59 2010 +0000 @@ -7,6 +7,7 @@ from gcalendar.models import Event from gcalendar.admin_views import google_sync +import bio.badges class EventAdmin(admin.ModelAdmin): @@ -47,6 +48,9 @@ event.save() count += 1 + if event.status == Event.NEW_APRV: + bio.badges.award_badge(bio.badges.CALENDAR_PIN, event.user) + msg = "1 event was" if count == 1 else "%d events were" % count msg += " approved." self.message_user(request, msg) diff -r 40e5903903e1 -r b4305e18d3af gpp/news/admin.py --- a/gpp/news/admin.py Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/news/admin.py Sat May 01 21:53:59 2010 +0000 @@ -1,32 +1,52 @@ """ This file contains the automatic admin site definitions for the News models. """ +import datetime from django.contrib import admin from django.conf import settings +from django.core.cache import cache from news.models import PendingStory from news.models import Story from news.models import Category class PendingStoryAdmin(admin.ModelAdmin): - list_display = ('title', 'date_submitted', 'submitter') - list_filter = ('date_submitted', ) - search_fields = ('title', 'short_text', 'long_text') - date_hierarchy = 'date_submitted' + list_display = ('title', 'date_submitted', 'submitter') + list_filter = ('date_submitted', ) + search_fields = ('title', 'short_text', 'long_text') + date_hierarchy = 'date_submitted' + actions = ('approve_story', ) - class Media: - js = settings.GPP_THIRD_PARTY_JS['tiny_mce'] + def approve_story(self, request, qs): + for pending_story in qs: + story = Story( + title=pending_story.title, + submitter=pending_story.submitter, + category=pending_story.category, + short_text=pending_story.short_text, + long_text=pending_story.long_text, + date_submitted=datetime.datetime.now(), + allow_comments=pending_story.allow_comments, + tags=pending_story.tags) + story.save() + pending_story.delete() + cache.delete('home_news') + + approve_story.short_description = "Approve selected pending stories" + + class Media: + js = settings.GPP_THIRD_PARTY_JS['tiny_mce'] class StoryAdmin(admin.ModelAdmin): - list_display = ('title', 'date_published', 'submitter', 'category') - list_filter = ('date_published', 'category') - search_fields = ('title', 'short_text', 'long_text') - date_hierarchy = 'date_published' + list_display = ('title', 'date_submitted', 'submitter', 'category') + list_filter = ('date_submitted', 'category') + search_fields = ('title', 'short_text', 'long_text') + date_hierarchy = 'date_submitted' - class Media: - js = settings.GPP_THIRD_PARTY_JS['tiny_mce'] + class Media: + js = settings.GPP_THIRD_PARTY_JS['tiny_mce'] admin.site.register(Category) diff -r 40e5903903e1 -r b4305e18d3af gpp/news/feeds.py --- a/gpp/news/feeds.py Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/news/feeds.py Sat May 01 21:53:59 2010 +0000 @@ -22,7 +22,7 @@ return copyright_str() def items(self): - return Story.objects.order_by('-date_published')[:5] + return Story.objects.order_by('-date_submitted')[:5] def item_title(self, item): return item.title @@ -34,7 +34,7 @@ return get_full_name(item.submitter) def item_pubdate(self, item): - return item.date_published + return item.date_submitted def item_categories(self, item): return (item.category.title, ) diff -r 40e5903903e1 -r b4305e18d3af gpp/news/models.py --- a/gpp/news/models.py Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/news/models.py Sat May 01 21:53:59 2010 +0000 @@ -25,32 +25,29 @@ ordering = ('title', ) -class PendingStory(models.Model): - """Stories submitted by users are held pending admin approval""" +class StoryBase(models.Model): + """Abstract model to collect common fields.""" title = models.CharField(max_length=255) submitter = models.ForeignKey(User) category = models.ForeignKey(Category) short_text = models.TextField() long_text = models.TextField(blank=True) - date_submitted = models.DateTimeField(auto_now_add=True, db_index=True) + date_submitted = models.DateTimeField(db_index=True) allow_comments = models.BooleanField(default=True) - approved = models.BooleanField(default=False) tags = TagField() + class Meta: + abstract = True + + +class PendingStory(StoryBase): + """Stories submitted by users are held pending admin approval""" + def save(self, *args, **kwargs): - if self.approved: - Story.objects.create(title=self.title, - submitter=self.submitter, - category=self.category, - short_text=self.short_text, - long_text=self.long_text, - allow_comments=self.allow_comments, - date_published=datetime.datetime.now(), - tags=self.tags) - self.delete() - cache.delete('home_news') - else: - super(PendingStory, self).save(*args, **kwargs) + if not self.pk: + self.date_submitted = datetime.datetime.now() + + super(PendingStory, self).save(*args, **kwargs) def __unicode__(self): return self.title @@ -60,16 +57,8 @@ verbose_name_plural = 'Pending Stories' -class Story(models.Model): +class Story(StoryBase): """Model for news stories""" - title = models.CharField(max_length=255) - submitter = models.ForeignKey(User) - category = models.ForeignKey(Category) - short_text = models.TextField() - long_text = models.TextField(blank=True) - allow_comments = models.BooleanField(default=True) - date_published = models.DateTimeField(db_index=True) - tags = TagField() @models.permalink def get_absolute_url(self): @@ -79,13 +68,13 @@ return self.title class Meta: - ordering = ('-date_published', ) + ordering = ('-date_submitted', ) verbose_name_plural = 'Stories' def can_comment_on(self): now = datetime.datetime.now() - delta = now - self.date_published - return delta.days < 30 + delta = now - self.date_submitted + return self.allow_comments and delta.days < 30 def save(self, *args, **kwargs): super(Story, self).save(*args, **kwargs) diff -r 40e5903903e1 -r b4305e18d3af gpp/news/views.py --- a/gpp/news/views.py Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/news/views.py Sat May 01 21:53:59 2010 +0000 @@ -57,7 +57,7 @@ ####################################################################### def archive_index(request): - dates = Story.objects.dates('date_published', 'month', order='DESC') + dates = Story.objects.dates('date_submitted', 'month', order='DESC') return render_to_response('news/archive_index.html', { 'title': 'News Archive', 'dates': dates, @@ -68,7 +68,7 @@ ####################################################################### def archive(request, year, month, page=1): - stories = Story.objects.filter(date_published__year=year, date_published__month=month) + stories = Story.objects.filter(date_submitted__year=year, date_submitted__month=month) paginator = create_paginator(stories) try: the_page = paginator.page(int(page)) @@ -144,7 +144,7 @@ stories = stories.filter( Q(title__icontains=query_text) | Q(short_text__icontains=query_text) | - Q(long_text__icontains=query_text)).order_by('-date_published') + Q(long_text__icontains=query_text)).order_by('-date_submitted') paginator = create_paginator(stories) try: diff -r 40e5903903e1 -r b4305e18d3af gpp/templates/bio/view_profile.html --- a/gpp/templates/bio/view_profile.html Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/templates/bio/view_profile.html Sat May 01 21:53:59 2010 +0000 @@ -55,6 +55,16 @@ {% endif %} Elsewhere{% elsewhere_links subject %} Time Zone{{ profile.time_zone }} + Badges + {% if badge_collection %} + + + {% for bo in badge_collection %} + + {% endfor %} +
BadgeQty.NameDescription
{{ bo.badge.html|safe }}{{ bo.count }}{{ bo.badge.name }}{{ bo.badge.description }}
+ {% endif %} + {% if this_is_me %} diff -r 40e5903903e1 -r b4305e18d3af gpp/templates/core/admin_dashboard.html --- a/gpp/templates/core/admin_dashboard.html Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/templates/core/admin_dashboard.html Sat May 01 21:53:59 2010 +0000 @@ -20,10 +20,10 @@
  • News: {{ new_stories }}
  • {% endif %} {% if new_downloads %} -
  • Downloads: {{ new_downloads }}
  • +
  • Downloads: {{ new_downloads }}
  • {% endif %} {% if new_links %} -
  • New Links: {{ new_links }}
  • +
  • New Links: {{ new_links }}
  • {% endif %} {% if broken_links %}
  • Broken Links: {{ broken_links }}
  • diff -r 40e5903903e1 -r b4305e18d3af gpp/templates/forums/display_post.html --- a/gpp/templates/forums/display_post.html Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/templates/forums/display_post.html Sat May 01 21:53:59 2010 +0000 @@ -10,6 +10,9 @@ {% if post.user_profile.location %} Location: {{ post.user_profile.location }}
    {% endif %} + {% for bo in post.user_profile.badge_ownership %} + {{ bo.badge_count_str }} + {% endfor %} {% if user.is_authenticated %}

    diff -r 40e5903903e1 -r b4305e18d3af gpp/templates/news/category_index.html --- a/gpp/templates/news/category_index.html Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/templates/news/category_index.html Sat May 01 21:53:59 2010 +0000 @@ -20,7 +20,7 @@

    {% else %} diff -r 40e5903903e1 -r b4305e18d3af gpp/templates/news/story.html --- a/gpp/templates/news/story.html Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/templates/news/story.html Sat May 01 21:53:59 2010 +0000 @@ -15,7 +15,7 @@ {% block news_content %}

    {{ story.title }}

    - Submitted by {{ story.submitter.username }} on {{ story.date_published|date:"F d, Y" }}. + Submitted by {{ story.submitter.username }} on {{ story.date_submitted|date:"F d, Y" }}.

    diff -r 40e5903903e1 -r b4305e18d3af gpp/templates/news/story_summary.html --- a/gpp/templates/news/story_summary.html Wed Apr 28 03:00:31 2010 +0000 +++ b/gpp/templates/news/story_summary.html Sat May 01 21:53:59 2010 +0000 @@ -7,7 +7,7 @@

    {{ story.title }}

    {% endif %}
    - Submitted by {{ story.submitter.username }} on {{ story.date_published|date:"F d, Y" }}. + Submitted by {{ story.submitter.username }} on {{ story.date_submitted|date:"F d, Y" }}.