Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'development'

  • Loading branch information...
commit e970ec0e1bbc2f077b7b1ea1d9f7ea8fe11f2da4 2 parents ba43fed + 949f2b1
@ryanpitts ryanpitts authored
Showing with 1,649 additions and 294 deletions.
  1. +10 −4 source/articles/models.py
  2. +1 −1  source/articles/templates/articles/_article_link_list.html
  3. +17 −5 source/articles/templates/articles/article_detail.html
  4. +26 −0 source/base/feeds.py
  5. +2 −2 source/base/helpers.py
  6. +352 −50 source/base/static/base/css/app.css
  7. BIN  source/base/static/base/img/source_retina_top_invert.png
  8. +69 −0 source/base/static/base/js/app.js
  9. +3 −0  source/base/static/base/js/libs/overthrow.min.js
  10. +11 −0 source/base/static/base/js/libs/snap.min.js
  11. +112 −49 source/base/static/base/js/listfilter.js
  12. +81 −60 source/base/templates/base.html
  13. +1 −1  source/base/templates/search/includes/_article_list_item.html
  14. +1 −1  source/base/templates/search/includes/_code_list_item.html
  15. +1 −1  source/base/templates/search/includes/_organization_list_item.html
  16. +1 −1  source/base/templates/search/includes/_person_list_item.html
  17. +2 −1  source/base/urls.py
  18. +1 −1  source/code/templates/code/_code_link_list.html
  19. 0  source/guides/__init__.py
  20. +34 −0 source/guides/admin.py
  21. +234 −0 source/guides/migrations/0001_initial.py
  22. +207 −0 source/guides/migrations/0002_auto__add_field_guide_cover_color.py
  23. 0  source/guides/migrations/__init__.py
  24. +128 −0 source/guides/models.py
  25. +3 −0  source/guides/templates/guides/_base_guides.html
  26. +15 −0 source/guides/templates/guides/_guide_article_list_item.html
  27. +5 −0 source/guides/templates/guides/_guide_link_list.html
  28. +5 −0 source/guides/templates/guides/_guide_list_item.html
  29. +26 −0 source/guides/templates/guides/guide_detail.html
  30. +16 −0 source/guides/templates/guides/guide_list.html
  31. +30 −0 source/guides/urls.py
  32. +39 −0 source/guides/views.py
  33. +1 −2  source/jobs/admin.py
  34. +8 −1 source/jobs/management/commands/tweet_new_jobs.py
  35. +40 −15 source/jobs/models.py
  36. +18 −0 source/jobs/templates/jobs/_job_list_as_grouped_list.html
  37. +3 −2 source/jobs/templates/jobs/_job_list_as_list.html
  38. +0 −18 source/jobs/templates/jobs/_job_list_as_table.html
  39. +62 −3 source/jobs/templates/jobs/job_list.html
  40. +28 −10 source/jobs/views.py
  41. +1 −1  source/people/templates/people/_organization_link_list.html
  42. +1 −1  source/people/templates/people/_person_link_list.html
  43. +48 −42 source/people/templates/people/organization_update.html
  44. +0 −19 source/people/templates/people/person_list.html
  45. +4 −2 source/people/utils.py
  46. +1 −1  source/people/views.py
  47. +1 −0  source/settings/base.py
