Skip to content
This repository has been archived by the owner on Aug 26, 2022. It is now read-only.

Commit

Permalink
bug 632195: Tag namespaces (tech: and challenge:); complete switch-ov…
Browse files Browse the repository at this point in the history
…er to django-taggit
  • Loading branch information
lmorchard committed May 31, 2011
1 parent 452bc3c commit 19bbfd1
Show file tree
Hide file tree
Showing 15 changed files with 325 additions and 56 deletions.
59 changes: 37 additions & 22 deletions apps/demos/__init__.py
Expand Up @@ -51,8 +51,23 @@ def scale_image(img_upload, img_max_size):

# HACK: For easier L10N, define tag descriptions in code instead of as a DB model
TAG_DESCRIPTIONS = dict( (x['tag_name'], x) for x in getattr(settings, 'TAG_DESCRIPTIONS', (

{
"tag_name": "challenge:2011-05-27",
"title": _("Dev Derby Challenge #1"),
"description": _("This is the first Dev Derby!"),
"learn_more": [],
},
{
"tag_name": "challenge:2011-06-27",
"title": _("Dev Derby Challenge #2"),
"description": _("This is the second Dev Derby!"),
"learn_more": [],
},


{
"tag_name": "audio",
"tag_name": "tech:audio",
"title": _("Audio"),
"description": _("Mozilla's Audio Data API extends the current HTML5 API and allows web developers to read and write raw audio data."),
"learn_more": (
Expand All @@ -62,7 +77,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "canvas",
"tag_name": "tech:canvas",
"title": _("Canvas"),
"description": _("The HTML5 canvas element allows you to display scriptable renderings of 2D shapes and bitmap images."),
"learn_more": (
Expand All @@ -72,7 +87,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "css3",
"tag_name": "tech:css3",
"title": _("CSS3"),
"description": _("Cascading Style Sheets level 3 (CSS3) provide serveral new features and properties to enhance the formatting and look of documents written in different kinds of markup languages like HTML or XML."),
"learn_more": (
Expand All @@ -82,7 +97,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "device",
"tag_name": "tech:device",
"title": _("Device"),
"description": _("Media queries and orientation events let authors adjust their layout on hand-held devices such as mobile phones."),
"learn_more": (
Expand All @@ -91,7 +106,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "files",
"tag_name": "tech:files",
"title": _("Files"),
"description": _("The File API allows web developers to use file objects in web applications, as well as selecting and accessing their data."),
"learn_more": (
Expand All @@ -100,7 +115,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "fonts",
"tag_name": "tech:fonts",
"title": _("Fonts & Type"),
"description": _("The CSS3-Font specification contains enhanced features for fonts and typography like embedding own fonts via @font-face or controlling OpenType font features directly via CSS."),
"learn_more": (
Expand All @@ -110,7 +125,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "forms",
"tag_name": "tech:forms",
"title": _("Forms"),
"description": _("Form elements and attributes in HTML5 provide a greater degree of semantic mark-up than HTML4 and remove a great deal of the need for tedious scripting and styling that was required in HTML4."),
"learn_more": (
Expand All @@ -120,7 +135,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "geolocation",
"tag_name": "tech:geolocation",
"title": _("Geolocation"),
"description": _("The Geolocation API allows web applications to access the user's geographical location."),
"learn_more": (
Expand All @@ -130,7 +145,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "javascript",
"tag_name": "tech:javascript",
"title": _("JavaScript"),
"description": _("JavaScript is a lightweight, object-oriented programming language, commonly used for scripting interactive behavior on web pages and in web applications."),
"learn_more": (
Expand All @@ -140,7 +155,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "html5",
"tag_name": "tech:html5",
"title": _("HTML5"),
"description": _("HTML5 is the newest version of the HTML standard, currently under development."),
"learn_more": (
Expand All @@ -150,7 +165,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "indexeddb",
"tag_name": "tech:indexeddb",
"title": _("IndexedDB"),
"description": _("IndexedDB is an API for client-side storage of significant amounts of structured data and for high performance searches on this data using indexes. "),
"learn_more": (
Expand All @@ -160,7 +175,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "dragndrop",
"tag_name": "tech:dragndrop",
"title": _("Drag and Drop"),
"description": _("Drag and Drop features allow the user to move elements on the screen using the mouse pointer."),
"learn_more": (
Expand All @@ -170,7 +185,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "mobile",
"tag_name": "tech:mobile",
"title": _("Mobile"),
"description": _("Firefox Mobile brings the true Web experience to mobile phones and other non-PC devices."),
"learn_more": (
Expand All @@ -180,7 +195,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "offlinesupport",
"tag_name": "tech:offlinesupport",
"title": _("Offline Support"),
"description": _("Offline caching of web applications' resources using the application cache and local storage."),
"learn_more": (
Expand All @@ -190,7 +205,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "svg",
"tag_name": "tech:svg",
"title": _("SVG"),
"description": _("Scalable Vector Graphics (SVG) is an XML based language for describing two-dimensional vector graphics."),
"learn_more": (
Expand All @@ -200,7 +215,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "video",
"tag_name": "tech:video",
"title": _("Video"),
"description": _("The HTML5 video element provides integrated support for playing video media without requiring plug-ins."),
"learn_more": (
Expand All @@ -210,7 +225,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "webgl",
"tag_name": "tech:webgl",
"title": _("WebGL"),
"description": _("In the context of the HTML canvas element WebGL provides an API for 3D graphics in the browser."),
"learn_more": (
Expand All @@ -220,7 +235,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "websockets",
"tag_name": "tech:websockets",
"title": _("WebSockets"),
"description": _("WebSockets is a technology that makes it possible to open an interactive communication session between the user's browser and a server."),
"learn_more": (
Expand All @@ -230,7 +245,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "webworkers",
"tag_name": "tech:webworkers",
"title": _("Web Workers"),
"description": _("Web Workers provide a simple means for web content to run scripts in background threads."),
"learn_more": (
Expand All @@ -240,7 +255,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "xhr",
"tag_name": "tech:xhr",
"title": _("XMLHttpRequest"),
"description": _("XMLHttpRequest (XHR) is used to send HTTP requests directly to a webserver and load the response data directly back into the script."),
"learn_more": (
Expand All @@ -250,7 +265,7 @@ def scale_image(img_upload, img_max_size):
),
},
{
"tag_name": "multitouch",
"tag_name": "tech:multitouch",
"title": _("Multi-touch"),
"description": _("Track the movement of the user's finger on a touch screen, monitoring the raw touch events generated by the system."),
"learn_more": (
Expand Down Expand Up @@ -295,4 +310,4 @@ def scale_image(img_upload, img_max_size):
},
)))

DEMOS_CACHE_NS_KEY = getattr(settings, 'DEMOS_CACHE_NS_KEY', 'demos_listing')
DEMOS_CACHE_NS_KEY = getattr(settings, 'DEMOS_CACHE_NS_KEY', 'demos_listing')
4 changes: 3 additions & 1 deletion apps/demos/admin.py
Expand Up @@ -6,7 +6,9 @@
class SubmissionAdmin(admin.ModelAdmin):
change_list_template = 'smuggler/change_list.html'

list_display = ( 'title', 'creator', 'featured', 'hidden', 'censored', 'tags', 'modified', )
list_display = ( 'title', 'creator', 'featured', 'hidden', 'censored', 'modified', )
list_editable = ( 'featured', 'hidden', 'censored', )

def queryset(self, request):
return Submission.admin_manager

Expand Down
87 changes: 82 additions & 5 deletions apps/demos/forms.py
Expand Up @@ -10,8 +10,11 @@
from django.conf import settings

from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User

from django.db import models

from django.contrib.auth.models import User, AnonymousUser

from django.core.exceptions import ObjectDoesNotExist
from django.core import validators
from django.core.exceptions import ValidationError
Expand All @@ -20,13 +23,14 @@
from django.core.files.uploadedfile import InMemoryUploadedFile

from . import scale_image
from .models import Submission, TAG_DESCRIPTIONS
from .models import Submission, TAG_DESCRIPTIONS, TAG_NAMESPACE_DEMO_CREATOR_WHITELIST

from captcha.fields import ReCaptchaField

import django.forms.fields
from django.forms.widgets import CheckboxSelectMultiple


import tagging.forms
from tagging.utils import parse_tag_input

Expand Down Expand Up @@ -101,7 +105,6 @@ def clean(self, value):
else:
return ','.join('"%s"' % x for x in value)


class SubmissionEditForm(MyModelForm):
"""Form accepting demo submissions"""

Expand All @@ -111,16 +114,84 @@ class Meta:
'navbar_optout': forms.Select
}
fields = (
'title', 'summary', 'description', 'hidden', 'tags',
'title', 'summary', 'description',
'tech_tags', 'challenge_tags',
'screenshot_1', 'screenshot_2', 'screenshot_3',
'screenshot_4', 'screenshot_5',
'video_url', 'navbar_optout',
'demo_package', 'source_code_url', 'license_name',
)

# Assemble tech tag choices from TAG_DESCRIPTIONS
tech_tags = forms.MultipleChoiceField(
label = "Tech tags",
widget = CheckboxSelectMultiple,
required = False,
choices = (
(x['tag_name'], x['title'])
for x in TAG_DESCRIPTIONS.values()
if x['tag_name'].startswith('tech:')
)
)

challenge_tags = forms.MultipleChoiceField(
label = "Dev Derby Challenge tags",
widget = CheckboxSelectMultiple,
required = False,
choices = (
(x['tag_name'], x['title'])
for x in TAG_DESCRIPTIONS.values()
if x['tag_name'].startswith('challenge:')
)
)

def __init__(self, *args, **kwargs):

# Set the request user, for tag namespace permissions
self.request_user = kwargs.get('request_user', AnonymousUser)
del kwargs['request_user']

# Hit up the super class for init
super(SubmissionEditForm, self).__init__(*args, **kwargs)

# If we have an instance, try populating the user-accessible namespace
# form fields with exiating tags.
if 'instance' in kwargs and kwargs['instance']:
instance = kwargs['instance']
tags_orig = (
( instance.pk is not None )
and [ x.name for x in self.instance.taggit_tags.all() ]
or [ ]
)
for ns in TAG_NAMESPACE_DEMO_CREATOR_WHITELIST:
self.initial['%s_tags' % ns[:-1]] = [
x for x in tags_orig if x.startswith(ns)
]

def clean(self):
cleaned_data = super(SubmissionEditForm, self).clean()

# Establish a set of tags, if none available
if 'taggit_tags' not in cleaned_data:
cleaned_data['taggit_tags'] = []

# If there are *_tags fields, append them as tags.
for k in cleaned_data:
if k.endswith('_tags'):
cleaned_data['taggit_tags'].extend(cleaned_data[k])

# If we have an instance, apply the submitted tags as per the permissions.
if self.instance:
tags_orig = (
( self.instance.pk is not None )
and [ x.name for x in self.instance.taggit_tags.all() ]
or [ ]
)
cleaned_data['taggit_tags'] = self.instance.resolve_allowed_tags(
tags_orig, cleaned_data['taggit_tags'], self.request_user
)

# If we have a demo_package, try validating it.
if 'demo_package' in self.files:
try:
demo_package = self.files['demo_package']
Expand All @@ -130,6 +201,13 @@ def clean(self):

return cleaned_data

def save(self, commit=True):
rv = super(SubmissionEditForm,self).save(commit)
if commit:
# Set the tags, if we have a go to commit.
self.instance.taggit_tags.set(*self.cleaned_data['taggit_tags'])
return rv


class SubmissionNewForm(SubmissionEditForm):

Expand All @@ -139,4 +217,3 @@ class Meta(SubmissionEditForm.Meta):
captcha = ReCaptchaField(label=_("Show us you're human"))
accept_terms = forms.BooleanField(initial=False, required=True)


10 changes: 6 additions & 4 deletions apps/demos/helpers.py
Expand Up @@ -32,6 +32,8 @@
from tagging.models import Tag, TaggedItem
from tagging.utils import LINEAR, LOGARITHMIC

from taggit.models import TaggedItem

from .models import Submission, TAG_DESCRIPTIONS, DEMO_LICENSES
from . import DEMOS_CACHE_NS_KEY

Expand Down Expand Up @@ -116,8 +118,8 @@ def submission_listing_cache_key(*args, **kw):
def submission_listing(request, submission_list, is_paginated, paginator, page_obj, feed_title, feed_url):
return locals()

@register.inclusion_tag('demos/elements/tags_list.html')
def tags_list(): return locals()
@register.inclusion_tag('demos/elements/tech_tags_list.html')
def tech_tags_list(): return locals()

# Not cached, because it's small and changes based on current search query string
@register.inclusion_tag('demos/elements/search_form.html')
Expand Down Expand Up @@ -198,12 +200,12 @@ def tag_learn_more(tag):

@register.function
def tags_for_object(obj):
tags = Tag.objects.get_for_object(obj)
tags = obj.taggit_tags.all()
return tags

@register.function
def tags_used_for_submissions():
return Tag.objects.usage_for_model(Submission, counts=True, min_count=1)
return TaggedItem.tags_for(Submission)

@register.filter
def date_diff(timestamp, to=None):
Expand Down

0 comments on commit 19bbfd1

Please sign in to comment.