Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'learning' into development

  • Loading branch information...
commit e1f5da3b3e18d2e17f3ad6ac20c4535b0167ff1d 2 parents 186ffa5 + 3f08ac4
@ryanpitts ryanpitts authored
View
1  source/articles/admin.py
@@ -23,6 +23,7 @@ class ArticleAdmin(AdminImageMixin, admin.ModelAdmin):
prepopulated_fields = {'slug': ('title',)}
filter_horizontal = ('authors', 'people', 'organizations', 'code',)
list_filter = ('is_live', 'article_type',)
+ list_display = ('title', 'pubdate', 'article_type', 'is_live')
search_fields = ('title', 'body', 'summary',)
date_hierarchy = 'pubdate'
fieldsets = (
View
86 source/articles/models.py
@@ -2,7 +2,7 @@
import itertools
from django.db import models
-from django.template.defaultfilters import date as dj_date, linebreaks
+from django.template.defaultfilters import date as dj_date, linebreaks, removetags
from caching.base import CachingManager, CachingMixin
from sorl.thumbnail import ImageField
@@ -24,6 +24,73 @@
('learning', 'Learning'),
)
+# Current iteration does not use this in nav, but leaving dict
+# in place for feed, url imports until we make a permanent call
+SECTION_MAP = {
+ 'articles': {
+ 'name': 'Features',
+ 'slug': 'articles',
+ 'article_types': ['project', 'tool', 'how-to', 'interview', 'roundtable', 'roundup', 'event', 'update'],
+ 'gets_promo_items': False,
+ },
+ 'learning': {
+ 'name': 'Learning',
+ 'slug': 'learning',
+ 'article_types': ['learning',],
+ 'gets_promo_items': True,
+ },
+}
+
+# Current iteration only has *one* articles section, but this map is in place
+# in case we split out into multiple sections that need parent categories
+CATEGORY_MAP = {
+ 'project': {
+ 'name': 'Project',
+ 'parent_name': 'Features',
+ 'parent_slug': 'articles',
+ },
+ 'tool': {
+ 'name': 'Tool',
+ 'parent_name': 'Features',
+ 'parent_slug': 'articles',
+ },
+ 'how-to': {
+ 'name': 'How-to',
+ 'parent_name': 'Features',
+ 'parent_slug': 'articles',
+ },
+ 'interview': {
+ 'name': 'Interview',
+ 'parent_name': 'Features',
+ 'parent_slug': 'articles',
+ },
+ 'roundtable': {
+ 'name': 'Roundtable',
+ 'parent_name': 'Features',
+ 'parent_slug': 'articles',
+ },
+ 'roundup': {
+ 'name': 'Roundup',
+ 'parent_name': 'Features',
+ 'parent_slug': 'articles',
+ },
+ 'event': {
+ 'name': 'Event',
+ 'parent_name': 'Features',
+ 'parent_slug': 'articles',
+ },
+ 'update': {
+ 'name': 'Update',
+ 'parent_name': 'Features',
+ 'parent_slug': 'articles',
+ },
+ 'learning': {
+ 'name': 'Learning',
+ 'parent_name': 'Learning',
+ 'parent_slug': 'learning',
+ },
+}
+
class LiveArticleManager(CachingManager):
def get_query_set(self):
return super(LiveArticleManager, self).get_query_set().filter(is_live=True, pubdate__lte=datetime.now())
@@ -62,7 +129,17 @@ def __unicode__(self):
@models.permalink
def get_absolute_url(self):
return ('article_detail', (), {
- 'slug': self.slug })
+ 'section': self.section['slug'],
+ 'slug': self.slug
+ })
+
+ @property
+ def section(self):
+ '''determine whether article matches specific section'''
+ for section in SECTION_MAP:
+ if self.article_type in SECTION_MAP[section]['article_types']:
+ return SECTION_MAP[section]
+ return SECTION_MAP['articles']
@property
def pretty_pubdate(self):
@@ -87,6 +164,11 @@ def pretty_body_text(self):
# that already contains <p> tags
_body = linebreaks(_body)
return _body
+
+ @property
+ def safe_summary(self):
+ '''suitable for use in places that must avoid nested anchor tags'''
+ return removetags(self.summary, 'a')
@property
def merged_tag_list(self):
View
7 source/articles/static/articles/js/jquery.localScroll.min.js
@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) 2007-2010 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
+ * Dual licensed under MIT and GPL.
+ * @author Ariel Flesler
+ * @version 1.2.8b
+ */
+;(function($){var g=location.href.replace(/#.*/,'');var h=$.localScroll=function(a){$('body').localScroll(a)};h.defaults={duration:1000,axis:'y',event:'click',stop:true,target:window,reset:true};h.hash=function(a){if(location.hash){a=$.extend({},h.defaults,a);a.hash=false;if(a.reset){var d=a.duration;delete a.duration;$(a.target).scrollTo(0,a);a.duration=d}scroll(0,location,a)}};$.fn.localScroll=function(b){b=$.extend({},h.defaults,b);return b.lazy?this.bind(b.event,function(e){var a=$([e.target,e.target.parentNode]).filter(filter)[0];if(a)scroll(e,a,b)}):this.find('a,area').filter(filter).bind(b.event,function(e){scroll(e,this,b)}).end().end();function filter(){return!!this.href&&!!this.hash&&this.href.replace(this.hash,'')==g&&(!b.filter||$(this).is(b.filter))}};function scroll(e,a,b){var c=a.hash.slice(1),elem=document.getElementById(c)||document.getElementsByName(c)[0];if(!elem)return;if(e)e.preventDefault();var d=$(b.target);if(b.lock&&d.is(':animated')||b.onBefore&&b.onBefore(e,elem,d)===false)return;if(b.stop)d._scrollable().stop(true);if(b.hash){var f=elem.id==c?'id':'name',$a=$('<a> </a>').attr(f,c).css({position:'absolute',top:$(window).scrollTop(),left:$(window).scrollLeft()});elem[f]='';$('body').prepend($a);location=a.hash;$a.remove();elem[f]=c}d.scrollTo(elem,b).trigger('notify.serialScroll',[elem])}})(jQuery);
View
7 source/articles/static/articles/js/jquery.scrollTo.min.js
@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) 2007-2012 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
+ * Dual licensed under MIT and GPL.
+ * @author Ariel Flesler
+ * @version 1.4.5 BETA
+ */
+;(function($){var h=$.scrollTo=function(a,b,c){$(window).scrollTo(a,b,c)};h.defaults={axis:'xy',duration:parseFloat($.fn.jquery)>=1.3?0:1,limit:true};h.window=function(a){return $(window)._scrollable()};$.fn._scrollable=function(){return this.map(function(){var a=this,isWin=!a.nodeName||$.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!isWin)return a;var b=(a.contentWindow||a).document||a.ownerDocument||a;return/webkit/i.test(navigator.userAgent)||b.compatMode=='BackCompat'?b.body:b.documentElement})};$.fn.scrollTo=function(e,f,g){if(typeof f=='object'){g=f;f=0}if(typeof g=='function')g={onAfter:g};if(e=='max')e=9e9;g=$.extend({},h.defaults,g);f=f||g.duration;g.queue=g.queue&&g.axis.length>1;if(g.queue)f/=2;g.offset=both(g.offset);g.over=both(g.over);return this._scrollable().each(function(){if(e==null)return;var d=this,$elem=$(d),targ=e,toff,attr={},win=$elem.is('html,body');switch(typeof targ){case'number':case'string':if(/^([+-]=?)?\d+(\.\d+)?(px|%)?$/.test(targ)){targ=both(targ);break}targ=$(targ,this);if(!targ.length)return;case'object':if(targ.is||targ.style)toff=(targ=$(targ)).offset()}$.each(g.axis.split(''),function(i,a){var b=a=='x'?'Left':'Top',pos=b.toLowerCase(),key='scroll'+b,old=d[key],max=h.max(d,a);if(toff){attr[key]=toff[pos]+(win?0:old-$elem.offset()[pos]);if(g.margin){attr[key]-=parseInt(targ.css('margin'+b))||0;attr[key]-=parseInt(targ.css('border'+b+'Width'))||0}attr[key]+=g.offset[pos]||0;if(g.over[pos])attr[key]+=targ[a=='x'?'width':'height']()*g.over[pos]}else{var c=targ[pos];attr[key]=c.slice&&c.slice(-1)=='%'?parseFloat(c)/100*max:c}if(g.limit&&/^\d+$/.test(attr[key]))attr[key]=attr[key]<=0?0:Math.min(attr[key],max);if(!i&&g.queue){if(old!=attr[key])animate(g.onAfterFirst);delete attr[key]}});animate(g.onAfter);function animate(a){$elem.animate(attr,f,g.easing,a&&function(){a.call(this,e,g)})}}).end()};h.max=function(a,b){var c=b=='x'?'Width':'Height',scroll='scroll'+c;if(!$(a).is('html,body'))return a[scroll]-$(a)[c.toLowerCase()]();var d='client'+c,html=a.ownerDocument.documentElement,body=a.ownerDocument.body;return Math.max(html[scroll],body[scroll])-Math.min(html[d],body[d])};function both(a){return typeof a=='object'?a:{top:a,left:a}}})(jQuery);
View
1  source/articles/templates/articles/_article_list_section_overline.html
@@ -0,0 +1 @@
+<h1 class="maintopic"><a href="{{ url('article_list_by_section', section.slug) }}">{{ section.name }}</a>{% if category %} / <span class="category">{{ category }}</span>{% endif %}{% if tags %} / <span class="category">{% for tag in tags %}{{ tag.name }}{% if not loop.last %}, {% endif %}{% endfor %}</span>{% endif %}</h1>
View
8 source/articles/templates/articles/_base_articles.html
@@ -1,3 +1,7 @@
{% extends "base.html" %}
-{% set active_nav = "features" %}
-{% block page_title %}Features{% if tags %} tagged: {% for tag in tags %}{{ tag.name }}{% if not loop.last %}, {% endif %}{% endfor %}{% endif %} - {{ super() }}{% endblock %}
+
+{% if section %}
+ {% set active_nav = section.slug %}
+{% endif %}
+
+{% block page_title %}{% if section %}{{ section.name }}{% else %}Features{% endif %}{% if tags %} tagged: {% for tag in tags %}{{ tag.name }}{% if not loop.last %}, {% endif %}{% endfor %}{% endif %} - {{ super() }}{% endblock %}
View
36 source/articles/templates/articles/article_detail.html
@@ -3,9 +3,21 @@
{% block page_title %} {{ article.title }} - {{ super() }}{% endblock %}
{% block site_js_extra %}
-<script src="{{ static('articles/js/jquery.syntaxhighlighter.min.js') }}"></script>
+{% compress js %}
+ {% if article.section.slug == "learning" %}
+ <script src="{{ static('articles/js/jquery.scrollTo.min.js') }}"></script>
+ <script src="{{ static('articles/js/jquery.localScroll.min.js') }}"></script>
+ {% endif %}
+ <script src="{{ static('articles/js/jquery.syntaxhighlighter.min.js') }}"></script>
+{% endcompress %}
<script type="text/javascript">
$.SyntaxHighlighter.init();
+ {% if article.section.slug == "learning" %}
+ $('.article-toc').localScroll({
+ offset: -80,
+ hash: true
+ });
+ {% endif %}
</script>
{% endblock %}
@@ -23,6 +35,17 @@
<aside>
<div class="byline">
+ {% if article.section.slug == "learning" %}
+ <div class="article-toc">
+ {% for article_block in article.articleblock_set.all() %}
+ {% if loop.first %}<h3 class="subhead list-header">In this case study</h3>
+ <ul class="toc">{% 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 %}
+
<h6 class="date">{{ article.pretty_pubdate }}</h6>
{% if article.get_live_author_set().exists() %}<h6 class="byline-name">{% with author_list = article.get_live_author_set() %}{% include "articles/_article_author_list.html" %}{% endwith %}</h6>{% endif %}
<ul class="link-list">
@@ -30,17 +53,6 @@ <h6 class="date">{{ article.pretty_pubdate }}</h6>
</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
2  source/articles/templates/articles/article_list.html
@@ -2,7 +2,7 @@
{% block content %}
{% if section %}
-<h1 class="maintopic"><a href="{{ url('article_list_by_section', section.slug) }}">{{ section.name }}</a>{% if category %} / <span class="category">{{ category }}</span>{% endif %}{% if tags %} / <span class="category">{% for tag in tags %}{{ tag.name }}{% if not loop.last %}, {% endif %}{% endfor %}</span>{% endif %}</h1>
+ {% include "articles/_article_list_section_overline.html" %}
{% endif %}
{% for article in page.object_list %}
View
63 source/articles/templates/articles/article_list_learning.html
@@ -0,0 +1,63 @@
+{% extends "articles/_base_articles.html" %}
+
+{% block article_class %}learning{% endblock %}
+
+{% block content %}
+{% if section %}
+ {% include "articles/_article_list_section_overline.html" %}
+{% endif %}
+
+<p class="intro">In-depth case studies by the best journalist-developers in the field. <span class="tinyhide">Get schooled on how journo-coders find and build web-native stories, what kinds of questions they ask of data, how choices in presenting the news affects how it’s interpreted, and the ethics they encounter along the way.</span></p>
+
+{% if lead_promo %}
+ {% with article=lead_promo %}
+ <a class="page-block lead promo" href="{{ article.get_absolute_url() }}">
+ {% if article.image %}<img src="{{ MEDIA_URL }}{{ thumbnail(article.image, "525x400", crop="center") }}" alt="{{ article.title }}">{% endif %}
+ <h2>
+ <span class="category">{% if article.article_type %}{{ article.get_article_type_display() }}{% endif %}{% if article.tags.all().exists() %} <span class="tags">/ {% for tag in article.tags.all() %}{{ tag }}{% if not loop.last %} {% endif %}{% endfor %}</span>{% endif %}</span>
+ {{ article.title }}
+ </h2>
+ <p>{{ article.safe_summary|safe }}</p>
+ </a>
+ {% endwith %}
+
+ <div class="secondary-promo-wrap">
+ {# two secondary promo items go in here #}
+ {% for article in secondary_promos %}
+ <a class="page-block promo secondary-promo{% if not loop.first %} second-promo{% endif %}" href="{{ article.get_absolute_url() }}">
+ {% if article.image %}<img src="{{ MEDIA_URL }}{{ thumbnail(article.image, "200x90", crop="center") }}" alt="{{ article.title }}">{% endif %}
+ <h2>
+ <span class="category">{% if article.article_type %}{{ article.get_article_type_display() }}{% endif %}{% if article.tags.all().exists() %} <span class="tags">/ {% for tag in article.tags.all() %}{{ tag }}{% if not loop.last %} {% endif %}{% endfor %}</span>{% endif %}</span>
+ {{ article.title }}
+ </h2>
+ <p>{{ article.safe_summary|safe }}</p>
+ </a>
+ {% endfor %}
+ </div>
+{% endif %}
+
+{% if lead_promo and page.object_list|length > 3 %}
+ <p class="intro"><b>More case studies:</b></p>
+{% endif %}
+
+{% for article in page.object_list %}
+ {# if we have promos on this page, don't include those articles in list #}
+ {% if article.pk not in articles_to_exclude_from_list %}
+ {% include "articles/_article_list_item.html" %}
+ {% endif %}
+{% endfor %}
+
+{% include "utils/_paginate.html" %}
+{% endblock content %}
+
+{% block site_js_extra %}
+<script type="text/javascript">
+var disqus_shortname = 'source-opennews';
+(function () {
+ var s = document.createElement('script'); s.async = true;
+ s.type = 'text/javascript';
+ s.src = '{{ HTTP_PROTOCOL }}://' + disqus_shortname + '.disqus.com/count.js';
+ (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s);
+}());
+</script>
+{% endblock %}
View
8 source/articles/urls.py
@@ -2,7 +2,7 @@
from django.views.decorators.cache import cache_page
from django.views.generic.simple import redirect_to
-from .views import ArticleList, ArticleDetail
+from .views import ArticleList
from source.base.feeds import ArticleFeed
@@ -32,10 +32,4 @@
kwargs = {'url': '/articles/'},
name = 'article_list_tags',
),
- url(
- regex = '^(?P<slug>[-\w]+)/$',
- view = ArticleDetail.as_view(),
- kwargs = {},
- name = 'article_detail',
- ),
)
View
128 source/articles/views.py
@@ -1,84 +1,37 @@
from django.core.urlresolvers import reverse
+from django.http import Http404, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.views.generic import ListView, DetailView
-from .models import Article
+from .models import Article, SECTION_MAP, CATEGORY_MAP
from source.base.utils import paginate
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
-SECTION_MAP = {
- 'articles': {
- 'name': 'Features',
- '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
-# in case we split out into multiple sections that need parent categories
-CATEGORY_MAP = {
- 'project': {
- 'name': 'Project',
- 'parent_name': 'Features',
- 'parent_slug': 'articles',
- },
- 'tool': {
- 'name': 'Tool',
- 'parent_name': 'Features',
- 'parent_slug': 'articles',
- },
- 'how-to': {
- 'name': 'How-to',
- 'parent_name': 'Features',
- 'parent_slug': 'articles',
- },
- 'interview': {
- 'name': 'Interview',
- 'parent_name': 'Features',
- 'parent_slug': 'articles',
- },
- 'roundtable': {
- 'name': 'Roundtable',
- 'parent_name': 'Features',
- 'parent_slug': 'articles',
- },
- 'roundup': {
- 'name': 'Roundup',
- 'parent_name': 'Features',
- 'parent_slug': 'articles',
- },
- 'event': {
- 'name': 'Event',
- 'parent_name': 'Features',
- 'parent_slug': 'articles',
- },
- 'update': {
- 'name': 'Update',
- 'parent_name': 'Features',
- 'parent_slug': 'articles',
- },
- 'learning': {
- 'name': 'Learning',
- 'parent_name': 'Learning',
- 'parent_slug': 'learning',
- },
-}
-
class ArticleList(ListView):
model = Article
-
+ template_name = 'articles/article_list.html'
+
def dispatch(self, *args, **kwargs):
self.section = kwargs.get('section', None)
self.category = kwargs.get('category', None)
self.tag_slugs = kwargs.get('tag_slugs', None)
self.tags = []
+
+ if self.category == 'learning' and not self.section:
+ # redirecting this to our "Section" page for Learning
+ # until we refactor Sections into database models
+ return HttpResponseRedirect(reverse('article_list_by_section', args=('learning',)))
+
+ if self.section:
+ # check for template override based on section name
+ self.template_list = [
+ 'articles/article_list_%s.html' % self.section,
+ self.template_name,
+ ]
+
+ if self.section == 'learning':
+ self.template_name = 'articles/article_list_learning.html'
+
return super(ArticleList, self).dispatch(*args, **kwargs)
def get_queryset(self):
@@ -130,9 +83,36 @@ def paginate_list(self, context):
return ''
+ def get_promo_items(self, context, num_items):
+ _page = context.get('page', None)
+
+ # Only get promo items for the first page of a paginated section
+ if _page and _page.number == 1:
+ '''
+ Take the most recent three items from this section's list
+ of articles. Pop the first item for a `lead_promo` object.
+ Stash the rest in a `secondary_promos` list. Also generate
+ a set of pks to ignore when we iterate through the main
+ headline list.
+ '''
+ promo_list = self.get_queryset()[:num_items]
+ lead_promo = None
+ secondary_promos = None
+ if promo_list:
+ lead_promo = promo_list[0]
+ secondary_promos = promo_list[1:]
+
+ context.update({
+ 'lead_promo': lead_promo,
+ 'secondary_promos': secondary_promos,
+ 'articles_to_exclude_from_list': [promo.pk for promo in promo_list]
+ })
+
def get_standard_context(self, context):
self.get_section_links(context)
self.paginate_list(context)
+ if self.section and SECTION_MAP[self.section]['gets_promo_items']:
+ self.get_promo_items(context, 3)
return ''
@@ -145,6 +125,7 @@ def get_context_data(self, **kwargs):
class ArticleDetail(DetailView):
model = Article
+ template_name = 'articles/article_detail.html'
def get_queryset(self):
if self.request.user.is_staff:
@@ -160,3 +141,16 @@ def get_queryset(self):
return queryset
+ def get_context_data(self, **kwargs):
+ context = super(ArticleDetail, self).get_context_data(**kwargs)
+
+ # make sure `section` kwarg matches this article's section
+ if self.kwargs['section'] != self.object.section['slug']:
+ raise Http404
+
+ context.update({
+ 'section': self.object.section,
+ })
+
+ return context
+
View
18 source/base/feeds.py
@@ -1,11 +1,11 @@
+from django.conf import settings
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.articles.models import Article, 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
@@ -54,7 +54,12 @@ def description(self, obj):
return "Recent articles %s" % identifier
def item_title(self, item):
- return item.title
+ _title = item.title
+ # Alert anyone using an RSS feed on staging
+ if settings.DEBUG:
+ _title = "THIS IS A TEST ARTICLE ON THE STAGING SITE: " + _title
+
+ return _title
def item_pubdate(self, item):
return item.pubdate
@@ -98,7 +103,12 @@ def description(self, obj):
return "Recent code index pages%s" % identifier
def item_title(self, item):
- return item.name
+ _name = item.name
+ # Alert anyone using an RSS feed on staging
+ if settings.DEBUG:
+ _name = "THIS IS A TEST ENTRY ON THE STAGING SITE: " + _name
+
+ return _name
def item_description(self, item):
return item.description
View
201 source/base/static/base/css/app.css
@@ -45,7 +45,7 @@ ul.errorlist li {
padding-bottom: 4px; border-bottom: 1px solid #ddd; margin-bottom: 18px;
}
-.link-list {
+.link-list, .toc {
list-style: none;
margin: 0;
padding: 0;
@@ -187,6 +187,10 @@ p, li p {
font-family: Georgia;
}
+p {
+ margin-bottom: 1em;
+}
+
article {
margin-right: auto;
margin-left: auto;
@@ -243,7 +247,7 @@ article h3 {
margin-top: 1em;
}
-h4 {
+h4, .toc li {
font-family: 'Open Sans', Helvetica, sans-serif;
font-weight: normal;
font-size: .9em;
@@ -795,6 +799,112 @@ pre.prettyprint {
width: auto;
}
+
+/* Learning section */
+
+.article-toc {
+ margin-bottom: 2em;
+}
+
+.learning p.intro {
+ font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
+ font-size: 16px;
+ margin-bottom: 25px;
+ clear: both;
+}
+
+a.promo {
+ display: block;
+}
+
+a.promo:hover {
+ text-decoration: none;
+}
+
+a.promo span.category {
+ color: #fc6e1f;
+}
+
+a.promo span.tags {
+ color: #bebebe;
+}
+
+a.promo span.tags:before {
+ content: "";
+}
+
+a.promo p {
+ color: #333;
+ margin-bottom: .25em;
+}
+
+.promo.lead {
+ width: 525px;
+ height: 580px;
+ border: 1px solid #ccc;
+ float: left;
+ margin-right: 25px;
+ border-bottom-left-radius: 5px;
+ border-bottom-right-radius: 5px;
+ padding: 10px;
+ box-shadow: 0px 2px 2px #ccc;
+
+}
+
+.promo span.byline, .promo b, .promo strong {
+ font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
+ font-weight: bold;
+}
+
+.promo img {
+ margin-bottom: 10px;
+}
+
+.promo.lead p {
+ font-size: .8em;
+ line-height: 1.4em;
+}
+
+.promo.lead span.category {
+ margin-bottom: 20px;
+}
+
+.secondary-promo-wrap {
+ width: 205px;
+ height: auto;
+ float: left;
+}
+
+.promo.secondary-promo {
+ font-size: .8em;
+ width: 95%;
+ height: auto;
+ border: 1px solid #ccc;
+ border-bottom-left-radius: 5px;
+ border-bottom-right-radius: 5px;
+ padding: 10px;
+ box-shadow: 0px 2px 2px #ccc;
+ margin-bottom: 25px;
+}
+
+.promo.secondary-promo h2 {
+ font-size: .8em;
+ margin-bottom: 10px;
+}
+
+.promo.secondary-promo h2 span.category {
+ font-size: .7em;
+}
+
+.promo.secondary-promo p {
+ font-size: .75em;
+ line-height: 1.3em;
+}
+
+.promo.secondary-promo img {
+ width: 100%;
+}
+
@media screen and (max-width: 840px) {
h2 span.category {
line-height: 1.3em;
@@ -853,10 +963,62 @@ pre.prettyprint {
.grid-box {
width: 46%;
}
+
+ .promo.lead {
+ width: 65%;
+ margin-right: 10px;
+ height: auto;
+ }
+
+ .promo.lead img {
+ width: 100%;
+ }
+
+ p.intro {
+ font-size: .7em;
+ }
+
+ .secondary-promo-wrap {
+ width: 30%;
+ }
+
+ .promo.secondary-promo {
+ margin-right: 0px;
+ margin-bottom: 10px;
+ }
+}
+
+@media screen and (max-width: 690px) {
+ .promo.lead {
+ width: 94%;
+ margin-right: 0;
+ height: auto;
+ }
+
+ .promo.lead img {
+ width: 100%;
+ }
+
+ .secondary-promo-wrap {
+ width: 97%;
+ margin-right: 0;
+ height: auto;
+ }
+
+ .promo.secondary-promo {
+ float: left;
+ width: 45%;
+ height: auto;
+ }
+
+ .promo.second-promo {
+ float: right;
+ }
}
+
@media screen and (max-width: 640px) {
- aside ul.link-list li {
+ aside ul.link-list li, aside ul.toc li {
font-size: 1em;
}
@@ -953,7 +1115,7 @@ pre.prettyprint {
line-height: 1.2em;
}
- ul.link-list {
+ ul.link-list, ul.toc {
font-size: 0.6em;
}
@@ -981,6 +1143,37 @@ pre.prettyprint {
}
}
+@media screen and (max-width: 525px) {
+
+ .secondary-promo-wrap {
+ width: 94%;
+ height: auto;
+ }
+
+ .promo.secondary-promo {
+ width: 100%;
+ margin-right: 20px;
+ margin-bottom: 1.5em;
+ }
+
+ .promo.second-promo {
+ float: left;
+ }
+
+ span.tinyhide {
+ display: none;
+ }
+
+ ul#navigation {
+ margin-left: -10px;
+ margin-right: -10px;
+ }
+ #navigation li a, #navigation li a:hover, #navigation li.active a {
+ padding-right: 4px;
+ padding-left: 4px;
+ }
+}
+
@media screen and (max-width: 480px) {
h6.date, h6.byline-name {
font-size: .8em;
View
5 source/base/templates/base.html
@@ -56,7 +56,8 @@
</form>
{% set navigation_links = [
- ('/articles/', 'features', 'Features'),
+ ('/articles/', 'articles', 'Features'),
+ ('/learning/', 'learning', 'Learn<span class="tinyhide">ing</span>'),
('/people/', 'community', 'Community'),
('/code/', 'code', 'Code'),
('/search/', 'search', 'Search'),
@@ -64,7 +65,7 @@
{% set active_nav = active_nav %}
<ul id="navigation">
{% for url, id, name in navigation_links %}
- <li{% if id == active_nav %} class="active"{% endif %} id="nav-{{ id }}"><a href="{{ url|e }}">{{ name|e }}</a></li>
+ <li{% if id == active_nav %} class="active"{% endif %} id="nav-{{ id }}"><a href="{{ url|e }}">{{ name|safe|e }}</a></li>
{% endfor %}
</ul>
View
14 source/base/urls.py
@@ -6,8 +6,10 @@
from haystack.forms import SearchForm
from haystack.query import SearchQuerySet
from haystack.views import search_view_factory
-from source.articles.views import ArticleList, CATEGORY_MAP, SECTION_MAP
+from source.articles.models import CATEGORY_MAP, SECTION_MAP
+from source.articles.views import ArticleList, ArticleDetail
+article_section_options = "|".join(SECTION_MAP.keys())
article_category_options = "|".join(CATEGORY_MAP.keys())
urlpatterns = patterns('',
@@ -25,18 +27,24 @@
),
# matching /articles/ here to offer future support for multiple sections
url(
- regex = '^(?P<section>articles|learning)/$',
+ regex = '^(?P<section>%s)/$' % article_section_options,
view = ArticleList.as_view(),
kwargs = {},
name = 'article_list_by_section',
),
url(
- regex = '^(?P<section>articles|learning)/rss/$',
+ regex = '^(?P<section>%s)/rss/$' % article_section_options,
view = cache_page(ArticleFeed(), 60*15),
kwargs = {},
name = 'article_list_by_section_feed',
),
url(
+ regex = '^(?P<section>%s)/(?P<slug>[-\w]+)/$' % article_section_options,
+ view = ArticleDetail.as_view(),
+ kwargs = {},
+ name = 'article_detail',
+ ),
+ url(
regex = '^category/(?P<category>%s)/$' % article_category_options,
view = ArticleList.as_view(),
kwargs = {},
Please sign in to comment.
Something went wrong with that request. Please try again.