Skip to content
This repository was archived by the owner on Dec 17, 2019. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ docs/_build
_static
_templates
.tox
.project
.pydevproject
64 changes: 43 additions & 21 deletions badger/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from django.db.models.fields.files import FieldFile, ImageFieldFile
from django.core.mail import send_mail
from django.core.exceptions import ValidationError
from django.core.files.storage import FileSystemStorage
from django.core.files.base import ContentFile
from django.contrib.auth.models import User, AnonymousUser
from django.contrib.sites.models import Site
Expand All @@ -28,6 +27,7 @@
from django.template.loader import render_to_string

from django.core.serializers.json import DjangoJSONEncoder
from django.utils.importlib import import_module

try:
import django.utils.simplejson as json
Expand Down Expand Up @@ -99,8 +99,29 @@
os.path.join(getattr(settings, 'MEDIA_ROOT', 'media/'), 'uploads'))
UPLOADS_URL = getattr(settings, 'BADGER_MEDIA_URL',
urljoin(getattr(settings, 'MEDIA_URL', '/media/'), 'uploads/'))
BADGE_UPLOADS_FS = FileSystemStorage(location=UPLOADS_ROOT,
base_url=UPLOADS_URL)

# Copied from Django
def get_storage_class(import_path=None):
if import_path is None:
import_path = settings.DEFAULT_FILE_STORAGE
try:
dot = import_path.rindex('.')
except ValueError:
raise ImproperlyConfigured("%s isn't a storage module." % import_path)
module, classname = import_path[:dot], import_path[dot+1:]
try:
mod = import_module(module)
except ImportError as e:
raise ImproperlyConfigured('Error importing storage module %s: "%s"' % (module, e))
try:
return getattr(mod, classname)
except AttributeError:
raise ImproperlyConfigured('Storage module "%s" does not define a "%s" class.' % (module, classname))

BADGE_UPLOADS_STORAGE_CLASS = getattr(settings, 'BADGE_UPLOADS_STORAGE',
'django.core.files.storage.FileSystemStorage')

BADGE_UPLOADS_STORAGE = get_storage_class(BADGE_UPLOADS_STORAGE_CLASS)()

