Merge bkuhn master local branch with upstream
This commit is contained in:
commit
07faf5cebc
23 changed files with 343 additions and 152 deletions
|
@ -1,9 +1,12 @@
|
|||
import hashlib
|
||||
|
||||
from django.conf import settings
|
||||
from django.shortcuts import render_to_response
|
||||
from django.template import RequestContext
|
||||
|
||||
# This is backwards compatibilty support for a custom function we wrote
|
||||
# ourselves that is no longer necessary in modern Django.
|
||||
from django.shortcuts import render as render_template_with_context
|
||||
|
||||
class ParameterValidator(object):
|
||||
def __init__(self, given_hash_or_params, params_hash_key=None):
|
||||
if params_hash_key is None:
|
||||
|
@ -41,8 +44,3 @@ class ParameterValidator(object):
|
|||
|
||||
def fail(self):
|
||||
self.valid = False
|
||||
|
||||
|
||||
def render_template_with_context(request, template_path, context_dict):
|
||||
return render_to_response(template_path, context_dict,
|
||||
context_instance=RequestContext(request))
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from django.db import models
|
||||
from django.conf import settings
|
||||
from conservancy import bsoup
|
||||
from conservancy.apps.staff.models import Person
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
@ -18,7 +19,7 @@ class EntryTag(models.Model):
|
|||
def get_absolute_url(self):
|
||||
return u"/blog/?tag=%s" % self.slug
|
||||
|
||||
class Entry(models.Model):
|
||||
class Entry(models.Model, bsoup.SoupModelMixin):
|
||||
"""Blog entry"""
|
||||
|
||||
headline = models.CharField(max_length=200)
|
||||
|
@ -38,6 +39,8 @@ class Entry(models.Model):
|
|||
ordering = ('-pub_date',)
|
||||
get_latest_by = 'pub_date'
|
||||
|
||||
SOUP_ATTRS = ['body']
|
||||
|
||||
def __unicode__(self):
|
||||
return self.headline
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from django.conf.urls import patterns, url, include
|
||||
from django.conf.urls import url, include
|
||||
from conservancy.apps.blog.models import Entry, EntryTag # relative import
|
||||
from conservancy.apps.staff.models import Person
|
||||
from datetime import datetime
|
||||
from conservancy.apps.blog.views import last_name, BlogYearArchiveView, BlogMonthArchiveView, BlogDayArchiveView, BlogDateDetailView
|
||||
from conservancy.apps.blog.views import last_name, BlogYearArchiveView, BlogMonthArchiveView, BlogDayArchiveView, BlogDateDetailView, custom_index, query
|
||||
|
||||
extra_context = {}
|
||||
|
||||
|
@ -12,23 +12,14 @@ info_dict = {
|
|||
'extra_context': extra_context,
|
||||
}
|
||||
|
||||
# urlpatterns = patterns('django.views.generic.date_based',
|
||||
urlpatterns = patterns('',
|
||||
# (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/(?P<slug>[-\w]+)/$', 'object_detail', dict(info_dict, slug_field='slug')),
|
||||
# (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/$', 'archive_day', info_dict),
|
||||
# (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 'archive_month', info_dict),
|
||||
# (r'^(?P<year>\d{4})/$', 'archive_year', dict(info_dict,
|
||||
# make_object_list=True)),
|
||||
(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/(?P<slug>[-\w]+)/$', BlogDateDetailView.as_view(**info_dict)),
|
||||
(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/$', BlogDayArchiveView.as_view(**info_dict)),
|
||||
(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', BlogMonthArchiveView.as_view(**info_dict)),
|
||||
(r'^(?P<year>\d{4})/$', BlogYearArchiveView.as_view(**info_dict)),
|
||||
)
|
||||
|
||||
urlpatterns += patterns('conservancy.apps.blog.views',
|
||||
(r'^/?$', 'custom_index', dict(info_dict, paginate_by=4)),
|
||||
(r'^query/$', 'query'),
|
||||
)
|
||||
urlpatterns = [
|
||||
url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/(?P<slug>[-\w]+)/$', BlogDateDetailView.as_view(**info_dict)),
|
||||
url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/$', BlogDayArchiveView.as_view(**info_dict)),
|
||||
url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', BlogMonthArchiveView.as_view(**info_dict)),
|
||||
url(r'^(?P<year>\d{4})/$', BlogYearArchiveView.as_view(**info_dict)),
|
||||
url(r'^/?$', custom_index, dict(info_dict, paginate_by=4)),
|
||||
url(r'^query/$', query),
|
||||
]
|
||||
|
||||
# Code to display authors and tags on each blog page
|
||||
|
||||
|
|
|
@ -4,8 +4,7 @@ from django.views.generic import ListView
|
|||
from django.views.generic.dates import YearArchiveView, MonthArchiveView, DayArchiveView, DateDetailView
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from conservancy.apps.staff.models import Person
|
||||
from django.shortcuts import get_object_or_404, render_to_response
|
||||
from django.template import RequestContext
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from datetime import datetime
|
||||
|
||||
def OR_filter(field_name, objs):
|
||||
|
@ -65,7 +64,7 @@ def custom_index(request, queryset, *args, **kwargs):
|
|||
|
||||
extra_context['blog_entries'] = blog_entries
|
||||
|
||||
return render_to_response('blog/entry_list.html', extra_context, context_instance=RequestContext(request))
|
||||
return render(request, 'blog/entry_list.html', extra_context)
|
||||
|
||||
def techblog_redirect(request):
|
||||
"""Redirect from the old 'techblog' to the new blog
|
||||
|
@ -103,8 +102,7 @@ def query(request):
|
|||
entry__isnull=False).distinct(),
|
||||
key=last_name)
|
||||
tags = EntryTag.objects.all().order_by('label')
|
||||
return render_to_response('blog/query.html',
|
||||
{'authors': authors, 'tags': tags}, context_instance=RequestContext(request))
|
||||
return render(request, 'blog/query.html', {'authors': authors, 'tags': tags})
|
||||
|
||||
def relative_redirect(request, path):
|
||||
from django import http
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
from django.shortcuts import render_to_response
|
||||
from django.template import RequestContext
|
||||
from django.shortcuts import render
|
||||
from django import forms
|
||||
from conservancy.apps.contacts.models import ContactEntry
|
||||
from django.forms import ModelForm
|
||||
|
@ -18,10 +17,8 @@ def subscribe(request):
|
|||
form = ContactEntryForm(request.POST)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
return render_to_response('contacts/subscribe_success.html',
|
||||
{'form': form.cleaned_data}, context_instance=RequestContext(request))
|
||||
return render(request, 'contacts/subscribe_success.html', {'form': form.cleaned_data})
|
||||
else:
|
||||
form = ContactEntryForm()
|
||||
|
||||
return render_to_response('contacts/subscribe.html',
|
||||
{'form': form}, context_instance=RequestContext(request))
|
||||
return render(request, 'contacts/subscribe.html', {'form': form})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from django.conf.urls import patterns, url, include
|
||||
from django.conf.urls import url, include
|
||||
from conservancy.apps.contractpatch import views as cpatch_views
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
(r'', 'conservancy.apps.contractpatch.views.index'),
|
||||
)
|
||||
urlpatterns = [
|
||||
url(r'', cpatch_views.index),
|
||||
]
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# from django.views.generic.list_detail import object_list
|
||||
from django.shortcuts import render_to_response
|
||||
from django.template import RequestContext
|
||||
from django.shortcuts import render
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.template import loader
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
|
@ -21,7 +20,7 @@ def event_detail(request, year, slug, queryset, **kwargs):
|
|||
event = queryset.get(date__year=year, slug__exact=slug)
|
||||
except ObjectDoesNotExist:
|
||||
raise Http404, "Event does not exist"
|
||||
return render_to_response('events/event_detail.html', {'event': event}, context_instance=RequestContext(request))
|
||||
return render(request, 'events/event_detail.html', {'event': event})
|
||||
|
||||
def custom_index(request, queryset, *args, **kwargs):
|
||||
"""Scrollable index of future and past events, with date index.
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
from django.db import models
|
||||
from django.conf import settings
|
||||
from conservancy import bsoup
|
||||
from conservancy.apps.staff.models import Person
|
||||
from conservancy.apps.events.models import Event
|
||||
from django.contrib.sites.models import Site
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
class PressRelease(models.Model):
|
||||
class PressRelease(models.Model, bsoup.SoupModelMixin):
|
||||
"""News release model"""
|
||||
|
||||
headline = models.CharField(max_length=300)
|
||||
|
@ -24,6 +25,8 @@ class PressRelease(models.Model):
|
|||
ordering = ("-pub_date",)
|
||||
get_latest_by = "pub_date"
|
||||
|
||||
SOUP_ATTRS = ['summary', 'body']
|
||||
|
||||
def __unicode__(self):
|
||||
return self.headline
|
||||
|
||||
|
|
20
www/conservancy/apps/news/templatetags/fill_url.py
Normal file
20
www/conservancy/apps/news/templatetags/fill_url.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
import urlparse
|
||||
|
||||
from django import template
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@register.filter(name='fill_url')
|
||||
def fill_url(given_url, base_url):
|
||||
""""Fill out" missing pieces of one URL from another.
|
||||
|
||||
This function parses the given URL, and if it's missing any pieces
|
||||
(scheme, netloc, etc.), it fills those in from the base URL.
|
||||
Typical usage is "/URL/path"|fill_url:"https://hostname/"
|
||||
to generate "https://hostname/URL/path".
|
||||
"""
|
||||
given_parts = urlparse.urlsplit(given_url)
|
||||
base_parts = urlparse.urlsplit(base_url)
|
||||
return urlparse.urlunsplit(
|
||||
given_part or base_part for given_part, base_part in zip(given_parts, base_parts)
|
||||
)
|
|
@ -17,10 +17,10 @@
|
|||
# along with this program in a file in the toplevel directory called
|
||||
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.conf.urls import patterns, url, include
|
||||
from django.conf.urls import url, include
|
||||
from django.conf import settings
|
||||
from conservancy.apps.news.models import PressRelease, ExternalArticle
|
||||
from conservancy.apps.news.views import NewsYearArchiveView, NewsMonthArchiveView, NewsDayArchiveView, NewsDateDetailView
|
||||
from conservancy.apps.news.views import NewsYearArchiveView, NewsMonthArchiveView, NewsDayArchiveView, NewsDateDetailView, listing
|
||||
|
||||
info_dict = {
|
||||
'queryset': PressRelease.objects.all().filter(sites__id__exact=settings.SITE_ID),
|
||||
|
@ -31,18 +31,10 @@ external_article_dict = {
|
|||
'articles': ExternalArticle.objects.all()
|
||||
}
|
||||
|
||||
urlpatterns = patterns('',
|
||||
# (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/(?P<slug>[-\w]+)/$', 'conservancy.apps.news.views.object_detail', info_dict),
|
||||
(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/(?P<slug>[-\w]+)/$', NewsDateDetailView.as_view(**info_dict)),
|
||||
# (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/$', 'conservancy.apps.news.views.archive_day', info_dict),
|
||||
(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/$', NewsDayArchiveView.as_view(**info_dict)),
|
||||
# (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 'conservancy.apps.news.views.archive_month', info_dict),
|
||||
(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', NewsMonthArchiveView.as_view(**info_dict)),
|
||||
# (r'^(?P<year>\d{4})/$', 'conservancy.apps.news.views.archive_year',
|
||||
# dict(info_dict, make_object_list=True)),
|
||||
(r'^(?P<year>\d{4})/$', NewsYearArchiveView.as_view(**info_dict)),
|
||||
)
|
||||
|
||||
urlpatterns += patterns('',
|
||||
(r'^/?$', 'conservancy.apps.news.views.listing', dict(info_dict, paginate_by=6)),
|
||||
)
|
||||
urlpatterns = [
|
||||
url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/(?P<slug>[-\w]+)/$', NewsDateDetailView.as_view(**info_dict)),
|
||||
url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/$', NewsDayArchiveView.as_view(**info_dict)),
|
||||
url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', NewsMonthArchiveView.as_view(**info_dict)),
|
||||
url(r'^(?P<year>\d{4})/$', NewsYearArchiveView.as_view(**info_dict)),
|
||||
url(r'^/?$', listing, dict(info_dict, paginate_by=6)),
|
||||
]
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# from django.views.generic.list_detail import object_list
|
||||
from django.views.generic import ListView
|
||||
from django.template import RequestContext
|
||||
from django.shortcuts import render_to_response
|
||||
from django.shortcuts import render
|
||||
from django.views.generic.dates import YearArchiveView, MonthArchiveView, DayArchiveView, DateDetailView
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from conservancy.apps.news.models import ExternalArticle
|
||||
|
@ -42,7 +41,7 @@ def listing(request, *args, **kwargs):
|
|||
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||
news = paginator.page(paginator.num_pages)
|
||||
|
||||
return render_to_response('news/pressrelease_list.html', {"news": news, "date_list" : date_list}, context_instance=RequestContext(request))
|
||||
return render(request, 'news/pressrelease_list.html', {"news": news, "date_list" : date_list})
|
||||
|
||||
class NewsYearArchiveView(YearArchiveView):
|
||||
# queryset = Article.objects.all()
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
from django.shortcuts import render_to_response
|
||||
from django.template import RequestContext
|
||||
from django.shortcuts import render
|
||||
from django import forms
|
||||
from django.template import RequestContext
|
||||
from conervancy.apps.summit_registration.models import SummitRegistration
|
||||
|
||||
def register(request):
|
||||
|
@ -21,10 +19,8 @@ def register(request):
|
|||
form = SummitForm(request.POST)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
return render_to_response('summit_registration/register_success.html',
|
||||
{'form': form.cleaned_data}, context_instance=RequestContext(request))
|
||||
return render(reqeust, 'summit_registration/register_success.html', {'form': form.cleaned_data})
|
||||
else:
|
||||
form = SummitForm()
|
||||
|
||||
return render_to_response('summit_registration/register.html',
|
||||
{'form': form}, context_instance=RequestContext(request))
|
||||
return render(request, 'summit_registration/register.html', {'form': form})
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
from django.conf.urls import patterns
|
||||
from django.conf.urls import url
|
||||
from conservancy.apps.supporter import views as supp_views
|
||||
from conservancy.static import views as static_views
|
||||
|
||||
INDEX_VIEW = 'conservancy.apps.supporter.views.index'
|
||||
pattern_pairs = [(r'^/?$', INDEX_VIEW)]
|
||||
pattern_pairs.extend(
|
||||
(r'^{}(?:\.html|/|)$'.format(basename), INDEX_VIEW)
|
||||
INDEX_VIEW = supp_views.index
|
||||
urlpatterns = [url(r'^/?$', INDEX_VIEW)]
|
||||
urlpatterns.extend(
|
||||
url(r'^{}(?:\.html|/|)$'.format(basename), INDEX_VIEW)
|
||||
for basename in ['index', '2015-supporter-appeal', '2016-supporter-appeal']
|
||||
)
|
||||
pattern_pairs.append((r'', 'conservancy.static.views.index'))
|
||||
|
||||
urlpatterns = patterns('', *pattern_pairs)
|
||||
urlpatterns.append(url(r'', static_views.index))
|
||||
|
|
144
www/conservancy/bsoup.py
Normal file
144
www/conservancy/bsoup.py
Normal file
|
@ -0,0 +1,144 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import io
|
||||
import re
|
||||
|
||||
import bs4
|
||||
import bs4.element
|
||||
|
||||
class BeautifulSoup(bs4.BeautifulSoup):
|
||||
"""A wrapper of the original BeautifulSoup class, with convenience methods added."""
|
||||
|
||||
IMAGE_ATTRS = {
|
||||
'img': 'src',
|
||||
'video': 'poster',
|
||||
}
|
||||
NON_BODY_TEXT_TAGS = frozenset([
|
||||
'img',
|
||||
'video',
|
||||
])
|
||||
SENTENCE_END = re.compile(r'[.?!]\s*\W*\s*$')
|
||||
|
||||
def __init__(self, src, parser='html5lib'):
|
||||
# WARNING! It seems like it would be ideal to use the 'lxml' parser
|
||||
# for speed, but that doesn't work in our web application. On
|
||||
# Debian stretch, at least, using lxml causes the web server WSGI
|
||||
# application to go into an infinite loop.
|
||||
super(BeautifulSoup, self).__init__(src, parser)
|
||||
|
||||
def _body_text(self, root):
|
||||
# "Body text" is all the strings under the root element, in order,
|
||||
# except:
|
||||
# * strings inside NON_BODY_TEXT_TAGS
|
||||
# * strings inside containers of NON_BODY_TEXT_TAGS. A container is
|
||||
# an element that has a NON_BODY_TEXT_TAGS element as its first child.
|
||||
# For example, in <div> <video …> … </div>, none of the div's strings
|
||||
# are included in the body text, because it's considered to be a
|
||||
# <video> container, and any strings are probably a caption, fallback
|
||||
# text, or other non-body text.
|
||||
started = False
|
||||
for child in root.children:
|
||||
child_type = type(child)
|
||||
if issubclass(child_type, bs4.element.Tag):
|
||||
if child.name in self.NON_BODY_TEXT_TAGS:
|
||||
if not started:
|
||||
break
|
||||
else:
|
||||
for s in self._body_text(child):
|
||||
yield s
|
||||
# It's not worth it to use issubclass here, because elements that
|
||||
# don't have body text like Comments and CDATA are subclasses of
|
||||
# NavigableString.
|
||||
elif child_type is bs4.element.NavigableString:
|
||||
if started:
|
||||
yield child
|
||||
elif child.isspace():
|
||||
pass
|
||||
else:
|
||||
yield child
|
||||
started = True
|
||||
|
||||
def body_text(self):
|
||||
"""Return an iterator of strings comprising this document's body text."""
|
||||
return self._body_text(self)
|
||||
|
||||
def some_body_text(self, char_target=300):
|
||||
"""Return an iterator of strings with some of this document's body text.
|
||||
|
||||
This is the same as body_text, except after it yields a string that
|
||||
looks like the end of a sentence, it checks whether it has yielded
|
||||
at least `char_target` characters. If so, the iterator stops.
|
||||
"""
|
||||
# This implementation is likely to overshoot `char_target` a lot,
|
||||
# because it doesn't look inside the strings it yields, just at the
|
||||
# end of them. We can implement something smarter later if needed.
|
||||
char_count = 0
|
||||
for s in self.body_text():
|
||||
yield s
|
||||
char_count += len(s)
|
||||
if (char_count > char_target) and self.SENTENCE_END.search(s):
|
||||
break
|
||||
|
||||
@staticmethod
|
||||
def is_video_source(elem):
|
||||
try:
|
||||
return elem.name == 'source' and elem.parent.name == 'video'
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
def iter_attr(self, tag, attr_name, **kwargs):
|
||||
kwargs[attr_name] = True
|
||||
for elem in self.find_all(tag, **kwargs):
|
||||
yield elem[attr_name]
|
||||
|
||||
def iter_image_urls(self):
|
||||
"""Return an iterator of source URL strings of all images in this document.
|
||||
|
||||
Images include <img> tags and <video> poster attributes.
|
||||
"""
|
||||
for elem in self.find_all(list(self.IMAGE_ATTRS.keys())):
|
||||
try:
|
||||
yield elem[self.IMAGE_ATTRS[elem.name]]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def iter_video_urls(self):
|
||||
"""Return an iterator of source URL strings of all videos in this document."""
|
||||
return self.iter_attr(self.is_video_source, 'src')
|
||||
|
||||
|
||||
class SoupModelMixin:
|
||||
"""Mixin for models to parse HTML with BeautifulSoup.
|
||||
|
||||
Classes that use this mixin must define `SOUP_ATTRS`, a list of strings
|
||||
that name attributes with HTML in them. After that, all the public methods
|
||||
are usable.
|
||||
"""
|
||||
|
||||
SOUP_ATTRS = []
|
||||
|
||||
def _get_soup(self):
|
||||
try:
|
||||
return self._soup
|
||||
except AttributeError:
|
||||
html = io.StringIO()
|
||||
for attr_name in self.SOUP_ATTRS:
|
||||
html.write(getattr(self, attr_name))
|
||||
html.seek(0)
|
||||
self._soup = BeautifulSoup(html)
|
||||
return self._soup
|
||||
|
||||
def get_description(self):
|
||||
"""Return a string with a brief excerpt of body text from the HTML."""
|
||||
return u''.join(self._get_soup().some_body_text())
|
||||
|
||||
def get_image_urls(self):
|
||||
"""Return an iterator of source URL strings of all images in the HTML.
|
||||
|
||||
Images include <img> tags and <video> poster attributes.
|
||||
"""
|
||||
return self._get_soup().iter_image_urls()
|
||||
|
||||
def get_video_urls(self):
|
||||
"""Return an iterator of source URL strings of all videos in the HTML."""
|
||||
return self._get_soup().iter_video_urls()
|
|
@ -2,8 +2,7 @@ from django.contrib.syndication.views import Feed
|
|||
from django.utils.feedgenerator import Rss201rev2Feed
|
||||
from conservancy.apps.news.models import PressRelease
|
||||
from conservancy.apps.blog.models import Entry as BlogEntry
|
||||
from django.template import RequestContext
|
||||
from django.shortcuts import render_to_response
|
||||
from django.shortcuts import render
|
||||
from django.conf import settings
|
||||
from datetime import datetime
|
||||
|
||||
|
@ -255,4 +254,4 @@ def view(request):
|
|||
"""
|
||||
|
||||
feeds = (PressReleaseFeed, BlogFeed, OmnibusFeed)
|
||||
return render_to_response("feeds.html", {'feeds': feeds}, context_instance=RequestContext(request))
|
||||
return render(request, "feeds.html", {'feeds': feeds})
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import conservancy.settings
|
||||
from conservancy.apps.fundgoal.models import FundraisingGoal as FundraisingGoal
|
||||
|
||||
def fundgoal_lookup(fundraiser_sought):
|
||||
|
@ -9,3 +10,11 @@ def fundgoal_lookup(fundraiser_sought):
|
|||
|
||||
def sitefundraiser(request):
|
||||
return {'sitefundgoal': fundgoal_lookup('supporterrun') }
|
||||
|
||||
if conservancy.settings.FORCE_CANONICAL_HOSTNAME:
|
||||
_HOST_URL_VAR = {'host_url': 'https://' + conservancy.settings.FORCE_CANONICAL_HOSTNAME}
|
||||
def host_url(request):
|
||||
return _HOST_URL_VAR
|
||||
else:
|
||||
def host_url(request):
|
||||
return {'host_url': request.build_absolute_uri('/').rstrip('/')}
|
||||
|
|
|
@ -27,6 +27,8 @@ ROOT_URLCONF = 'conservancy.urls'
|
|||
FORCE_CANONICAL_HOSTNAME = False if DEBUG else 'sfconservancy.org'
|
||||
|
||||
ALLOWED_HOSTS = [ 'www.sfconservancy.org', 'aspen.sfconservancy.org', 'sfconservancy.org', u'104.130.70.210' ]
|
||||
if DEBUG:
|
||||
ALLOWED_HOSTS.append('localhost')
|
||||
|
||||
REDIRECT_TABLE = {
|
||||
'www.sf-conservancy.org': 'sfconservancy.org',
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
from django.shortcuts import render_to_response
|
||||
from django.template import RequestContext
|
||||
from django.shortcuts import render
|
||||
from conservancy.apps.supporters.models import Supporter as Supporter
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
@ -19,4 +18,4 @@ def view(request):
|
|||
'supporters_count' : supporters_count,
|
||||
'anonymous_count' : anonymous_count
|
||||
}
|
||||
return render_to_response("sponsors.html", c, context_instance=RequestContext(request))
|
||||
return render(request, "sponsors.html", c)
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
{% extends "base_blog.html" %}
|
||||
|
||||
{% block head %}
|
||||
{% include "opengraph_partial.html" with url=object.get_absolute_url title=object.headline description=object.get_description %}
|
||||
{% include "opengraph_urllist_partial.html" with property='image' urls=object.get_image_urls fallback='/img/conservancy-logo.png' %}
|
||||
{% include "opengraph_urllist_partial.html" with property='video' urls=object.get_video_urls %}
|
||||
{% endblock %}
|
||||
|
||||
{% block subtitle %}{{ object.headline|striptags|safe }} - Conservancy Blog - {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
{% extends "base_news.html" %}
|
||||
|
||||
{% block head %}
|
||||
{% include "opengraph_partial.html" with url=object.get_absolute_url title=object.headline description=object.get_description %}
|
||||
{% include "opengraph_urllist_partial.html" with property='image' urls=object.get_image_urls fallback='/img/conservancy-logo.png' %}
|
||||
{% include "opengraph_urllist_partial.html" with property='video' urls=object.get_video_urls %}
|
||||
{% endblock %}
|
||||
|
||||
{% block subtitle %}{{ object.headline|striptags|safe }} - {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
|
38
www/conservancy/templates/opengraph_partial.html
Normal file
38
www/conservancy/templates/opengraph_partial.html
Normal file
|
@ -0,0 +1,38 @@
|
|||
{% comment %}
|
||||
|
||||
Include this partial in a head section to include basic Open Graph metadata.
|
||||
Pass a variable `NAME` to give a value for the `og:NAME` property.
|
||||
|
||||
These properties are only listed if you give a value for them:
|
||||
|
||||
* url: A URL string that includes at least an absolute path. This partial
|
||||
will fill in a default scheme and host if needed.
|
||||
* title: A string. Tags are stripped, then the rest is assumed HTML-safe.
|
||||
* description: A string. Tags are stripped, then the rest is assumed
|
||||
HTML-safe.
|
||||
|
||||
These properties are always included. You can override them but you
|
||||
normally shouldn't need to:
|
||||
|
||||
* type: Default "website".
|
||||
* locale: Default "en_US".
|
||||
* site_name: Default "Software Freedom Conservancy"
|
||||
|
||||
{% endcomment %}
|
||||
|
||||
<meta property="og:type" content="{{ type|default:"website" }}">
|
||||
<meta property="og:locale" content="{{ locale|default:"en_US" }}">
|
||||
<meta property="og:site_name" content="{{ site_name|default:"Software Freedom Conservancy" }}">
|
||||
|
||||
{% if url %}
|
||||
{% load fill_url %}
|
||||
<meta property="og:url" content="{{ url|fill_url:host_url }}">
|
||||
{% endif %}
|
||||
|
||||
{% if title %}
|
||||
<meta property="og:title" content="{{ title|striptags|safe }}">
|
||||
{% endif %}
|
||||
|
||||
{% if description %}
|
||||
<meta property="og:description" content="{{ description|striptags|safe }}">
|
||||
{% endif %}
|
26
www/conservancy/templates/opengraph_urllist_partial.html
Normal file
26
www/conservancy/templates/opengraph_urllist_partial.html
Normal file
|
@ -0,0 +1,26 @@
|
|||
{% comment %}
|
||||
|
||||
Include this partial in a head section to include a series of URLs for a
|
||||
given property, like og:image or og:video.
|
||||
|
||||
You must pass the following variables:
|
||||
|
||||
* property: A string with the name of the property, like 'image' or 'video'.
|
||||
* urls: A sequence of URL strings. Each should include at least an absolute
|
||||
path. This partial will fill in a scheme and host if needed.
|
||||
|
||||
You may also pass:
|
||||
|
||||
* fallback: A URL string, following the same rules as in `urls`. This URL
|
||||
will be used if `urls` is empty.
|
||||
|
||||
{% endcomment %}
|
||||
|
||||
{% load fill_url %}
|
||||
{% for url in urls %}
|
||||
<meta property="og:{{ property }}" content="{{ url|fill_url:host_url }}">
|
||||
{% empty %}
|
||||
{% if fallback %}
|
||||
<meta property="og:{{ property }}" content="{{ fallback|fill_url:host_url }}">
|
||||
{% endif %}
|
||||
{% endfor %}
|
|
@ -17,75 +17,41 @@
|
|||
# along with this program in a file in the toplevel directory called
|
||||
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.conf.urls import patterns, url, include
|
||||
from django.contrib import admin
|
||||
from django.conf.urls import url, include
|
||||
from django.contrib import admin, admindocs
|
||||
|
||||
# import conservancy.settings
|
||||
from django.conf import settings
|
||||
from conservancy.feeds import BlogFeed, PressReleaseFeed, OmnibusFeed
|
||||
# from django.views.static import serve
|
||||
# from django.conf.urls.static import static
|
||||
# from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
# import conservancy.static.overview.views
|
||||
|
||||
# handler404 = 'modpythoncustom.view404'
|
||||
# handler401 = 'conservancy.static.views.handler401'
|
||||
# handler403 = 'conservancy.static.views.handler403'
|
||||
handler404 = 'conservancy.static.views.handler404'
|
||||
# handler500 = 'conservancy.static.views.handler500'
|
||||
from conservancy import feeds, frontpage, sponsors
|
||||
import conservancy.apps.fundgoal.views as fundgoal_views
|
||||
import conservancy.static.views as static_views
|
||||
|
||||
admin.autodiscover()
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^$', 'conservancy.frontpage.view'),
|
||||
(r'^sponsors$', 'conservancy.frontpage.view'),
|
||||
(r'^sponsors/$', 'conservancy.sponsors.view'),
|
||||
(r'^sponsors/index.html$', 'conservancy.sponsors.view'),
|
||||
(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||
(r'^admin/', admin.site.urls),
|
||||
(r'^feeds/blog/?$', BlogFeed()),
|
||||
(r'^feeds/news/?$', PressReleaseFeed()),
|
||||
(r'^feeds/omnibus/?$', OmnibusFeed()),
|
||||
(r'^feeds/?$', 'conservancy.feeds.view'),
|
||||
(r'^news(/|$)', include('conservancy.apps.news.urls')),
|
||||
(r'^blog(/|$)', include('conservancy.apps.blog.urls')),
|
||||
urlpatterns = [
|
||||
url(r'^$', frontpage.view),
|
||||
url(r'^sponsors$', frontpage.view),
|
||||
url(r'^sponsors/$', sponsors.view),
|
||||
url(r'^sponsors/index.html$', sponsors.view),
|
||||
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||
url(r'^admin/', admin.site.urls),
|
||||
url(r'^feeds/blog/?$', feeds.BlogFeed()),
|
||||
url(r'^feeds/news/?$', feeds.PressReleaseFeed()),
|
||||
url(r'^feeds/omnibus/?$', feeds.OmnibusFeed()),
|
||||
url(r'^feeds/?$', feeds.view),
|
||||
url(r'^news(/|$)', include('conservancy.apps.news.urls')),
|
||||
url(r'^blog(/|$)', include('conservancy.apps.blog.urls')),
|
||||
# formerly static templated things... (dirs with templates)
|
||||
(r'^error/(40[134]|500)(?:/index\.html|/|)$', 'conservancy.static.views.handler'),
|
||||
(r'^error', 'conservancy.static.views.index'),
|
||||
(r'^about', 'conservancy.static.views.index'),
|
||||
(r'^donate', 'conservancy.static.views.index'),
|
||||
(r'^copyleft-compliance', 'conservancy.static.views.index',
|
||||
url(r'^error/(40[134]|500)(?:/index\.html|/|)$', static_views.handler),
|
||||
url(r'^error', static_views.index),
|
||||
url(r'^about', static_views.index),
|
||||
url(r'^donate', static_views.index),
|
||||
url(r'^copyleft-compliance', static_views.index,
|
||||
{'fundraiser_sought' : 'vmware-match-0'}),
|
||||
(r'^projects', 'conservancy.static.views.index'),
|
||||
(r'^npoacct', 'conservancy.static.views.index',
|
||||
url(r'^projects', static_views.index),
|
||||
url(r'^npoacct', static_views.index,
|
||||
{'fundraiser_sought' : 'npoacct'}),
|
||||
(r'^contractpatch', include('conservancy.apps.contractpatch.urls')),
|
||||
(r'^overview', 'conservancy.static.views.index'),
|
||||
(r'^privacy-policy', 'conservancy.static.views.index'),
|
||||
(r'^supporter', include('conservancy.apps.supporter.urls')),
|
||||
(r'^fundraiser_data', 'conservancy.apps.fundgoal.views.view'),
|
||||
)
|
||||
|
||||
# urlpatterns += url(regex = r'^%s(?P<path>.*)$' % conservancy.settings.STATIC_URL[1:],
|
||||
# urlpatterns += url(regex = r'^/overview',
|
||||
# view = 'django.views.static.serve',
|
||||
# kwargs = {'document_root': conservancy.settings.STATIC_ROOT,
|
||||
# 'show_indexes' : True})
|
||||
# urlpatterns += (r'^(?P<path>.*)$', 'django.views.static.serve',
|
||||
# urlpatterns += (r'^overview/$', 'django.views.static.serve',
|
||||
# {'document_root': conservancy.settings.STATIC_ROOT,
|
||||
# 'show_indexes' : True})
|
||||
|
||||
# https://docs.djangoproject.com/en/1.7/howto/static-files/
|
||||
# + static(conservancy.settings.STATIC_URL, document_root=conservancy.settings.STATIC_ROOT)
|
||||
|
||||
# urlpatterns += staticfiles_urlpatterns()
|
||||
|
||||
# urlpatterns += static(settings.STATIC_URL, view='django.contrib.staticfiles.views.serve',
|
||||
# urlpatterns += static('/', view='django.contrib.staticfiles.views.serve',
|
||||
# document_root=settings.STATIC_ROOT,
|
||||
# show_indexes=True)
|
||||
|
||||
|
||||
|
||||
|
||||
url(r'^contractpatch', include('conservancy.apps.contractpatch.urls')),
|
||||
url(r'^overview', static_views.index),
|
||||
url(r'^privacy-policy', static_views.index),
|
||||
url(r'^supporter', include('conservancy.apps.supporter.urls')),
|
||||
url(r'^fundraiser_data', fundgoal_views.view),
|
||||
]
|
||||
|
|
Loading…
Reference in a new issue