View
14 source/articles/models.py
@@ -24,8 +24,8 @@ class Article(CachingMixin, models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
is_live = models.BooleanField('Display on site', default=True)
- show_in_lists = models.BooleanField(default=True)
- allow_comments = models.BooleanField(default=True)
+ show_in_lists = models.BooleanField('Show in lists', default=True)
+ allow_comments = models.BooleanField('Allow comments', default=True)
title = models.CharField(max_length=128)
slug = models.SlugField(unique=True)
pubdate = models.DateTimeField(default=datetime.now)
@@ -109,11 +109,13 @@ def get_live_people_set(self):
return self.people.filter(is_live=True)
def get_live_author_set(self):
- author_set = self.authors.filter(is_live=True)
- return author_set
+ return self.authors.filter(is_live=True)
def get_live_code_set(self):
return self.code.filter(is_live=True)
+
+ def get_live_guide_set(self):
+ return self.guidearticle_set.filter(guide__is_live=True, guide__show_in_lists=True, guide__pubdate__lte=datetime.now)
def get_live_author_bio_set(self):
# only authors with acutal bio information
@@ -204,6 +206,10 @@ def clear_caches_for_article(sender, instance, **kwargs):
# clear caches for related code index entries
for code in instance.get_live_code_set():
expire_page_cache(code.get_absolute_url())
+
+ # clear caches for related guides
+ for guide in instance.get_live_guide_set():
+ expire_page_cache(guide.get_absolute_url())
# clear caches for tag pages. FWIW this will miss
# tag intersection queries like /foo+bar+baz/, so if we
View
2  source/articles/templates/articles/_article_link_list.html
@@ -1,5 +1,5 @@
{% for article in article_link_list %}
{% if loop.first %}{% if not hide_link_list_title %}<h3 class="subhead list-header">{% if override_list_title %}{{ override_list_title|smartypants }} {% else %}Articles{% endif %}</h3>{% endif %}
<ul class="link-list">{% endif %}
- <li><i class="icon-file"></i><a href="{{ article.get_absolute_url() }}">{{ article.title|typogrify }}</a>{% if show_article_summary %}<p>{{ article.summary|typogrify }}</p>{% endif %}</li>
+ <li><i class="icon-file"></i> <a href="{{ article.get_absolute_url() }}">{{ article.title|typogrify }}</a>{% if show_article_summary %}<p>{{ article.summary|typogrify }}</p>{% endif %}</li>
{% if loop.last %}</ul>{% endif %}{% endfor %}
View
22 source/articles/templates/articles/article_detail.html
@@ -8,14 +8,13 @@
<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
+ hash: true,
+ target: '#snap-content-wrapper'
});
{% endif %}
</script>
@@ -46,10 +45,14 @@ <h4 class="subhead">{{ article.subhead|typogrify }}</h4>
</div>
{% endif %}
+ {% with guide_link_list = article.get_live_guide_set() %}
+ {% include "guides/_guide_link_list.html" %}{% endwith %}
+
<h6 class="date">{{ article.pretty_pubdate|typogrify }}</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">
- <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>
+ <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>
@@ -93,6 +96,15 @@ <h3 id="{{ articleblock.slug }}">{{ articleblock.title|typogrify }}</h3>
{% endif %}
</div>
{% endfor %}
+
+ {% if article.get_live_guide_set().exists() %}
+ <h5>
+ This article is part of a Guide:
+ {% for guide in article.get_live_guide_set() %}
+ <a href="{{ guide.get_absolute_url() }}">{{ guide.title }}</a>{% if not loop.last %}, {% endif %}
+ {% endfor %}
+ </h5>
+ {% endif %}
{# test for author bios in person db, add block if found #}
{% if article.get_live_author_bio_set().exists() %}
@@ -117,7 +129,7 @@ <h3 id="about-{{ author.slug }}">About <a href="{{ author.get_absolute_url() }}"
{% include "people/_organization_link_list.html" %}{% endwith %}
</aside>
- {% if article.allow_comments %}
+ {% if article.allow_comments and not settings.DEBUG %}
<div id="disqus_thread"></div>
<script type="text/javascript">
var disqus_shortname = "source-opennews";
View
26 source/base/feeds.py
@@ -7,6 +7,7 @@
from source.articles.models import Article, Section, Category
from source.code.models import Code
+from source.guides.models import Guide
from source.jobs.models import Job
from source.tags.models import TechnologyTag, ConceptTag
from source.tags.utils import get_validated_tag_list, get_tag_filtered_queryset
@@ -156,3 +157,28 @@ def items(self, obj):
queryset = Job.live_objects.order_by('-created')
return queryset[:20]
+class GuideFeed(Feed):
+ def title(self, obj):
+ return "Source: Guides"
+
+ def link(self, obj):
+ return reverse('guide_list')
+
+ def description(self, obj):
+ return 'Recent guides from Source'
+
+ def item_title(self, item):
+ _name = item.title
+ # 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.summary_or_description
+
+ def items(self, obj):
+ queryset = Guide.live_objects.order_by('-pubdate')
+ return queryset[:20]
+
View
4 source/base/helpers.py
@@ -100,12 +100,12 @@ def simple_timesince(value):
@register.filter
def simple_datesince(value):
- today = datetime.datetime.today().date()
+ today = datetime.datetime.now().date()
try:
difference = today - value
except:
return value
- if difference <= datetime.timedelta(days=1):
+ if difference < datetime.timedelta(days=1):
return 'today'
return '%(days)s ago' % {'days': timesince(value).split(', ')[0]}
View
402 source/base/static/base/css/app.css
@@ -1,6 +1,10 @@
/* Override bootstrap.css defaults here,
until all the styles are belong to us */
+*, *:before, *:after {
+ -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box;
+}
+
.container {
width: auto;
max-width: 800px;
@@ -124,7 +128,7 @@ aside .byline .link-list {
float: right;
font-family: 'Open Sans', sans-serif;
font-weight: 300;
- font-size: 16px;
+ font-size: 15px;
text-transform: uppercase;
width: auto;
text-shadow: 0px 1px 0px #f3f3f4;
@@ -145,8 +149,6 @@ aside .byline .link-list {
}
#navigation li a {
- border-right: 1px solid #e4e4e4;
- border-left: 1px solid #e4e4e4;
padding: 15px 10px;
padding-top: 16px;
color: #333333;
@@ -155,8 +157,6 @@ aside .byline .link-list {
}
#navigation li a:hover, #navigation li.active a {
- border-right: 1px solid #dfdfdf;
- border-left: 1px solid #dfdfdf;
background-color: #d8d8d8;
}
@@ -373,17 +373,30 @@ h6 a {
margin-bottom: .5em;
}
-#js-filter-form {
+#js-filter-form,
+#list-filter-form {
margin-bottom: 1em;
}
-#js-filter-form label {
+#list-filter-form,
+#sort-options {
+ display: inline;
+}
+
+#js-filter-form label,
+#list-filter-form label,
+#sort-options {
display: inline-block;
font-size: .67em;
vertical-align: middle;
}
+#filter-list-input-container #sort-options {
+ height: 20px;
+ margin-left: 1em;
+}
-#js-filter-form input {
+#js-filter-form input,
+#list-filter-form input {
vertical-align: text-top;
}
@@ -410,10 +423,15 @@ h6 a {
margin-right: 3%;
}
+.column-full {
+ width: 100%;
+ clear: both;
+}
+
.column-third {
float: left;
- width: 30%;
- margin-right: 3%;
+ width: 31%;
+ margin-right: 2%;
}
aside {
@@ -535,6 +553,12 @@ a:hover {
color: black;
}
+a.disabled {
+ color: #808080;
+ cursor: default;
+ pointer-events: none;
+}
+
h1.maintopic {
text-align: right;
width: 550px;
@@ -570,7 +594,7 @@ h1.maintopic {
}
.image-full-width-wrapper {
- width: 99%;
+ width: 100%;
margin-bottom: 1em;
}
@@ -677,9 +701,9 @@ img.list-thumbnail {
height: auto;
font-size: 16px;
}
-
+textarea, input[type="text"], input[type="password"], input[type="datetime"], input[type="datetime-local"], input[type="date"], input[type="month"], input[type="time"], input[type="week"], input[type="number"], input[type="email"], input[type="url"], input[type="search"], input[type="tel"], input[type="color"], .uneditable-input
.search-form input[type="text"] {
- width: 300px;
+ height: auto;
}
#topbar .search-form {
@@ -786,7 +810,7 @@ article.search-results h2 {
}
article.search-results h3 {
- margin-top: 0;
+ margin: 0;
}
article.search-results-single-col {
@@ -823,7 +847,7 @@ aside.search-results-single-col {
.grid-box {
display: inline-block;
vertical-align: top;
- width: 29%;
+ width: 30%;
margin-right: 3%;
zoom: 1;
*display: inline;
@@ -1046,6 +1070,15 @@ a.promo p {
font-size: 18px;
line-height: 1.3;
}
+.save-alert.error-message {
+ padding: .5em 1em;
+ margin: .5em 0;
+ display: block;
+ background-color: red;
+ color: #fff;
+ font-weight: bold;
+ -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px;
+}
.save-alert {
font-size: 14px;
@@ -1149,11 +1182,218 @@ a.promo p {
.job-listing .job-contact-block:before {
content: "| ";
}
+.offset-anchor {
+ display: block;
+ position: relative;
+ top: -75px;
+ visibility: hidden;
+}
+a.permalink {
+ text-decoration: none;
+ color: inherit;
+}
+a.permalink span,
+a.permalink i {
+ color: #808080;
+ display: none;
+}
+h3 a.permalink span,
+h3 a.permalink i {
+ font-size: .75em;
+ margin-left: .33em;
+}
+a.permalink:hover {
+ color: inherit;
+}
+a.permalink:hover span,
+a.permalink:hover i {
+ display: inherit;
+}
+
+#guide-header p {
+ font-size: 21px;
+}
+#guide-articles .notes {
+ margin-bottom: 0;
+}
.unbordered {
border: none !important;
}
+.snap-content {
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ width: auto;
+ height: auto;
+ z-index: 2;
+ overflow: auto;
+ background: #fff;
+ position: absolute;
+}
+.absolute {
+ position: absolute;
+}
+.hidden {
+ display: none;
+}
+.snap-drawers {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ width: auto;
+ height: auto;
+ z-index: 1;
+}
+.snap-drawer {
+ position: absolute;
+ top: 0;
+ right: auto;
+ bottom: 0;
+ left: auto;
+ width: 205px;
+ height: auto;
+ overflow: auto;
+ background: #212121;
+ color: #eee;
+ -webkit-overflow-scrolling: touch;
+ -webkit-transition: width 0.3s ease;
+ -moz-transition: width 0.3s ease;
+ -ms-transition: width 0.3s ease;
+ -o-transition: width 0.3s ease;
+ transition: width 0.3s ease;
+}
+.snap-drawer ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+.snap-drawer li {
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
+}
+.snap-drawer li a {
+ color: #d0d0d0;
+ font-family: 'Open Sans', sans-serif;
+ font-weight: 300;
+ text-shadow: 0px 1px 0px #111;
+ text-transform: uppercase;
+ display: block;
+ font-size: 16px;
+ padding: 15px 20px;
+}
+.snap-drawer .indent a {
+ padding-left: 35px;
+}
+.drawer-logo {
+ width: 160px;
+ padding: 20px;
+}
+.snap-drawer-left {
+ left: 0;
+ z-index: 1;
+}
+
+.snap-drawer-right {
+ right: 0;
+ z-index: 1;
+}
+
+.snapjs-left .snap-drawer-right,
+.snapjs-right .snap-drawer-left {
+ display: none;
+}
+
+.snapjs-expand-left .snap-drawer-left,
+.snapjs-expand-right .snap-drawer-right {
+ width: 100%;
+}
+
+.toggle-navigation {
+ display: none;
+ color: #111;
+ font-size: 24px;
+ float: right;
+ padding: 3px 0;
+}
+.toggle-navigation:hover {
+ text-decoration: none;
+}
+.toggle-navigation:focus {
+ outline: none;
+ text-decoration: none;
+ color: #111;
+}
+
+/* https://github.com/filamentgroup/Overthrow */
+.overthrow-enabled .overthrow {
+ overflow: auto;
+ -webkit-overflow-scrolling: touch;
+}
+
+.guides-box {
+ position: relative;
+ display: block;
+ background-color: #ff4e31;
+ height: 360px;
+}
+.guides-box-main {
+ height: auto;
+ max-height: 360px;
+ margin-bottom: 2em;
+}
+.guides-box .image-wrapper {
+ overflow: hidden;
+}
+.guides-box-main .image-wrapper {
+ float: right;
+ width: 50%;
+}
+.guides-box img {
+ border: none;
+ display: block;
+ width: 100%;
+}
+.guides-box h1,
+.guides-box h2 {
+ position: absolute;
+ font-size: 1.2em;
+ line-height: 1.1em;
+ color: #fff;
+ padding: .5em;
+ font-family: 'Open Sans', Helvetica, sans-serif;
+ font-weight: normal;
+ vertical-align: bottom;
+ margin-bottom: -.4em;
+ padding-bottom: 0;
+ text-transform: none;
+ background-color: #ff4e31;
+}
+.guides-box h2 {
+ bottom: 25px;
+}
+.guides-box h1 {
+ bottom: 30px;
+ font-size: 1.6em;
+ line-height: 1em;
+}
+.guides-box h1:before,
+.guides-box h2:before {
+ font-size: .6em;
+ content: 'the source guide to';
+ display: block;
+}
+.guides-box-main h1,
+.guides-box-main h2 {
+ max-width: 50%;
+}
+.guides-box.column-third {
+ margin-bottom: 2%;
+}
+
@media screen and (max-width: 840px) {
h2 span.category {
line-height: 1.3em;
@@ -1211,6 +1451,10 @@ a.promo p {
.grid-box {
width: 46%;
+ margin-bottom: 2em;
+ }
+ .grid-box .link-list {
+ margin-bottom: 0;
}
.promo.lead {
@@ -1235,6 +1479,22 @@ a.promo p {
margin-right: 0px;
margin-bottom: 10px;
}
+ .column-third {
+ width: 48%;
+ }
+ .guides-box .image-wrapper {
+ max-height: 60%;
+ }
+ #topbar .search-form {
+ display: none;
+ }
+ #navigation li#nav-search {
+ display: inline-block;
+ }
+}
+
+
+@media screen and (max-width: 730px) {
}
@media screen and (max-width: 690px) {
@@ -1249,14 +1509,14 @@ a.promo p {
}
.secondary-promo-wrap {
- width: 97%;
+ width: 100%;
margin-right: 0;
height: auto;
}
.promo.secondary-promo {
float: left;
- width: 45%;
+ width: 48%;
height: auto;
}
@@ -1264,20 +1524,28 @@ a.promo p {
float: right;
}
- #navigation li#nav-search {
- display: inline-block;
- }
-
+ #navigation li a,
#navigation li a:hover {
border: none;
}
- #topbar .search-form {
- display: none;
- }
.tt-query {
width: 100%;
}
+ #navigation {
+ font-size: 12px;
+ width: auto;
+ padding-top: 5px;
+ margin-left: -10px;
+ margin-right: -10px;
+ }
+
+ #navigation li a, #navigation li a:hover, #navigation li.active a {
+ font-weight: 400;
+ border: none;
+ padding: 10px 7px;
+ }
+
}
@@ -1312,30 +1580,15 @@ a.promo p {
margin-right: 10px;
}
- #navigation {
- float: none;
- font-size: 12px;
- width: auto;
- padding-top: 5px;
- margin-left: -10px;
- margin-right: -10px;
- }
-
- #navigation li a, #navigation li a:hover, #navigation li.active a {
- font-weight: 400;
- border: none;
- padding: 10px 7px;
- }
-
.col {
padding-left: 15px;
padding-right: 15px;
}
- .column-half,
- .column-third {
+ .column-half {
width: auto;
float: none;
+ margin-right: 0;
}
aside.homepage, aside.search-results {
@@ -1408,6 +1661,16 @@ a.promo p {
.image-inset-left-wrapper img, .image-inset-right-wrapper img {
width: 198px;
}
+ .guides-box .image-wrapper {
+ max-height: 67%;
+ }
+ #guide-header p {
+ font-size: 18px;
+ }
+ #filter-list-input-container #sort-options {
+ display: block;
+ margin-left: 0;
+ }
}
@media screen and (max-width: 525px) {
@@ -1430,11 +1693,6 @@ a.promo p {
display: none;
}
- #navigation li a, #navigation li a:hover, #navigation li.active a {
- padding-right: 4px;
- padding-left: 4px;
- }
-
.image-inset-left-wrapper,
.image-inset-right-wrapper {
float: none;
@@ -1445,7 +1703,20 @@ a.promo p {
img.map-inset {
display: none;
}
-
+ #navigation {
+ font-size: 11px;
+ }
+ .column-third {
+ width: auto;
+ float: none;
+ margin-right: 0;
+ }
+ .guides-box.column-third {
+ margin-bottom: 15px;
+ }
+ .guides-box h1 {
+ font-size: 1.4em;
+ }
}
@media screen and (max-width: 480px) {
@@ -1496,7 +1767,9 @@ a.promo p {
}
img.list-thumbnail {
- width: 100px;
+ float: none;
+ width: 100%;
+ margin-right: 0;
}
aside.homepage, aside.search-results {
@@ -1547,10 +1820,39 @@ a.promo p {
.job-listing .job-contact-block:before {
content: "";
}
-
-
.image-inset-left-wrapper img,
.image-inset-right-wrapper img {
width: 98%;
}
+ #navigation {
+ display: none;
+ }
+ #topbar .container {
+ height: auto;
+ }
+ .toggle-navigation {
+ display: inline;
+ }
+ .snap-content {
+ -webkit-overflow-scrolling: touch;
+ -webkit-transform: translate3d(0, 0, 0);
+ -moz-transform: translate3d(0, 0, 0);
+ -ms-transform: translate3d(0, 0, 0);
+ -o-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+ .guides-box-main {
+ margin-bottom: .75em;
+ }
+ .guides-box-main .img-wrapper,
+ .guides-box-main img {
+ display: none;
+ }
+ .guides-box-main h1 {
+ max-width: 100%;
+ position: relative;
+ }
+ #guide-header p {
+ font-size: 16px;
+ }
}
View
BIN  source/base/static/base/img/source_retina_top_invert.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
69 source/base/static/base/js/app.js
@@ -15,6 +15,75 @@ $(document).ready(function () {
});
});
+var gaTrackEvent = function(category, action, label) {
+ var _gaq = _gaq || [];
+ _gaq.push(['_trackEvent', category, action, label]);
+}
+
+// http://www.hnldesign.nl/work/code/debouncing-events-with-jquery/
+var jQueryDebounce = function($,cf,of, interval) {
+ // deBouncer by hnldesign.nl
+ // based on code by Paul Irish and the original debouncing function from John Hann
+ // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
+ var debounce = function (func, threshold, execAsap) {
+ var timeout;
+
+ return function debounced () {
+ var obj = this, args = arguments;
+ function delayed () {
+ if (!execAsap)
+ func.apply(obj, args);
+ timeout = null;
+ }
+ if (timeout)
+ clearTimeout(timeout);
+ else if (execAsap)
+ func.apply(obj, args);
+
+ timeout = setTimeout(delayed, threshold || interval);
+ };
+ };
+ jQuery.fn[cf] = function(fn){ return fn ? this.bind(of, debounce(fn)) : this.trigger(cf); };
+};
+
+// debounce the resize event, and apply nav pane if necessary
+jQueryDebounce(jQuery,'smartresize', 'resize', 100);
+$(window).smartresize(function(e) {
+ applyNavPane();
+})
+
+// snap.js nav pane
+var navPane = new Snap({
+ element: document.getElementById('snap-content-wrapper'),
+ disable: 'left',
+ slideIntent: 30,
+ minDragDistance: 20,
+ minPosition: -205
+});
+$('.toggle-navigation').on('click', function() {
+ if (navPane.state().state == 'right') {
+ navPane.close();
+ } else {
+ navPane.open('right');
+ }
+ return false;
+})
+
+// only enable the snap.js nav pane if appropriate for browser width
+var applyNavPane = function() {
+ window.browserWidth = document.documentElement.clientWidth;
+
+ if (browserWidth <= 480) {
+ navPane.enable();
+ $('.snap-drawers').removeClass('hidden')
+ } else {
+ navPane.disable();
+ $('.snap-drawers').addClass('hidden')
+ }
+}
+// initial page load
+applyNavPane();
+
// https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
jQuery(document).ajaxSend(function(event, xhr, settings) {
function getCookie(name) {
View
3  source/base/static/base/js/libs/overthrow.min.js
@@ -0,0 +1,3 @@
+/*! overthrow - An overflow:auto polyfill for responsive design. - v0.7.0 - 2014-02-21
+* Copyright (c) 2014 Scott Jehl, Filament Group, Inc.; Licensed MIT */
+!function(a){var b=a.document,c=b.documentElement,d="overthrow-enabled",e="ontouchmove"in b,f="WebkitOverflowScrolling"in c.style||"msOverflowStyle"in c.style||!e&&a.screen.width>800||function(){var b=a.navigator.userAgent,c=b.match(/AppleWebKit\/([0-9]+)/),d=c&&c[1],e=c&&d>=534;return b.match(/Android ([0-9]+)/)&&RegExp.$1>=3&&e||b.match(/ Version\/([0-9]+)/)&&RegExp.$1>=0&&a.blackberry&&e||b.indexOf("PlayBook")>-1&&e&&-1===!b.indexOf("Android 2")||b.match(/Firefox\/([0-9]+)/)&&RegExp.$1>=4||b.match(/wOSBrowser\/([0-9]+)/)&&RegExp.$1>=233&&e||b.match(/NokiaBrowser\/([0-9\.]+)/)&&7.3===parseFloat(RegExp.$1)&&c&&d>=533}();a.overthrow={},a.overthrow.enabledClassName=d,a.overthrow.addClass=function(){-1===c.className.indexOf(a.overthrow.enabledClassName)&&(c.className+=" "+a.overthrow.enabledClassName)},a.overthrow.removeClass=function(){c.className=c.className.replace(a.overthrow.enabledClassName,"")},a.overthrow.set=function(){f&&a.overthrow.addClass()},a.overthrow.canBeFilledWithPoly=e,a.overthrow.forget=function(){a.overthrow.removeClass()},a.overthrow.support=f?"native":"none"}(this),function(a,b,c){if(b!==c){b.easing=function(a,b,c,d){return c*((a=a/d-1)*a*a+1)+b},b.tossing=!1;var d;b.toss=function(a,e){b.intercept();var f,g,h=0,i=a.scrollLeft,j=a.scrollTop,k={top:"+0",left:"+0",duration:50,easing:b.easing,finished:function(){}},l=!1;if(e)for(var m in k)e[m]!==c&&(k[m]=e[m]);return"string"==typeof k.left?(k.left=parseFloat(k.left),f=k.left+i):(f=k.left,k.left=k.left-i),"string"==typeof k.top?(k.top=parseFloat(k.top),g=k.top+j):(g=k.top,k.top=k.top-j),b.tossing=!0,d=setInterval(function(){h++<k.duration?(a.scrollLeft=k.easing(h,i,k.left,k.duration),a.scrollTop=k.easing(h,j,k.top,k.duration)):(f!==a.scrollLeft?a.scrollLeft=f:(l&&k.finished(),l=!0),g!==a.scrollTop?a.scrollTop=g:(l&&k.finished(),l=!0),b.intercept())},1),{top:g,left:f,duration:b.duration,easing:b.easing}},b.intercept=function(){clearInterval(d),b.tossing=!1}}}(this,this.overthrow),function(a,b,c){if(b!==c){b.scrollIndicatorClassName="overthrow";var d=a.document,e=d.documentElement,f="native"===b.support,g=b.canBeFilledWithPoly,h=(b.configure,b.set),i=b.forget,j=b.scrollIndicatorClassName;b.closest=function(a,c){return!c&&a.className&&a.className.indexOf(j)>-1&&a||b.closest(a.parentNode)};var k=!1;b.set=function(){if(h(),!k&&!f&&g){a.overthrow.addClass(),k=!0,b.support="polyfilled",b.forget=function(){i(),k=!1,d.removeEventListener&&d.removeEventListener("touchstart",u,!1)};var j,l,m,n,o=[],p=[],q=function(){o=[],l=null},r=function(){p=[],m=null},s=function(a){n=j.querySelectorAll("textarea, input");for(var b=0,c=n.length;c>b;b++)n[b].style.pointerEvents=a},t=function(a,b){if(d.createEvent){var e,f=(!b||b===c)&&j.parentNode||j.touchchild||j;f!==j&&(e=d.createEvent("HTMLEvents"),e.initEvent("touchend",!0,!0),j.dispatchEvent(e),f.touchchild=j,j=f,f.dispatchEvent(a))}},u=function(a){if(b.intercept&&b.intercept(),q(),r(),j=b.closest(a.target),j&&j!==e&&!(a.touches.length>1)){s("none");var c=a,d=j.scrollTop,f=j.scrollLeft,g=j.offsetHeight,h=j.offsetWidth,i=a.touches[0].pageY,k=a.touches[0].pageX,n=j.scrollHeight,u=j.scrollWidth,v=function(a){var b=d+i-a.touches[0].pageY,e=f+k-a.touches[0].pageX,s=b>=(o.length?o[0]:0),v=e>=(p.length?p[0]:0);b>0&&n-g>b||e>0&&u-h>e?a.preventDefault():t(c),l&&s!==l&&q(),m&&v!==m&&r(),l=s,m=v,j.scrollTop=b,j.scrollLeft=e,o.unshift(b),p.unshift(e),o.length>3&&o.pop(),p.length>3&&p.pop()},w=function(){s("auto"),setTimeout(function(){s("none")},450),j.removeEventListener("touchmove",v,!1),j.removeEventListener("touchend",w,!1)};j.addEventListener("touchmove",v,!1),j.addEventListener("touchend",w,!1)}};d.addEventListener("touchstart",u,!1)}}}}(this,this.overthrow),function(a){a.overthrow.set()}(this);
View
11 source/base/static/base/js/libs/snap.min.js
@@ -0,0 +1,11 @@
+/*
+ * Snap.js
+ *
+ * Copyright 2013, Jacob Kelley - http://jakiestfu.com/
+ * Released under the MIT Licence
+ * http://opensource.org/licenses/MIT
+ *
+ * Github: http://github.com/jakiestfu/Snap.js/
+ * Version: 1.9.2
+ */
+ (function(c,b){var a=a||function(k){var f={element:null,dragger:null,disable:"none",addBodyClasses:true,hyperextensible:true,resistance:0.5,flickThreshold:50,transitionSpeed:0.3,easing:"ease",maxPosition:266,minPosition:-266,tapToClose:true,touchToDrag:true,slideIntent:40,minDragDistance:5},e={simpleStates:{opening:null,towards:null,hyperExtending:null,halfway:null,flick:null,translation:{absolute:0,relative:0,sinceDirectionChange:0,percentage:0}}},h={},d={hasTouch:(b.ontouchstart===null),eventType:function(m){var l={down:(d.hasTouch?"touchstart":"mousedown"),move:(d.hasTouch?"touchmove":"mousemove"),up:(d.hasTouch?"touchend":"mouseup"),out:(d.hasTouch?"touchcancel":"mouseout")};return l[m]},page:function(l,m){return(d.hasTouch&&m.touches.length&&m.touches[0])?m.touches[0]["page"+l]:m["page"+l]},klass:{has:function(m,l){return(m.className).indexOf(l)!==-1},add:function(m,l){if(!d.klass.has(m,l)&&f.addBodyClasses){m.className+=" "+l}},remove:function(m,l){if(f.addBodyClasses){m.className=(m.className).replace(l,"").replace(/^\s+|\s+$/g,"")}}},dispatchEvent:function(l){if(typeof h[l]==="function"){return h[l].call()}},vendor:function(){var m=b.createElement("div"),n="webkit Moz O ms".split(" "),l;for(l in n){if(typeof m.style[n[l]+"Transition"]!=="undefined"){return n[l]}}},transitionCallback:function(){return(e.vendor==="Moz"||e.vendor==="ms")?"transitionend":e.vendor+"TransitionEnd"},canTransform:function(){return typeof f.element.style[e.vendor+"Transform"]!=="undefined"},deepExtend:function(l,n){var m;for(m in n){if(n[m]&&n[m].constructor&&n[m].constructor===Object){l[m]=l[m]||{};d.deepExtend(l[m],n[m])}else{l[m]=n[m]}}return l},angleOfDrag:function(l,o){var n,m;m=Math.atan2(-(e.startDragY-o),(e.startDragX-l));if(m<0){m+=2*Math.PI}n=Math.floor(m*(180/Math.PI)-180);if(n<0&&n>-180){n=360-Math.abs(n)}return Math.abs(n)},events:{addEvent:function g(m,l,n){if(m.addEventListener){return m.addEventListener(l,n,false)}else{if(m.attachEvent){return m.attachEvent("on"+l,n)}}},removeEvent:function g(m,l,n){if(m.addEventListener){return m.removeEventListener(l,n,false)}else{if(m.attachEvent){return m.detachEvent("on"+l,n)}}},prevent:function(l){if(l.preventDefault){l.preventDefault()}else{l.returnValue=false}}},parentUntil:function(n,l){var m=typeof l==="string";while(n.parentNode){if(m&&n.getAttribute&&n.getAttribute(l)){return n}else{if(!m&&n===l){return n}}n=n.parentNode}return null}},i={translate:{get:{matrix:function(n){if(!d.canTransform()){return parseInt(f.element.style.left,10)}else{var m=c.getComputedStyle(f.element)[e.vendor+"Transform"].match(/\((.*)\)/),l=8;if(m){m=m[1].split(",");if(m.length===16){n+=l}return parseInt(m[n],10)}return 0}}},easeCallback:function(){f.element.style[e.vendor+"Transition"]="";e.translation=i.translate.get.matrix(4);e.easing=false;clearInterval(e.animatingInterval);if(e.easingTo===0){d.klass.remove(b.body,"snapjs-right");d.klass.remove(b.body,"snapjs-left")}d.dispatchEvent("animated");d.events.removeEvent(f.element,d.transitionCallback(),i.translate.easeCallback)},easeTo:function(l){if(!d.canTransform()){e.translation=l;i.translate.x(l)}else{e.easing=true;e.easingTo=l;f.element.style[e.vendor+"Transition"]="all "+f.transitionSpeed+"s "+f.easing;e.animatingInterval=setInterval(function(){d.dispatchEvent("animating")},1);d.events.addEvent(f.element,d.transitionCallback(),i.translate.easeCallback);i.translate.x(l)}if(l===0){f.element.style[e.vendor+"Transform"]=""}},x:function(m){if((f.disable==="left"&&m>0)||(f.disable==="right"&&m<0)){return}if(!f.hyperextensible){if(m===f.maxPosition||m>f.maxPosition){m=f.maxPosition}else{if(m===f.minPosition||m<f.minPosition){m=f.minPosition}}}m=parseInt(m,10);if(isNaN(m)){m=0}if(d.canTransform()){var l="translate3d("+m+"px, 0,0)";f.element.style[e.vendor+"Transform"]=l}else{f.element.style.width=(c.innerWidth||b.documentElement.clientWidth)+"px";f.element.style.left=m+"px";f.element.style.right=""}}},drag:{listen:function(){e.translation=0;e.easing=false;d.events.addEvent(f.element,d.eventType("down"),i.drag.startDrag);d.events.addEvent(f.element,d.eventType("move"),i.drag.dragging);d.events.addEvent(f.element,d.eventType("up"),i.drag.endDrag)},stopListening:function(){d.events.removeEvent(f.element,d.eventType("down"),i.drag.startDrag);d.events.removeEvent(f.element,d.eventType("move"),i.drag.dragging);d.events.removeEvent(f.element,d.eventType("up"),i.drag.endDrag)},startDrag:function(n){var m=n.target?n.target:n.srcElement,l=d.parentUntil(m,"data-snap-ignore");if(l){d.dispatchEvent("ignore");return}if(f.dragger){var o=d.parentUntil(m,f.dragger);if(!o&&(e.translation!==f.minPosition&&e.translation!==f.maxPosition)){return}}d.dispatchEvent("start");f.element.style[e.vendor+"Transition"]="";e.isDragging=true;e.hasIntent=null;e.intentChecked=false;e.startDragX=d.page("X",n);e.startDragY=d.page("Y",n);e.dragWatchers={current:0,last:0,hold:0,state:""};e.simpleStates={opening:null,towards:null,hyperExtending:null,halfway:null,flick:null,translation:{absolute:0,relative:0,sinceDirectionChange:0,percentage:0}}},dragging:function(s){if(e.isDragging&&f.touchToDrag){var v=d.page("X",s),u=d.page("Y",s),t=e.translation,o=i.translate.get.matrix(4),n=v-e.startDragX,p=o>0,q=n,w;if((e.intentChecked&&!e.hasIntent)){return}if(f.addBodyClasses){if((o)>0){d.klass.add(b.body,"snapjs-left");d.klass.remove(b.body,"snapjs-right")}else{if((o)<0){d.klass.add(b.body,"snapjs-right");d.klass.remove(b.body,"snapjs-left")}}}if(e.hasIntent===false||e.hasIntent===null){var m=d.angleOfDrag(v,u),l=(m>=0&&m<=f.slideIntent)||(m<=360&&m>(360-f.slideIntent)),r=(m>=180&&m<=(180+f.slideIntent))||(m<=180&&m>=(180-f.slideIntent));if(!r&&!l){e.hasIntent=false}else{e.hasIntent=true}e.intentChecked=true}if((f.minDragDistance>=Math.abs(v-e.startDragX))||(e.hasIntent===false)){return}d.events.prevent(s);d.dispatchEvent("drag");e.dragWatchers.current=v;if(e.dragWatchers.last>v){if(e.dragWatchers.state!=="left"){e.dragWatchers.state="left";e.dragWatchers.hold=v}e.dragWatchers.last=v}else{if(e.dragWatchers.last<v){if(e.dragWatchers.state!=="right"){e.dragWatchers.state="right";e.dragWatchers.hold=v}e.dragWatchers.last=v}}if(p){if(f.maxPosition<o){w=(o-f.maxPosition)*f.resistance;q=n-w}e.simpleStates={opening:"left",towards:e.dragWatchers.state,hyperExtending:f.maxPosition<o,halfway:o>(f.maxPosition/2),flick:Math.abs(e.dragWatchers.current-e.dragWatchers.hold)>f.flickThreshold,translation:{absolute:o,relative:n,sinceDirectionChange:(e.dragWatchers.current-e.dragWatchers.hold),percentage:(o/f.maxPosition)*100}}}else{if(f.minPosition>o){w=(o-f.minPosition)*f.resistance;q=n-w}e.simpleStates={opening:"right",towards:e.dragWatchers.state,hyperExtending:f.minPosition>o,halfway:o<(f.minPosition/2),flick:Math.abs(e.dragWatchers.current-e.dragWatchers.hold)>f.flickThreshold,translation:{absolute:o,relative:n,sinceDirectionChange:(e.dragWatchers.current-e.dragWatchers.hold),percentage:(o/f.minPosition)*100}}}i.translate.x(q+t)}},endDrag:function(m){if(e.isDragging){d.dispatchEvent("end");var l=i.translate.get.matrix(4);if(e.dragWatchers.current===0&&l!==0&&f.tapToClose){d.dispatchEvent("close");d.events.prevent(m);i.translate.easeTo(0);e.isDragging=false;e.startDragX=0;return}if(e.simpleStates.opening==="left"){if((e.simpleStates.halfway||e.simpleStates.hyperExtending||e.simpleStates.flick)){if(e.simpleStates.flick&&e.simpleStates.towards==="left"){i.translate.easeTo(0)}else{if((e.simpleStates.flick&&e.simpleStates.towards==="right")||(e.simpleStates.halfway||e.simpleStates.hyperExtending)){i.translate.easeTo(f.maxPosition)}}}else{i.translate.easeTo(0)}}else{if(e.simpleStates.opening==="right"){if((e.simpleStates.halfway||e.simpleStates.hyperExtending||e.simpleStates.flick)){if(e.simpleStates.flick&&e.simpleStates.towards==="right"){i.translate.easeTo(0)}else{if((e.simpleStates.flick&&e.simpleStates.towards==="left")||(e.simpleStates.halfway||e.simpleStates.hyperExtending)){i.translate.easeTo(f.minPosition)}}}else{i.translate.easeTo(0)}}}e.isDragging=false;e.startDragX=d.page("X",m)}}}},j=function(l){if(l.element){d.deepExtend(f,l);e.vendor=d.vendor();i.drag.listen()}};this.open=function(l){d.dispatchEvent("open");d.klass.remove(b.body,"snapjs-expand-left");d.klass.remove(b.body,"snapjs-expand-right");if(l==="left"){e.simpleStates.opening="left";e.simpleStates.towards="right";d.klass.add(b.body,"snapjs-left");d.klass.remove(b.body,"snapjs-right");i.translate.easeTo(f.maxPosition)}else{if(l==="right"){e.simpleStates.opening="right";e.simpleStates.towards="left";d.klass.remove(b.body,"snapjs-left");d.klass.add(b.body,"snapjs-right");i.translate.easeTo(f.minPosition)}}};this.close=function(){d.dispatchEvent("close");i.translate.easeTo(0)};this.expand=function(l){var m=c.innerWidth||b.documentElement.clientWidth;if(l==="left"){d.dispatchEvent("expandLeft");d.klass.add(b.body,"snapjs-expand-left");d.klass.remove(b.body,"snapjs-expand-right")}else{d.dispatchEvent("expandRight");d.klass.add(b.body,"snapjs-expand-right");d.klass.remove(b.body,"snapjs-expand-left");m*=-1}i.translate.easeTo(m)};this.on=function(l,m){h[l]=m;return this};this.off=function(l){if(h[l]){h[l]=false}};this.enable=function(){d.dispatchEvent("enable");i.drag.listen()};this.disable=function(){d.dispatchEvent("disable");i.drag.stopListening()};this.settings=function(l){d.deepExtend(f,l)};this.state=function(){var l,m=i.translate.get.matrix(4);if(m===f.maxPosition){l="left"}else{if(m===f.minPosition){l="right"}else{l="closed"}}return{state:l,info:e.simpleStates}};j(k)};if((typeof module!=="undefined")&&module.exports){module.exports=a}if(typeof ender==="undefined"){this.Snap=a}if((typeof define==="function")&&define.amd){define("snap",[],function(){return a})}}).call(this,window,document);
View
161 source/base/static/base/js/listfilter.js
@@ -1,54 +1,117 @@
-// custom jQuery filter selector `icontains` for text matching
-// http://answers.oreilly.com/topic/1055-creating-a-custom-filter-selector-with-jquery/
-$.expr[':'].icontains = function(element, index, match) {
- return (element.textContent || element.innerText || "").toUpperCase().indexOf(match[3].toUpperCase()) >= 0;
-};
-
-$(document).ready(function() {
- // set up initial vars
- var filterForm = '<div id="js-filter-form">\
- <label for="list-filter">Start typing to filter list</label>\
- <input class="filter" type="text" id="list-filter" />\
- </div>';
- var filteredList = $('#filterable-list');
-
- // insert filter form because we know we have js
- $(filterForm).insertBefore(filteredList);
-
- // after each keystroke in #list-filter input, do a case-insensitive
- // search against all the `li` elements inside #filterable-list
- $('#list-filter').change(function() {
- var filterVal = $(this).val();
- if (filterVal) {
- // hide the list container to avoid potential repaints
- filteredList.css('display','none');
- // hide items that don't have matching text, lists that don't have
- // visible items, and blocks that don't have visible lists
- filteredList.find('li:not(:icontains(' + filterVal + '))').css('display','none');
- filteredList.find('.filter-list:not(:has(li:visible))').css('display','none');
- filteredList.find('.filter-block:not(:has(li:visible))').css('display','none');
- // show blocks/lists/items that contain matching text
- filteredList.find('.filter-block:has(li:icontains(' + filterVal + '))').css('display','block');
- filteredList.find('.filter-list:has(li:icontains(' + filterVal + '))').css('display','block');
- filteredList.find('li:icontains(' + filterVal + ')').css('display','block');
- // show the list container again
- filteredList.css('display','block');
+function ListFilter(options) {
+ var filter = filter || {};
+
+ filter.init = function(options) {
+ // required args
+ filter.listContainer = options.listContainer;
+ filter.filterItemClass = options.filterItemClass;
+
+ // optional args
+ filter.inputContainer = options.inputContainer || null;
+ filter.inputPrompt = options.inputPrompt || 'Search list';
+ filter.inputClass = options.inputClass || '';
+ filter.filterBlockClass = options.filterBlockClass || null;
+
+ // built here
+ filter.filteredList = $(filter.listContainer);
+ filter.filterFormID = 'list-filter-form';
+ filter.filterFormInputID = 'list-filter-input';
+
+ filter.filterForm = '<div id="'+filter.filterFormID+'">\
+ <label for="'+filter.filterFormInputID+'">'+filter.inputPrompt+'</label>\
+ <input class="'+filter.inputClass+'" type="text" id="'+filter.filterFormInputID+'" />\
+ </div>';
+
+ filter.make();
+ return filter;
+ }
+
+ filter.make = function() {
+ filter.addFilterForm();
+ filter.addMatcher();
+
+ return filter;
+ }
+
+ filter.addFilterForm = function() {
+ // add filterForm to the inputContainer if present,
+ // otherwise insert onto page before listContainer
+ if (!!filter.inputContainer) {
+ $(filter.inputContainer).prepend(filter.filterForm);
} else {
- // nothing in filter form, so make sure everything is visible
- filteredList.find('.filter-block').css('display','block');
- filteredList.find('.filter-list').css('display','block');
- filteredList.find('li').css('display','block');
+ $(filter.filterForm).insertBefore(filter.listContainer);
}
+ }
+
+ filter.addMatcher = function() {
+ // after each keystroke in filterForm input, do a case-insensitive
+ // search against all filterItemClass elements inside listContainer
+ $('#'+filter.filterFormInputID).change(function() {
+ filter.filterValue = $(this).val();
+
+ if (!!filter.filterValue) {
+ filter.match();
+ } else {
+ filter.showAllItems();
+ }
+
+ filter.checkNoResults();
+
+ return false;
+ }).keyup(function() {
+ $(this).change();
+ });
+ }
+
+ filter.match = function() {
+ // hide the list container to avoid potential repaints
+ filter.filteredList.css('display','none');
+
+ // perform the matching
+ filter.hideUnmatchedItems();
+ filter.showMatchedItems();
+ // show the list container again
+ filter.filteredList.css('display','block');
+ }
+
+ filter.hideUnmatchedItems = function() {
+ // hide items that don't have matching text
+ filter.filteredList.find(filter.filterItemClass+':not(:icontains(' + filter.filterValue + '))').css('display','none');
+ if (!!filter.filterBlockClass) {
+ filter.filteredList.find(filter.filterBlockClass+':not(:has('+filter.filterItemClass+':visible))').css('display','none');
+ }
+ }
+
+ filter.showMatchedItems = function() {
+ // show items that contain matching text
+ if (!!filter.filterBlockClass) {
+ filter.filteredList.find(filter.filterBlockClass+':has('+filter.filterItemClass+':icontains(' + filter.filterValue + '))').css('display','block');
+ }
+ filter.filteredList.find(filter.filterItemClass+':icontains(' + filter.filterValue + ')').css('display','block');
+ }
+
+ filter.showAllItems = function () {
+ // nothing in filter form, so make sure everything is visible
+ filter.filteredList.find(filter.filterBlockClass).css('display','block');
+ filter.filteredList.find(filter.filterItemClass).css('display','block');
+ }
+
+ filter.checkNoResults = function() {
// show 'no results' message if we've removed all the items
- if ($('.filter-list > li:visible').length == 0) {
- $('#no-results').remove();
- $('#js-filter-form').after('<p id="no-results">No matching results found.</p>');
- } else {
- $('#no-results').remove();
+ $('#filter-no-results').remove();
+ if ($(filter.filterItemClass+':visible').length == 0) {
+ filter.filteredList.append('<p id="filter-no-results">No matching results found.</p>');
}
- return false;
- }).keyup(function() {
- $(this).change();
- });
-});
+ }
+
+ // ready, set, go
+ filter.init(options);
+ return filter;
+}
+
+// custom jQuery filter selector `icontains` for text matching
+// http://answers.oreilly.com/topic/1055-creating-a-custom-filter-selector-with-jquery/
+$.expr[':'].icontains = function(element, index, match) {
+ return (element.textContent || element.innerText || "").toUpperCase().indexOf(match[3].toUpperCase()) >= 0;
+};
View
141 source/base/templates/base.html
@@ -42,79 +42,100 @@
{% endif %}
{% block head_extra %}{% endblock %}
</head>
+
+ {% set navigation_links = [
+ ('/', 'home', 'Home'),
+ ('/articles/', 'articles', 'Features'),
+ ('/learning/', 'learning', 'Learning'),
+ ('/guides/', 'guides', 'Guides'),
+ ('/people/', 'community', 'Community'),
+ ('/code/', 'code', 'Code'),
+ ('/jobs/', 'jobs', 'Jobs'),
+ ('/search/', 'search', 'Search'),
+ ] -%}
+ {% set active_nav = active_nav %}
<body>
- <header id="topbar">
- {% if APP_MSG %}
- <div id="app_stage_box" class="whimper">
- <div class="busta">{{ APP_MSG|safe }}</div>
- </div>
- {% endif %}
- <div class="container">
- <form class="search-form" method="get" action="{{ url('haystack_search') }}">
- <input type="text" name="q" id="id_q" />
- <input class="btn" type="submit" value="Search">
- </form>
+ <div id="snap-content-wrapper" class="snap-content overthrow">
+ <header id="topbar">
+ {% if APP_MSG %}
+ <div id="app_stage_box" class="whimper">
+ <div class="busta">{{ APP_MSG|safe }}</div>
+ </div>
+ {% endif %}
+ <div class="container">
+ <form class="search-form" method="get" action="{{ url('haystack_search') }}">
+ <input type="text" name="q" id="id_q" />
+ <input class="btn" type="submit" value="Search">
+ </form>
- {% set navigation_links = [
- ('/articles/', 'articles', 'Features'),
- ('/learning/', 'learning', 'Learn<span class="tinyhide">ing</span>'),
- ('/people/', 'community', 'Community'),
- ('/code/', 'code', 'Code'),
- ('/search/', 'search', 'Search'),
- ] -%}
- {% 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|safe|e }}</a></li>
- {% endfor %}
- </ul>
+ <ul id="navigation">
+ {% for url, id, name in navigation_links %}
+ {% if id != "home" %}<li{% if id == active_nav %} class="active"{% endif %} id="nav-{{ id }}"><a href="{{ url|e }}">{{ name|safe|e }}</a></li>{% endif %}
+ {% endfor %}
+ </ul>
+ <a class="toggle-navigation" href="#"><i class="icon-reorder"></i></a>
- <a href="{{ url('homepage') }}"><img src="{{ static('base/img/source_retina_top.png') }}" alt="Source" class="sourcelogomin"></a>
- </div>
- </header>
- <div class="content">
- <div class="container page-block">
- <a href="{{ url('homepage') }}"><img id="main-logo" src="{{ static('base/img/source_retina.png') }}" alt="Source"></a>
- {% block base_tagline %}{% endblock %}
- </div>
+ <a href="{{ url('homepage') }}"><img src="{{ static('base/img/source_retina_top.png') }}" alt="Source" class="sourcelogomin"></a>
+ </div>
+ </header>
+ <div class="content">
+ <div class="container page-block">
+ <a href="{{ url('homepage') }}"><img id="main-logo" src="{{ static('base/img/source_retina.png') }}" alt="Source"></a>
+ {% block base_tagline %}{% endblock %}
+ </div>
- <div class="container">
- {% block base_above_article %}{% endblock %}
- <article class="{% block article_class %}{% endblock %}">{% block content %}{% endblock %}</article>
- {% block base_aside %}{% endblock %}
- {% block base_below_article %}{% endblock %}
+ <div class="container">
+ {% block base_above_article %}{% endblock %}
+ <article class="{% block article_class %}{% endblock %}">{% block content %}{% endblock %}</article>
+ {% block base_aside %}{% endblock %}
+ {% block base_below_article %}{% endblock %}
+ </div>
</div>
+ <footer class="container">{% block footer %}
+ <section class="external">
+ <div class="col">
+ <h2>Connect with Source</h2>
+ <ul>
+ {% if rss_link %}<li><i class="icon-rss"></i><a type="application/rss+xml" href="{{ rss_link }}">RSS for this page</a></li>{% endif %}
+ {% if json_link %}<li><i class="icon-rss"></i><a type="application/json" href="{{ json_link }}">JSON for this page</a></li>{% endif %}
+ <li><i class="icon-envelope-alt"></i><a href="mailto:opennews@mozillafoundation.org">Contact us</a></li>
+ <li><a href="/contribute/">Contribute code and articles</a></li>
+ <li><a href="/subscribe/">Subscribe to our email update</a></li>
+ <li>Connect on Twitter <a href="http://twitter.com/source" class="twitter-follow-button" data-show-count="false">@source</a></li>
+ <li><a rel="license" href="http://creativecommons.org/licenses/by/3.0/">{% if not settings.DEBUG %}<img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/3.0/80x15.png" />{% endif %}</a> <a rel="license" href="http://creativecommons.org/licenses/by/3.0/"> CC Attribution 3.0</a>
+ </ul>
+ {% if not settings.DEBUG %}
+ <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
+ {% endif %}
+ </div>
+ <div class="col">
+ <h2>About Source</h2>
+ <p>Source is a <a href="http://www.opennews.org">Knight-Mozilla OpenNews</a> project designed to amplify the impact of journalism code and the community of developers, designers, journalists, and editors who make it. Learn more <a href="/about/">about the project</a> or <a href="/contribute/">contribute your work</a>.</p>
+ </div>
+ <div class="col">
+ <h2>About OpenNews</h2>
+ <p>A multi-year partnership between <a href="http://www.mozilla.org/foundation/">Mozilla</a> and the <a href="http://www.knightfoundation.org/">Knight Foundation</a>, Knight-Mozilla OpenNews is dedicated to creating an ecosystem to help strengthen and build community around journalism on the web. More at <a href="http://opennews.org">opennews.org</a>.</p>
+ </div>
+ </section>
+ {% endblock footer %}</footer>
</div>
- <footer class="container">{% block footer %}
- <section class="external">
- <div class="col">
- <h2>Connect with Source</h2>
+ <div class="snap-drawers hidden">
+ <div class="snap-drawer snap-drawer-right">
+ <a href="/"><img class="drawer-logo" src="{{ static('base/img/source_retina_top_invert.png') }}" alt="Source"></a>
<ul>
- {% if rss_link %}<li><i class="icon-rss"></i><a type="application/rss+xml" href="{{ rss_link }}">RSS for this page</a></li>{% endif %}
- {% if json_link %}<li><i class="icon-rss"></i><a type="application/json" href="{{ json_link }}">JSON for this page</a></li>{% endif %}
- <li><i class="icon-envelope-alt"></i><a href="mailto:opennews@mozillafoundation.org">Contact us</a></li>
- <li><a href="/contribute/">Contribute code and articles</a></li>
- <li><a href="/subscribe/">Subscribe to our email update</a></li>
- <li>Connect on Twitter <a href="http://twitter.com/source" class="twitter-follow-button" data-show-count="false">@source</a></li>
- <li><a rel="license" href="http://creativecommons.org/licenses/by/3.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/3.0/80x15.png" /></a> <a rel="license" href="http://creativecommons.org/licenses/by/3.0/"> CC Attribution 3.0</a>
+ {% for url, id, name in navigation_links %}
+ <li class="{% if id not in "home|search" %}indent{% endif %}" id="nav-{{ id }}"><a href="{{ url|e }}">{{ name|safe|e }}</a></li>
+ {% endfor %}
</ul>
- <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
- </div>
- <div class="col">
- <h2>About Source</h2>
- <p>Source is a <a href="http://www.opennews.org">Knight-Mozilla OpenNews</a> project designed to amplify the impact of journalism code and the community of developers, designers, journalists, and editors who make it. Learn more <a href="/about/">about the project</a> or <a href="/contribute/">contribute your work</a>.</p>
</div>
- <div class="col">
- <h2>About OpenNews</h2>
- <p>A multi-year partnership between <a href="http://www.mozilla.org/foundation/">Mozilla</a> and the <a href="http://www.knightfoundation.org/">Knight Foundation</a>, Knight-Mozilla OpenNews is dedicated to creating an ecosystem to help strengthen and build community around journalism on the web. More at <a href="http://opennews.org">opennews.org</a>.</p>
- </div>
- </section>
- {% endblock footer %}</footer>
-
+ </div>
+
{% block site_js %}
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>window.jQuery || document.write("<script src=\"{{ static('base/js/libs/jquery-1.7.2.min.js') }}\"><\/script>")</script>
{% compress js %}
+ <script src="{{ static('base/js/libs/snap.min.js') }}"></script>
+ <script src="{{ static('base/js/libs/overthrow.min.js') }}"></script>
<script src="{{ static('base/js/app.js') }}"></script>
{% endcompress %}
{% endblock %}
View
2  source/base/templates/search/includes/_article_list_item.html
@@ -1,5 +1,5 @@
{# slim presentation block for an article item in search results #}
<div class="page-block">
<h3>{% if not hide_list_item_categories %}<span class="category">Article:</span> {% endif %}<a href="{{ object.get_absolute_url() }}">{{ object.title|typogrify }}</a></h3>
- <p>{{ object.summary|truncate(150)|typogrify|safe }}</p>
+ <p>{{ object.summary|typogrify|safe }}</p>
</div>
View
2  source/base/templates/search/includes/_code_list_item.html
@@ -1,5 +1,5 @@
{# slim presentation block for a code item in search results #}
<div class="page-block">
<h3>{% if not hide_list_item_categories %}<span class="category">Code:</span> {% endif %}<a href="{{ object.get_absolute_url() }}">{{ object.name|typogrify }}</a></h3>
- {% if object.description %}<p>{{ object.description|truncate(150)|typogrify|safe }}</p>{% endif %}
+ {% if object.summary_or_description %}<p>{{ object.summary_or_description|typogrify|safe }}</p>{% endif %}
</div>
View
2  source/base/templates/search/includes/_organization_list_item.html
@@ -1,5 +1,5 @@
{# slim presentation block for an organization item in search results #}
<div class="page-block">
<h3>{% if not hide_list_item_categories %}<span class="category">Organization:</span> {% endif %}<a href="{{ object.get_absolute_url() }}">{{ object.name|typogrify }}</a></h3>
- {% if object.description %}<p>{{ object.description|truncate(150)|typogrify|safe }}</p>{% endif %}
+ {% if object.description %}<p>{{ object.description|typogrify|safe }}</p>{% endif %}
</div>
View
2  source/base/templates/search/includes/_person_list_item.html
@@ -1,5 +1,5 @@
{# slim presentation block for a person item in search results #}
<div class="page-block">
<h3>{% if not hide_list_item_categories %}<span class="category">Person:</span> {% endif %}<a href="{{ object.get_absolute_url() }}">{{ object.name()|typogrify }}</a></h3>
- {% if object.description %}<p>{{ object.description|truncate(150)|typogrify|safe }}</p>{% endif %}
+ {% if object.description %}<p>{{ object.description|typogrify|safe }}</p>{% endif %}
</div>
View
3  source/base/urls.py
@@ -22,9 +22,10 @@
),
(r'^articles/', include('source.articles.urls')),
(r'^code/', include('source.code.urls')),
+ (r'^guides/', include('source.guides.urls')),
(r'^jobs/', include('source.jobs.urls')),
- (r'^people/', include('source.people.urls.people')),
(r'^organizations/', include('source.people.urls.organizations')),
+ (r'^people/', include('source.people.urls.people')),
url(
regex = '^search/$',
view = search_view_factory(view_class=SourceSearchView, form_class=SearchForm, searchqueryset=SearchQuerySet().order_by('django_ct')),
View
2  source/code/templates/code/_code_link_list.html
@@ -1,5 +1,5 @@
{% for code in code_link_list %}
{% if loop.first %}{% if not hide_link_list_title %}<h3 class="subhead list-header">Code</h3>{% endif %}
<ul class="link-list">{% endif %}
- <li><i class="icon-list-alt"></i><a href="{{ code.get_absolute_url() }}">{{ code.name|typogrify }}</a></li>
+ <li><i class="icon-list-alt"></i> <a href="{{ code.get_absolute_url() }}">{{ code.name|typogrify }}</a></li>
{% if loop.last %}</ul>{% endif %}{% endfor %}
View
0  source/guides/__init__.py
No changes.
View
34 source/guides/admin.py
@@ -0,0 +1,34 @@
+from django.contrib import admin
+
+from .models import Guide, GuideArticle
+from source.base.widgets import AdminImageMixin
+
+class GuideArticleInline(admin.StackedInline):
+ model = GuideArticle
+ extra = 1
+ raw_id_fields = ('article',)
+ fieldsets = (
+ ('', {'fields': ('article', 'order', 'article_notes')}),
+ )
+
+class GuideAdmin(AdminImageMixin, admin.ModelAdmin):
+ save_on_top = True
+ prepopulated_fields = {'slug': ('title',)}
+ list_filter = ('is_live', 'show_in_lists',)
+ search_fields = ('title', 'summary', 'description',)
+ fieldsets = (
+ ('', {'fields': (('pubdate', 'is_live', 'show_in_lists'), ('title', 'slug'), 'description', 'summary')}),
+ ('', {'fields': ('cover_color', 'image')}),
+ )
+ inlines = [GuideArticleInline,]
+
+ def formfield_for_dbfield(self, db_field, **kwargs):
+ # More usable heights and widths in admin form fields
+ field = super(GuideAdmin, self).formfield_for_dbfield(db_field, **kwargs)
+ if db_field.name in ['title','slug']:
+ field.widget.attrs['style'] = 'width: 30em;'
+ if db_field.name == 'summary':
+ field.widget.attrs['style'] = 'height: 4.5em;'
+ return field
+
+admin.site.register(Guide, GuideAdmin)
View
234 source/guides/migrations/0001_initial.py
@@ -0,0 +1,234 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding model 'Guide'
+ db.create_table('guides_guide', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+ ('modified', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
+ ('is_live', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('show_in_lists', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=50)),
+ ('pubdate', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
+ ('image', self.gf('sorl.thumbnail.fields.ImageField')(max_length=100, null=True, blank=True)),
+ ('image_caption', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('image_credit', self.gf('django.db.models.fields.CharField')(max_length=128, blank=True)),
+ ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('summary', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ))
+ db.send_create_signal('guides', ['Guide'])
+
+ # Adding model 'GuideArticle'
+ db.create_table('guides_guidearticle', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+ ('modified', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
+ ('guide', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['guides.Guide'])),
+ ('article', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['articles.Article'])),
+ ('order', self.gf('django.db.models.fields.PositiveIntegerField')(default=1, db_index=True, blank=True)),
+ ('article_notes', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ))
+ db.send_create_signal('guides', ['GuideArticle'])
+
+
+ def backwards(self, orm):
+ # Deleting model 'Guide'
+ db.delete_table('guides_guide')
+
+ # Deleting model 'GuideArticle'
+ db.delete_table('guides_guidearticle')
+
+
+ models = {
+ 'articles.article': {
+ 'Meta': {'ordering': "('-pubdate', 'title')", 'object_name': 'Article'},
+ 'allow_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'article_type': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'article_authors'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['people.Person']"}),
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['articles.Category']"}),
+ 'code': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['code.Code']", 'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'disable_auto_linebreaks': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'image_caption': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'image_credit': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
+ 'is_live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'organizations': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['people.Organization']", 'null': 'True', 'blank': 'True'}),
+ 'people': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['people.Person']", 'null': 'True', 'blank': 'True'}),
+ 'pubdate': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'show_in_lists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}),
+ 'subhead': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'summary': ('django.db.models.fields.TextField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'articles.category': {
+ 'Meta': {'ordering': "('section', 'name')", 'object_name': 'Category'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'section': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['articles.Section']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'})
+ },
+ 'articles.section': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Section'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'gets_promo_items': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
+ 'special_template': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
+ },
+ 'code.code': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'Code'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'organizations': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['people.Organization']", 'null': 'True', 'blank': 'True'}),
+ 'people': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['people.Person']", 'null': 'True', 'blank': 'True'}),
+ 'repo_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'repo_forks': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'repo_last_push': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'repo_master_branch': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+ 'repo_watchers': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'screenshot': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'seeking_contributors': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}),
+ 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'guides.guide': {
+ 'Meta': {'ordering': "('-pubdate', 'title')", 'object_name': 'Guide'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'image_caption': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'image_credit': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
+ 'is_live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'pubdate': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'show_in_lists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}),
+ 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'guides.guidearticle': {
+ 'Meta': {'ordering': "('guide', 'order')", 'object_name': 'GuideArticle'},
+ 'article': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['articles.Article']"}),
+ 'article_notes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'guide': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['guides.Guide']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'db_index': 'True', 'blank': 'True'})
+ },
+ 'people.organization': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Organization'},
+ 'address': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'city': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+ 'country': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'github_gists_num': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'github_repos_num': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'github_username': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+ 'homepage': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'logo': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'show_in_lists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+ 'twitter_username': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'})
+ },
+ 'people.person': {
+ 'Meta': {'ordering': "('last_name', 'first_name')", 'object_name': 'Person'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'github_gists_num': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'github_repos_num': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'github_username': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'organizations': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['people.Organization']", 'null': 'True', 'blank': 'True'}),
+ 'show_in_lists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}),
+ 'twitter_bio': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'twitter_profile_image_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+ 'twitter_username': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'})
+ },
+ 'taggit.tag': {
+ 'Meta': {'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'})
+ },
+ 'taggit.taggeditem': {
+ 'Meta': {'object_name': 'TaggedItem'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"})
+ },
+ 'tags.concepttag': {
+ 'Meta': {'object_name': 'ConceptTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'})
+ },
+ 'tags.concepttaggeditem': {
+ 'Meta': {'object_name': 'ConceptTaggedItem'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tags_concepttaggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tags_concepttaggeditem_concepttag_items'", 'to': "orm['tags.ConceptTag']"})
+ },
+ 'tags.technologytag': {
+ 'Meta': {'object_name': 'TechnologyTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'})
+ },
+ 'tags.technologytaggeditem': {
+ 'Meta': {'object_name': 'TechnologyTaggedItem'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tags_technologytaggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tags_technologytaggeditem_techtag_items'", 'to': "orm['tags.TechnologyTag']"})
+ }
+ }
+
+ complete_apps = ['guides']
View
207 source/guides/migrations/0002_auto__add_field_guide_cover_color.py
@@ -0,0 +1,207 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Guide.cover_color'
+ db.add_column('guides_guide', 'cover_color',
+ self.gf('django.db.models.fields.CharField')(default='', max_length='32', blank=True),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'Guide.cover_color'
+ db.delete_column('guides_guide', 'cover_color')
+
+
+ models = {
+ 'articles.article': {
+ 'Meta': {'ordering': "('-pubdate', 'title')", 'object_name': 'Article'},
+ 'allow_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'article_type': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'article_authors'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['people.Person']"}),
+ 'body': ('django.db.models.fields.TextField', [], {}),
+ 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['articles.Category']"}),
+ 'code': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['code.Code']", 'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'disable_auto_linebreaks': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'image_caption': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'image_credit': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
+ 'is_live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'organizations': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['people.Organization']", 'null': 'True', 'blank': 'True'}),
+ 'people': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['people.Person']", 'null': 'True', 'blank': 'True'}),
+ 'pubdate': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'show_in_lists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}),
+ 'subhead': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'summary': ('django.db.models.fields.TextField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'articles.category': {
+ 'Meta': {'ordering': "('section', 'name')", 'object_name': 'Category'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'section': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['articles.Section']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'})
+ },
+ 'articles.section': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Section'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'gets_promo_items': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
+ 'special_template': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
+ },
+ 'code.code': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'Code'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'organizations': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['people.Organization']", 'null': 'True', 'blank': 'True'}),
+ 'people': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['people.Person']", 'null': 'True', 'blank': 'True'}),
+ 'repo_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'repo_forks': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'repo_last_push': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'repo_master_branch': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+ 'repo_watchers': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'screenshot': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'seeking_contributors': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}),
+ 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'guides.guide': {
+ 'Meta': {'ordering': "('-pubdate', 'title')", 'object_name': 'Guide'},
+ 'cover_color': ('django.db.models.fields.CharField', [], {'max_length': "'32'", 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'image_caption': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'image_credit': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
+ 'is_live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'pubdate': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'show_in_lists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}),
+ 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'guides.guidearticle': {
+ 'Meta': {'ordering': "('guide', 'order')", 'object_name': 'GuideArticle'},
+ 'article': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['articles.Article']"}),
+ 'article_notes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'guide': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['guides.Guide']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'db_index': 'True', 'blank': 'True'})
+ },
+ 'people.organization': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Organization'},
+ 'address': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'city': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+ 'country': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'github_gists_num': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'github_repos_num': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'github_username': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+ 'homepage': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'logo': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'show_in_lists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+ 'twitter_username': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'})
+ },
+ 'people.person': {
+ 'Meta': {'ordering': "('last_name', 'first_name')", 'object_name': 'Person'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'github_gists_num': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'github_repos_num': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'github_username': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'organizations': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['people.Organization']", 'null': 'True', 'blank': 'True'}),
+ 'show_in_lists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}),
+ 'twitter_bio': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'twitter_profile_image_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+ 'twitter_username': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'})
+ },
+ 'taggit.tag': {
+ 'Meta': {'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'})
+ },
+ 'taggit.taggeditem': {
+ 'Meta': {'object_name': 'TaggedItem'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"})
+ },
+ 'tags.concepttag': {
+ 'Meta': {'object_name': 'ConceptTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'})
+ },
+ 'tags.concepttaggeditem': {
+ 'Meta': {'object_name': 'ConceptTaggedItem'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tags_concepttaggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tags_concepttaggeditem_concepttag_items'", 'to': "orm['tags.ConceptTag']"})
+ },
+ 'tags.technologytag': {
+ 'Meta': {'object_name': 'TechnologyTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'})
+ },
+ 'tags.technologytaggeditem': {
+ 'Meta': {'object_name': 'TechnologyTaggedItem'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tags_technologytaggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key':