DEFAULT_BADGE_IMAGE = getattr(settings, 'BADGER_DEFAULT_BADGE_IMAGE',
"%s/fixtures/default-badge.png" % dirname(__file__))
Expand Down Expand Up @@ -407,7 +428,7 @@ class Badge(models.Model):
description = models.TextField(blank=True,
help_text='Longer description of the badge and its criteria')
image = models.ImageField(blank=True, null=True,
storage=BADGE_UPLOADS_FS, upload_to=mk_upload_to('image', 'png'),
storage=BADGE_UPLOADS_STORAGE, upload_to=mk_upload_to('image', 'png'),
help_text='Upload an image to represent the badge')
prerequisites = models.ManyToManyField('self', symmetrical=False,
blank=True, null=True,
Expand Down Expand Up @@ -653,17 +674,6 @@ def as_obi_serialization(self, request=None):
else:
base_url = 'http://%s' % (Site.objects.get_current().domain,)

# see: https://github.com/brianlovesdata/openbadges/wiki/Assertions
if not self.creator:
issuer = SITE_ISSUER
else:
issuer = {
# TODO: Get from user profile instead?
"origin": urljoin(base_url, self.creator.get_absolute_url()),
"name": self.creator.username,
"contact": self.creator.email
}

data = {
# The version of the spec/hub this manifest is compatible with. Use
# "0.5.0" for the beta.
Expand All @@ -673,7 +683,7 @@ def as_obi_serialization(self, request=None):
# TODO: truncate more intelligently
"description": self.description[:128] or self.title[:128],
"criteria": urljoin(base_url, self.get_absolute_url()),
"issuer": issuer
"issuer": urljoin(base_url, "badge/issuer.json")
}

image_url = self.image and self.image.url or DEFAULT_BADGE_IMAGE_URL
Expand All @@ -698,7 +708,7 @@ class Award(models.Model):
help_text='Explanation and evidence for the badge award')
badge = models.ForeignKey(Badge)
image = models.ImageField(blank=True, null=True,
storage=BADGE_UPLOADS_FS,
storage=BADGE_UPLOADS_STORAGE,
upload_to=mk_upload_to('image', 'png'))
claim_code = models.CharField(max_length=32, blank=True,
default='', unique=False, db_index=True,
Expand Down Expand Up @@ -814,14 +824,26 @@ def as_obi_assertion(self, request=None):
recipient_text = '%s%s' % (self.user.email, hash_salt)
recipient_hash = ('sha256$%s' % hashlib.sha256(recipient_text)
.hexdigest())
recipient_data = {}
recipient_data['type'] = 'email'
recipient_data['salt'] = hash_salt
recipient_data['hashed'] = True
recipient_data['identity'] = recipient_hash

verify_data = {}
verify_data['type'] = 'hosted'
verify_data['url'] = urljoin(base_url, self.get_absolute_url())

assertion = {
"recipient": recipient_hash,
"salt": hash_salt,
"uid": str(self.pk),
"recipient": recipient_data,
"image": badge_data['image'],
"evidence": urljoin(base_url, self.get_absolute_url()),
# TODO: implement award expiration
# "expires": self.expires.isoformat(),
"issued_on": self.created.isoformat(),
"badge": badge_data
"issuedOn": self.created.isoformat(),
"badge": badge_data['criteria'],
"verify": verify_data
}
return assertion

Expand Down
1 change: 1 addition & 0 deletions badger/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
url(r'^badge/(?P<slug>.+)\.json$', 'detail',
kwargs=dict(format="json"),
name='badger.detail_json'),
url(r'^issuer.json$', 'issuer', name='badger.detail_json'),
url(r'^badge/(?P<slug>[^/]+)/?$', 'detail',
name='badger.detail'),
url(r'^badge/(?P<slug>[^/]+)/awards/?$', 'awards_by_badge',
Expand Down
23 changes: 23 additions & 0 deletions badger/urls_json_only.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
This is a simplified URLs list that omits any of the multiplayer features,
assuming that all badges will be managed from the admin interface, and most
badges will be awarded in badges.py
"""
from django.conf.urls import patterns, include, url

from django.conf import settings

from .feeds import (AwardsRecentFeed, AwardsByUserFeed, AwardsByBadgeFeed,
BadgesRecentFeed, BadgesByUserFeed)
from . import views


urlpatterns = patterns('badger.views',
url(r'^badge/(?P<slug>[^/]+)/awards/(?P<id>[^\.]+)\.json$', 'award_detail',
kwargs=dict(format="json"),
name='badger.award_detail_json'),
url(r'^badge/(?P<slug>[^\.]+)\.json$', 'detail',
kwargs=dict(format="json"),
name='badger.detail_json'),
url(r'^issuer.json$', 'issuer', name='badger.detail_json'),
)
1 change: 1 addition & 0 deletions badger/urls_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
url(r'^badge/(?P<slug>[^\.]+)\.json$', 'detail',
kwargs=dict(format="json"),
name='badger.detail_json'),
url(r'^issuer.json$', 'issuer', name='badger.detail_json'),
url(r'^badge/(?P<slug>[^/]+)/?$', 'detail',
name='badger.detail'),
url(r'^badge/(?P<slug>[^/]+)/awards/?$', 'awards_by_badge',
Expand Down
9 changes: 9 additions & 0 deletions badger/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,15 @@ def detail(request, slug, format="html"):
), context_instance=RequestContext(request))


def issuer(request):
data = getattr(settings, 'BADGER_SITE_ISSUER', {
"url": "http://mozilla.org",
"name": "Mozilla",
})
resp = HttpResponse(simplejson.dumps(data))
resp['Content-Type'] = 'application/json'
return resp

@require_http_methods(['GET', 'POST'])
@login_required
def create(request):
Expand Down