Permalink
Browse files

Merge pull request #155 from mozilla/development

Merge split tagfields from staging to live
  • Loading branch information...
2 parents e898bf7 + 4791b2a commit f9fecb2a354a9c7bc9a080abb56a1663918d7c40 @rossbruniges rossbruniges committed Mar 7, 2013
View
@@ -10,9 +10,6 @@
[submodule "vendor-local/src/django-south"]
path = vendor-local/src/django-south
url = git://github.com/lambdafu/django-south.git
-[submodule "vendor-local/src/django-taggit"]
- path = vendor-local/src/django-taggit
- url = git://github.com/alex/django-taggit.git
[submodule "vendor-local/src/django-cache-machine"]
path = vendor-local/src/django-cache-machine
url = git://github.com/jbalogh/django-cache-machine.git
@@ -25,3 +22,6 @@
[submodule "vendor-local/src/sorl-thumbnail"]
path = vendor-local/src/sorl-thumbnail
url = git://github.com/sorl/sorl-thumbnail.git
+[submodule "vendor-local/src/django-taggit"]
+ path = vendor-local/src/django-taggit
+ url = https://github.com/ryanpitts/django-taggit.git
View
@@ -36,6 +36,7 @@
# LOCALE_REPO_URL = ''
GIT_PULL = "git pull -q origin %(branch)s"
+GIT_SYNC = "git submodule sync"
GIT_SUBMODULE = "git submodule update --init --recursive"
#SVN_CO = "svn checkout --force %(url)s locale"
#SVN_UP = "svn update"
@@ -57,6 +58,7 @@ def update_site(env, debug):
commands = [
(CHDIR, here),
(EXEC, GIT_PULL % project_branch),
+ (EXEC, GIT_SYNC),
(EXEC, GIT_SUBMODULE),
]
@@ -87,8 +89,9 @@ def update_site(env, debug):
#(EXEC, GIT_PULL % vendor_branch),
#(CHDIR, os.path.join(here)),
#(EXEC, GIT_SUBMODULE),
- (EXEC, 'python2.6 manage.py migrate'),
(EXEC, 'python2.6 manage.py collectstatic --noinput'),
+ (EXEC, 'python2.6 manage.py syncdb'),
+ (EXEC, 'python2.6 manage.py migrate'),
(EXEC, '/etc/init.d/httpd restart'),
#(EXEC, 'python2.6 manage.py compress_assets'),
]
View
@@ -26,16 +26,26 @@ class ArticleAdmin(AdminImageMixin, admin.ModelAdmin):
search_fields = ('title', 'body', 'summary',)
date_hierarchy = 'pubdate'
fieldsets = (
- ('', {'fields': (('title', 'slug'), 'subhead', ('pubdate', 'is_live'),)}),
+ ('', {'fields': (('title', 'slug'), 'subhead', ('pubdate', 'is_live'), ('article_type', 'tags'), 'technology_tags', 'concept_tags', )}),
('Article relationships', {'fields': ('authors', 'people', 'organizations', 'code',)}),
- ('Article body', {'fields': ('article_type', 'tags', 'image', 'image_caption', 'image_credit', 'summary', 'body', 'disable_auto_linebreaks')}),
+ ('Article body', {'fields': ('image', 'image_caption', 'image_credit', 'summary', 'body', 'disable_auto_linebreaks')}),
)
inlines = [ArticleBlockInline,]
+ readonly_fields = ('tags',)
+
+ def save_model(self, request, obj, form, change):
+ technology_tags_list = form.cleaned_data['technology_tags']
+ concept_tags_list = form.cleaned_data['concept_tags']
+ merged_tags = technology_tags_list + concept_tags_list
+ if merged_tags:
+ form.cleaned_data['tags'] = merged_tags
+
+ super(ArticleAdmin, self).save_model(request, obj, form, change)
def formfield_for_dbfield(self, db_field, **kwargs):
# More usable heights and widths in admin form fields
field = super(ArticleAdmin, self).formfield_for_dbfield(db_field, **kwargs)
- if db_field.name in ['subhead','tags']:
+ if db_field.name in ['subhead','tags','technology_tags','concept_tags']:
field.widget.attrs['style'] = 'width: 45em;'
if db_field.name in ['title','slug']:
field.widget.attrs['style'] = 'width: 30em;'
View
@@ -1,12 +1,14 @@
from datetime import datetime
+import itertools
from django.db import models
from django.template.defaultfilters import date as dj_date, linebreaks
from caching.base import CachingManager, CachingMixin
from sorl.thumbnail import ImageField
-from source.people.models import Person, Organization
from source.code.models import Code
+from source.people.models import Person, Organization
+from source.tags.models import TechnologyTaggedItem, ConceptTaggedItem
from taggit.managers import TaggableManager
@@ -19,6 +21,7 @@
('roundup', 'Roundup'),
('event', 'Event'),
('update', 'Community Update'),
+ ('learning', 'Learning'),
)
class LiveArticleManager(CachingManager):
@@ -43,7 +46,9 @@ class Article(CachingMixin, models.Model):
people = models.ManyToManyField(Person, blank=True, null=True)
organizations = models.ManyToManyField(Organization, blank=True, null=True)
code = models.ManyToManyField(Code, blank=True, null=True)
- tags = TaggableManager(blank=True)
+ tags = TaggableManager(blank=True, help_text='Automatic combined list of Technology Tags and Concept Tags, for easy searching')
+ technology_tags = TaggableManager(verbose_name='Technology Tags', help_text='A comma-separated list of tags describing relevant technologies', through=TechnologyTaggedItem, blank=True)
+ concept_tags = TaggableManager(verbose_name='Concept Tags', help_text='A comma-separated list of tags describing relevant concepts', through=ConceptTaggedItem, blank=True)
objects = models.Manager()
live_objects = LiveArticleManager()
disable_auto_linebreaks = models.BooleanField(default=False, help_text='Check this if body and article blocks already have HTML paragraph tags.')
@@ -82,7 +87,11 @@ def pretty_body_text(self):
# that already contains <p> tags
_body = linebreaks(_body)
return _body
-
+
+ @property
+ def merged_tag_list(self):
+ '''return a combined list of technology_tags and concept_tags'''
+ return [item for item in itertools.chain(self.technology_tags.all(), self.concept_tags.all())]
def get_live_organization_set(self):
return self.organizations.filter(is_live=True)
@@ -1 +1,9 @@
-<span class="category">{% if article.article_type %}<a href="{{ url('article_list_by_category', article.article_type) }}">{{ article.get_article_type_display() }}</a>{% endif %}{% if article.tags.all().exists() %} <span class="tags">{% for tag in article.tags.all() %}<a href="{{ url('article_list_by_tag', tag.slug) }}">{{ tag }}</a>{% if not loop.last %} {% endif %}{% endfor %}</span>{% endif %}</span>
+<span class="category">{% if article.article_type %}<a href="{{ url('article_list_by_category', article.article_type) }}">{{ article.get_article_type_display() }}</a>{% endif %}{% if article.tags.all().exists() %} <span class="tags">{% for tag in article.tags.all() %}<a href="{{ url('article_list_by_tag', tag.slug) }}">{{ tag }}</a>{% if not loop.last %} {% endif %}{% endfor %}</span>{% endif %}</span>
+
+{#
+
+ If we ever completely drop the original `tags` field, the code below will
+ generate the proper list of tags for an article object
+
+<span class="category">{% if article.article_type %}<a href="{{ url('article_list_by_category', article.article_type) }}">{{ article.get_article_type_display() }}</a>{% endif %}{% if article.merged_tag_list %} <span class="tags">{% for tag in article.merged_tag_list %}<a href="{{ url('article_list_by_tag', tag.slug) }}">{{ tag }}</a>{% if not loop.last %} {% endif %}{% endfor %}</span>{% endif %}</span>
+#}
@@ -29,6 +29,18 @@ <h6 class="date">{{ article.pretty_pubdate }}</h6>
<li><i class="icon-comments"></i><a href="{{ HTTP_PROTOCOL }}://{{ request.get_host() }}{{ article.get_absolute_url() }}#disqus_thread" data-disqus-identifier="article_detail_{{ article.pk }}">Comments</a></li>
</ul>
</div>
+
+ {% if article.article_type == "learning" %}
+ <div class="article-toc">
+ {% for article_block in article.articleblock_set.all() %}
+ {% if loop.first %}<h3 class="subhead list-header">Contents</h3>
+ <ul class="link-list">{% endif %}
+ <li><i class="icon-circle-arrow-right"></i><a href="#{{ article_block.slug }}">{{ article_block.title }}</a></li>
+ {% if loop.last %}</ul>{% endif %}
+ {% endfor %}
+ </div>
+ {% endif %}
+
<div class="article-people">
{% with code_link_list = article.get_live_code_set() %}
{% include "code/_code_link_list.html" %}{% endwith %}
View
@@ -4,7 +4,7 @@
from .models import Article
from source.base.utils import paginate
-from taggit.models import Tag
+from source.tags.utils import filter_queryset_by_tags
# Current iteration does not use this in nav, but leaving dict
# in place for feed, url imports until we make a permanent call
@@ -14,6 +14,11 @@
'slug': 'articles',
'article_types': ['project', 'tool', 'how-to', 'interview', 'roundtable', 'roundup', 'event', 'update'],
},
+ 'learning': {
+ 'name': 'Learning',
+ 'slug': 'learning',
+ 'article_types': ['learning',],
+ },
}
# Current iteration only has *one* articles section, but this map is in place
@@ -59,6 +64,11 @@
'parent_name': 'Features',
'parent_slug': 'articles',
},
+ 'learning': {
+ 'name': 'Learning',
+ 'parent_name': 'Learning',
+ 'parent_slug': 'learning',
+ },
}
class ArticleList(ListView):
@@ -67,9 +77,8 @@ class ArticleList(ListView):
def dispatch(self, *args, **kwargs):
self.section = kwargs.get('section', None)
self.category = kwargs.get('category', None)
- self.tags = None
self.tag_slugs = kwargs.get('tag_slugs', None)
- self.tag_slug_list = []
+ self.tags = []
return super(ArticleList, self).dispatch(*args, **kwargs)
def get_queryset(self):
@@ -80,11 +89,7 @@ def get_queryset(self):
elif self.category:
queryset = queryset.filter(article_type=self.category)
elif self.tag_slugs:
- self.tag_slug_list = self.tag_slugs.split('+')
- # need to fail if any item in slug list references nonexistent tag
- self.tags = [get_object_or_404(Tag, slug=tag_slug) for tag_slug in self.tag_slug_list]
- for tag_slug in self.tag_slug_list:
- queryset = queryset.filter(tags__slug=tag_slug)
+ queryset, self.tags = filter_queryset_by_tags(queryset, self.tag_slugs, self.tags)
return queryset
@@ -106,7 +111,7 @@ def get_section_links(self, context):
context.update({
'section': SECTION_MAP['articles'],
'active_nav': SECTION_MAP['articles']['slug'],
- 'tags':self.tags,
+ 'tags': self.tags,
'rss_link': reverse('article_list_by_tag_feed', kwargs={'tag_slugs': self.tag_slugs}),
})
else:
View
@@ -1,27 +1,30 @@
from django.contrib.syndication.views import Feed
from django.core.urlresolvers import reverse
+from django.db.models import Q
+from django.http import Http404
from django.shortcuts import get_object_or_404
from source.articles.models import Article
from source.articles.views import CATEGORY_MAP, SECTION_MAP
from source.code.models import Code
+from source.tags.models import TechnologyTag, ConceptTag
+from source.tags.utils import get_validated_tag_list, get_tag_filtered_queryset
from taggit.models import Tag
-class ArticleFeed(Feed):
- description_template = "feeds/article_description.html"
-
+class ObjectWithTagsFeed(Feed):
+ '''common get_object for Article and Code feeds to handle tag queries'''
def get_object(self, request, *args, **kwargs):
self.section = kwargs.get('section', None)
self.category = kwargs.get('category', None)
- self.tags = None
self.tag_slugs = kwargs.get('tag_slugs', None)
- self.tag_slug_list = []
if self.tag_slugs:
self.tag_slug_list = self.tag_slugs.split('+')
- # need to fail if any item in slug list references nonexistent tag
- self.tags = [get_object_or_404(Tag, slug=tag_slug) for tag_slug in self.tag_slug_list]
+ self.tags = get_validated_tag_list(self.tag_slug_list, tags=[])
return ''
+class ArticleFeed(ObjectWithTagsFeed):
+ description_template = "feeds/article_description.html"
+
def title(self, obj):
if self.section:
return "Source: %s" % SECTION_MAP[self.section]['name']
@@ -73,24 +76,13 @@ def items(self, obj):
elif self.category:
queryset = queryset.filter(article_type=self.category)
elif self.tag_slugs:
- for tag_slug in self.tag_slug_list:
- queryset = queryset.filter(tags__slug=tag_slug)
+ queryset = get_tag_filtered_queryset(queryset, self.tag_slug_list)
return queryset[:20]
-class CodeFeed(Feed):
- def get_object(self, request, *args, **kwargs):
- self.tags = None
- self.tag_slugs = kwargs.get('tag_slugs', None)
- self.tag_slug_list = []
- if self.tag_slugs:
- self.tag_slug_list = self.tag_slugs.split('+')
- # need to fail if any item in slug list references nonexistent tag
- self.tags = [get_object_or_404(Tag, slug=tag_slug) for tag_slug in self.tag_slug_list]
- return ''
-
+class CodeFeed(ObjectWithTagsFeed):
def title(self, obj):
identifier = ""
- if self.tags:
+ if self.tag_slugs:
identifier = " tagged '%s'" % "+".join([tag.name for tag in self.tags])
return "Source: Code%s" % identifier
@@ -113,7 +105,7 @@ def item_description(self, item):
def items(self, obj):
queryset = Code.live_objects.order_by('-created')
- for tag_slug in self.tag_slug_list:
- queryset = queryset.filter(tags__slug=tag_slug)
+ if self.tag_slugs:
+ queryset = get_tag_filtered_queryset(queryset, self.tag_slug_list)
return queryset[:20]
View
@@ -25,13 +25,13 @@
),
# matching /articles/ here to offer future support for multiple sections
url(
- regex = '^(?P<section>articles)/$',
+ regex = '^(?P<section>articles|learning)/$',
view = ArticleList.as_view(),
kwargs = {},
name = 'article_list_by_section',
),
url(
- regex = '^(?P<section>articles)/rss/$',
+ regex = '^(?P<section>articles|learning)/rss/$',
view = cache_page(ArticleFeed(), 60*15),
kwargs = {},
name = 'article_list_by_section_feed',
View
@@ -24,15 +24,25 @@ class CodeAdmin(AdminImageMixin, admin.ModelAdmin):
list_filter = ('is_live', 'is_active',)
search_fields = ('name', 'description',)
fieldsets = (
- ('', {'fields': (('name', 'slug'), ('is_live', 'is_active', 'seeking_contributors'), 'url', 'tags', 'screenshot', 'description', 'summary',)}),
+ ('', {'fields': (('name', 'slug'), ('is_live', 'is_active', 'seeking_contributors'), 'url', 'tags', 'technology_tags', 'concept_tags', 'screenshot', 'description', 'summary',)}),
('Related objects', {'fields': ('people', 'organizations',)}),
)
inlines = [CodeLinkInline,]
+ readonly_fields = ('tags',)
+
+ def save_model(self, request, obj, form, change):
+ technology_tags_list = form.cleaned_data['technology_tags']
+ concept_tags_list = form.cleaned_data['concept_tags']
+ merged_tags = technology_tags_list + concept_tags_list
+ if merged_tags:
+ form.cleaned_data['tags'] = merged_tags
+
+ super(CodeAdmin, self).save_model(request, obj, form, change)
def formfield_for_dbfield(self, db_field, **kwargs):
# More usable heights and widths in admin form fields
field = super(CodeAdmin, self).formfield_for_dbfield(db_field, **kwargs)
- if db_field.name in ['url','tags']:
+ if db_field.name in ['url','tags','technology_tags','concept_tags']:
field.widget.attrs['style'] = 'width: 45em;'
if db_field.name in ['name','slug']:
field.widget.attrs['style'] = 'width: 30em;'
View
@@ -1,11 +1,13 @@
from datetime import datetime
+import itertools
from django.db import models
from django.template.defaultfilters import striptags, truncatewords
from caching.base import CachingManager, CachingMixin
from sorl.thumbnail import ImageField
from source.people.models import Person, Organization
+from source.tags.models import TechnologyTaggedItem, ConceptTaggedItem
from taggit.managers import TaggableManager
@@ -32,7 +34,9 @@ class Code(CachingMixin, models.Model):
repo_forks = models.PositiveIntegerField(blank=True, null=True)
repo_watchers = models.PositiveIntegerField(blank=True, null=True)
repo_description = models.TextField(blank=True)
- tags = TaggableManager(blank=True)
+ tags = TaggableManager(blank=True, help_text='Automatic combined list of Technology Tags and Concept Tags, for easy searching')
+ technology_tags = TaggableManager(verbose_name='Technology Tags', help_text='A comma-separated list of tags describing relevant technologies', through=TechnologyTaggedItem, blank=True)
+ concept_tags = TaggableManager(verbose_name='Concept Tags', help_text='A comma-separated list of tags describing relevant concepts', through=ConceptTaggedItem, blank=True)
objects = models.Manager()
live_objects = LiveCodeManager()
@@ -81,6 +85,11 @@ def description_or_summary(self):
elif self.summary:
return self.summary.strip()
return ''
+
+ @property
+ def merged_tag_list(self):
+ '''return a combined list of technology_tags and concept_tags'''
+ return [item for item in itertools.chain(self.technology_tags.all(), self.concept_tags.all())]
def get_live_article_set(self):
return self.article_set.filter(is_live=True, pubdate__lte=datetime.now)
@@ -1 +1,10 @@
-{% if code.tags.all().exists() %}<span class="category">Tags <span class="tags">{% for tag in code.tags.all() %}<a href="{{ url('code_list_by_tag', tag.slug) }}">{{ tag }}</a>{% if not loop.last %} {% endif %}{% endfor %}</span></span> {% endif %}
+{% if code.tags.all().exists() %}<span class="category">Tags <span class="tags">{% for tag in code.tags.all() %}<a href="{{ url('code_list_by_tag', tag.slug) }}">{{ tag }}</a>{% if not loop.last %} {% endif %}{% endfor %}</span></span> {% endif %}
+
+{#
+
+ If we ever completely drop the original `tags` field, the code below will
+ generate the proper list of tags for a code object
+
+{% if code.merged_tag_list %}<span class="category">Tags <span class="tags">{% for tag in code.merged_tag_list %}<a href="{{ url('code_list_by_tag', tag.slug) }}">{{ tag }}</a>{% if not loop.last %} {% endif %}{% endfor %}</span></span> {% endif %}
+
+#}
@@ -34,7 +34,7 @@ <h2 class="grouper-header"><span class="category">{{ alpha.grouper }}</span></h2
{% endfor %}
</div>
{% endfor %}
-
+ {% if not object_list %}<p>No matching code index entries found.</p>{% endif %}
{% endif %}
</div>
{% endblock content %}
Oops, something went wrong.

0 comments on commit f9fecb2

Please sign in to comment.