Merge FAIF "podjango" codebase into main sfconservancy.org site

It's a little awkward for now with podjango also having an "apps" module, but we
can clean that up later. I've excluded any JS and HTML that's likely not
required.

To accomodate this merging, I moved the templates into a "podjango" subdirectory
and, added a "podjango" namespace to the URLconf and converted a bunch of
hard-coded links to use the "url" template tag since there will now be a
"/faif/" prefix.
This commit is contained in:
Ben Sturmfels 2023-10-05 19:17:55 +11:00
parent 1e494a4e11
commit c0acfde9c3
Signed by: bsturmfels
GPG key ID: 023C05E2C9C068F0
43 changed files with 1438 additions and 0 deletions

View file

@ -97,6 +97,8 @@ INSTALLED_APPS = [
'conservancy.apps.fundgoal',
'conservancy.apps.assignment',
'conservancy.apps.fossy',
'podjango', # Here so that the templates are found
'podjango.apps.cast',
]
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'

View file

@ -67,4 +67,5 @@ urlpatterns = [
url(r'^assignment/', include('conservancy.apps.assignment.urls')),
url(r'^fossy/$', static_views.index),
url(r'^fossy/', include('conservancy.apps.fossy.urls')),
url(r'^faif/', include('podjango.urls')),
]

0
www/podjango/__init__.py Normal file
View file

View file

View file

View file

@ -0,0 +1,37 @@
# Copyright (C) 2008 Bradley M. Kuhn <bkuhn@ebb.org>
# Copyright (C) 2006, 2007 Software Freedom Law Center, Inc.
#
# This software's license gives you freedom; you can copy, convey,
# propogate, redistribute and/or modify this program under the terms of
# the GNU Affero General Public License (AGPL) as published by the Free
# Software Foundation (FSF), either version 3 of the License, or (at your
# option) any later version of the AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
from django.contrib import admin
from .models import CastTag, Cast
class CastTagAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ('label',)}
admin.site.register(CastTag, CastTagAdmin)
class CastAdmin(admin.ModelAdmin):
list_display = ('pub_date', 'title')
list_filter = ['pub_date']
date_hierarchy = 'pub_date'
search_fields = ['title', 'summary', 'body']
prepopulated_fields = {'slug': ("title",)}
filter_horizontal = ('tags',)
admin.site.register(Cast, CastAdmin)

View file

@ -0,0 +1,50 @@
# Generated by Django 3.2.19 on 2023-09-21 08:55
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='CastTag',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('label', models.CharField(max_length=100)),
('slug', models.SlugField()),
],
options={
'db_table': 'cast_tags',
},
),
migrations.CreateModel(
name='Cast',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200)),
('slug', models.SlugField(unique=True)),
('summary', models.TextField(help_text='Use raw HTML. This summary is not included at the beginning of the body when the entry is displayed. It used only for the material on the front page.')),
('body', models.TextField(help_text='Use raw HTML. Include the full body of any show notes or other information about this episode. It will be labelled on the site as Show Notes. It is included on the detail entry, and in the description data on the cast RSS feed.')),
('pub_date', models.DateTimeField()),
('ogg_path', models.CharField(blank=True, help_text='Local filename of the Ogg file, relative to webroot. File should be uploaded into the static media area for casts.', max_length=300)),
('mp3_path', models.CharField(blank=True, help_text='Local filename of the mp3 file, relative to webroot. File should be uploaded into the static media area for casts.', max_length=300)),
('ogg_length', models.IntegerField(help_text='size in bytes of ogg file')),
('mp3_length', models.IntegerField(help_text='size in bytes of mp3 file')),
('duration', models.CharField(help_text='length in hh:mm:ss of mp3 file', max_length=8)),
('date_created', models.DateTimeField(auto_now_add=True)),
('date_last_modified', models.DateTimeField(auto_now=True)),
('tags', models.ManyToManyField(blank=True, to='cast.CastTag')),
],
options={
'verbose_name_plural': 'casts',
'db_table': 'casts_entries',
'ordering': ('-pub_date',),
'get_latest_by': 'pub_date',
},
),
]

View file

@ -0,0 +1,76 @@
# Copyright (C) 2008 Bradley M. Kuhn <bkuhn@ebb.org>
# Copyright (C) 2006, 2007 Software Freedom Law Center, Inc.
#
# This software's license gives you freedom; you can copy, convey,
# propogate, redistribute and/or modify this program under the terms of
# the GNU Affero General Public License (AGPL) as published by the Free
# Software Foundation (FSF), either version 3 of the License, or (at your
# option) any later version of the AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
from django.db import models
from django.conf import settings
#from podjango.apps.staff.models import Person
from datetime import datetime, timedelta
class CastTag(models.Model):
"""Tagging for casts"""
label = models.CharField(max_length=100)
slug = models.SlugField()
class Meta:
db_table = 'cast_tags' # legacy
def __unicode__(self):
return self.label
def get_absolute_url(self):
return "/cast/?tag=%s" % self.slug
class Cast(models.Model):
"""Cast"""
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
summary = models.TextField(help_text="Use raw HTML. This summary is not included at the beginning of the body when the entry is displayed. It used only for the material on the front page.")
body = models.TextField(help_text="Use raw HTML. Include the full body of any show notes or other information about this episode. It will be labelled on the site as Show Notes. It is included on the detail entry, and in the description data on the cast RSS feed.")
pub_date = models.DateTimeField()
# poster = models.ForeignKey(Person)
tags = models.ManyToManyField(CastTag, blank=True)
ogg_path = models.CharField(max_length=300, blank=True,
help_text="Local filename of the Ogg file, relative to webroot. File should be uploaded into the static media area for casts.")
mp3_path = models.CharField(max_length=300, blank=True,
help_text="Local filename of the mp3 file, relative to webroot. File should be uploaded into the static media area for casts.")
ogg_length = models.IntegerField(blank=False, help_text="size in bytes of ogg file")
mp3_length = models.IntegerField(blank=False, help_text="size in bytes of mp3 file")
duration = models.CharField(max_length=8, blank=False, help_text="length in hh:mm:ss of mp3 file")
date_created = models.DateTimeField(auto_now_add=True)
date_last_modified = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'casts_entries' # legacy
verbose_name_plural = 'casts'
ordering = ('-pub_date',)
get_latest_by = 'pub_date'
def __unicode__(self):
return self.title
def get_absolute_url(self):
return "/cast/%s/%s/" % (self.pub_date.strftime("%Y/%b/%d").lower(),
self.slug)
# FIXME
# return (u"/cast/%s/" % (self.slug))
def is_recent(self):
return self.pub_date > (datetime.now() - timedelta(days=14))
# question: does datetime.now() do a syscall each time is it called?

View file

@ -0,0 +1,9 @@
from django import template
from datetime import timedelta, datetime
register = template.Library()
@register.filter
def date_within_past_days(value, arg):
# question: does datetime.now() do a syscall each time is it called?
return value > (datetime.now() - timedelta(days=int(arg)))

View file

@ -0,0 +1,82 @@
# Copyright (C) 2008 Bradley M. Kuhn <bkuhn@ebb.org>
# Copyright (C) 2006, 2007 Software Freedom Law Center, Inc.
#
# This software's license gives you freedom; you can copy, convey,
# propogate, redistribute and/or modify this program under the terms of
# the GNU Affero General Public License (AGPL) as published by the Free
# Software Foundation (FSF), either version 3 of the License, or (at your
# option) any later version of the AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
from datetime import datetime
from django.conf.urls import url
from django.views.generic.dates import DateDetailView, DayArchiveView, MonthArchiveView, YearArchiveView
from .models import Cast, CastTag
from .views import custom_index, query
extra_context = {}
info_dict = {
'queryset': Cast.objects.all(),
'date_field': 'pub_date',
'extra_context': extra_context,
}
urlpatterns = [
url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/(?P<slug>[-\w]+)/$', DateDetailView.as_view(**info_dict), name='detail'),
url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/$', DayArchiveView.as_view(**info_dict), name='day-archive'),
url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', MonthArchiveView.as_view(**info_dict), name='month-archive'),
url(r'^(?P<year>\d{4})/$', YearArchiveView.as_view(**info_dict), name='year-archive'),
# FIXME HOW DO I MAKE THE SLUG WORK WITH NO DATES IN IT.
# (r'^(?P<slug>[-\w]+)/$', 'object_detail', dict(info_dict, slug_field='slug')),
]
urlpatterns += [
url(r'^$', custom_index, dict(info_dict, paginate_by=20), name='cast'),
url(r'^query/$', query, name='query'),
]
# Code to display authors and tags on each blog page
def all_tags_by_use_amount():
"""Returns all tags with an added 'cnt' attribute (how many times used)
Also sorts the tags so most-used tags appear first.
"""
# tally use amount
retval = []
current = None
for obj in CastTag.objects.filter(cast__pub_date__lte=datetime.now(),
cast__isnull=False).order_by('label'):
if current is not None and obj.id == current.id:
current.cnt += 1
else:
if current is not None:
retval.append(current)
current = obj
current.cnt = 1
if current is not None:
retval.append(current)
# sort and return
retval.sort(key=lambda x: -x.cnt)
return retval
# The functions are passed to the context uncalled so they will be
# called for each web request. If we want to only make these database
# queries a single time when a web server process begins, call both
# functions below (i.e. make both lines below end in '()')
extra_context['all_tags'] = all_tags_by_use_amount

View file

@ -0,0 +1,110 @@
# Copyright (C) 2008 Bradley M. Kuhn <bkuhn@ebb.org>
# Copyright (C) 2006, 2007 Software Freedom Law Center, Inc.
#
# This software's license gives you freedom; you can copy, convey,
# propogate, redistribute and/or modify this program under the terms of
# the GNU Affero General Public License (AGPL) as published by the Free
# Software Foundation (FSF), either version 3 of the License, or (at your
# option) any later version of the AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
from datetime import datetime
from django.views.generic.list import ListView
from django.shortcuts import get_object_or_404, render
from .models import Cast, CastTag
def OR_filter(field_name, objs):
from django.db.models import Q
return reduce(lambda x, y: x | y,
[Q(**{field_name: x.id}) for x in objs])
def last_name(person):
return person.formal_name.rpartition(' ')[2]
def custom_index(request, queryset, *args, **kwargs):
"""Cast list view that allows scrolling and also shows an index by
year.
"""
kwargs = kwargs.copy()
kwargs['extra_context'] = kwargs.get('extra_context', {}).copy()
extra_context = kwargs['extra_context']
date_field = kwargs['date_field']
del kwargs['date_field']
if not kwargs.get('allow_future', False):
queryset = queryset.filter(**{'%s__lte' % date_field: datetime.now()})
authors = []
if 'author' in request.GET:
authors = [get_object_or_404(Person, username=author)
for author in request.GET.getlist('author')]
extra_context['authors'] = authors
queryset = queryset.filter(OR_filter('author', authors))
tags = []
if 'tag' in request.GET:
tags = [get_object_or_404(CastTag, slug=tag)
for tag in request.GET.getlist('tag')]
extra_context['tags'] = tags
queryset = queryset.filter(OR_filter('tags', tags))
if authors or tags:
query_string = '&'.join(['author=%s' % a.username for a in authors]
+ ['tag=%s' % t.slug for t in tags])
extra_context['query_string'] = query_string
else:
date_list = queryset.dates(date_field, 'year')
extra_context['date_list'] = date_list
# TODO
return render(request, 'podjango/cast/cast_list.html', {'object_list': queryset})
def query(request):
"""Page to query the cast based on and tags
"""
if request.GET:
d = request.GET.copy()
if 'authors' in d.getlist('all'):
d.setlist('author', []) # remove author queries
if 'tags' in d.getlist('all'):
d.setlist('tag', []) # remove tag queries
d.setlist('all', []) # remove "all" from the query string
base_url = '/cast/'
if 'rss' in d:
base_url = '/feeds/cast/'
d.setlist('rss', []) # remove it
query_string = d.urlencode()
return relative_redirect(request, '%s%s%s' % (base_url, '?' if query_string else '', query_string))
else:
tags = CastTag.objects.all().order_by('label')
return render(request, 'podjango/cast/query.html', {'tags': tags})
def relative_redirect(request, path):
from django import http
from django.conf import settings
host = http.get_host(request)
if settings.FORCE_CANONICAL_HOSTNAME:
host = settings.FORCE_CANONICAL_HOSTNAME
url = "%s://%s%s" % (request.is_secure() and 'https' or 'http', host, path)
return http.HttpResponseRedirect(url)

277
www/podjango/feeds.py Normal file
View file

@ -0,0 +1,277 @@
# Copyright (C) 2008, 2010 Bradley M. Kuhn <bkuhn@ebb.org>
# Copyright (C) 2006, 2007 Software Freedom Law Center, Inc.
#
# This software's license gives you freedom; you can copy, convey,
# propogate, redistribute and/or modify this program under the terms of
# the GNU Affero General Public License (AGPL) as published by the Free
# Software Foundation (FSF), either version 3 of the License, or (at your
# option) any later version of the AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
from django.contrib.syndication.views import Feed
from django.utils.feedgenerator import Rss201rev2Feed
#from podjango.apps.staff.models import Person
from podjango.apps.cast.models import Cast
from django.shortcuts import render
from django.conf import settings
from datetime import datetime
import itertools
import operator
# FIXME: Settings here should not be hard-coded for given casts, but
# should instead have settings from the main screen.
class CastFeedBase(Feed):
def copyright_holder(self): return "Bradley M. Kuhn, Karen M. Sandler"
def license_no_html(self): return "Licensed under a Creative Commons Attribution-Share Alike 3.0 USA License."
def item_copyright(self, item):
year = 2008
for attr in ('pub_date', 'date_created', 'date_last_modified'):
if hasattr(item, attr):
if hasattr(getattr(item, attr), 'year'):
year = getattr(getattr(item, attr), 'year')
break
return 'Copyright (C) %d, %s. %s' % (year, self.copyright_holder(), self.license_no_html())
def item_extra_kwargs(self, item):
year = 2008
for attr in ('pub_date', 'date_created', 'date_last_modified'):
if hasattr(item, attr):
if hasattr(getattr(item, attr), 'year'):
year = getattr(getattr(item, attr), 'year')
break
return { 'year' : year }
def for_podcast_feed_extra_kwargs(self, obj):
return { 'managingEditorNames' : 'Bradley and Karen',
'rssImage' : { 'url' : 'http://faif.us/img/cast/faif_144x144.jpg',
'width' : '144', 'height' : '144' },
'webMaster' : 'oggcast@faif.us (Bradley and Karen)',
'dcCreator' : 'oggcast@faif.us (Bradley and Karen)',
'iTunesExplicit' : 'No',
'iTunesBlock' : 'No',
'iTunesImage' : { 'url' : 'http://faif.us/img/cast/faif_300x300.jpg',
'title' : 'Free as in Freedom',
'link' : self.author_link,
'type' : 'video/jpg'},
'category' : { 'name' : 'Government & Organizations', 'scheme' : 'http://www.itunes.com/dtds/podcast-1.0.dtd',
'subcats' : [ 'Non-Profit' ] },
'keywords' : 'open source, opensource, freesoftware, software freedom, legal, law, linux, free, license, gpl, lgpl, agpl, bsd',
'iTunesAuthor' : 'Free as in Freedom',
'iTunesSubtitle' : 'Bi-Weekly Discussion of Legal, Policy, and Any other Issues in the Free, Libre, and Open Source Software (FLOSS) Community',
'copyrightHolder' : self.copyright_holder(),
'copyrightLicense' : self.license_no_html() }
def for_podcast_item_extra_kwargs(self, item):
return { 'duration' : item.duration,
'year' : item.date_created.year,
'dcCreator' : 'oggcast@faif.us (Bradley and Karen)',
'intheitembkuhn' : item.__dict__.__str__()}
def podcast_helper_add_root_elements(self, handler):
handler.addQuickElement('managingEditor', self.feed['author_email']
+ ' (' + self.feed['managingEditorNames'] + ')')
handler.startElement('image', {})
handler.addQuickElement('url', self.feed['rssImage']['url'])
handler.addQuickElement('title', self.feed['author_name'])
handler.addQuickElement('link', self.feed['link'])
handler.addQuickElement('width', self.feed['rssImage']['width'])
handler.addQuickElement('height', self.feed['rssImage']['height'])
handler.endElement('image')
handler.addQuickElement('webMaster', self.feed['webMaster'])
# handler.addQuickElement('dc:creator', self.feed['dcCreator'])
handler.addQuickElement('itunes:explicit', self.feed['iTunesExplicit'])
handler.addQuickElement('itunes:block', self.feed['iTunesBlock'])
handler.addQuickElement('generator', 'http://www.faif.us/code')
handler.addQuickElement('media:thumbnail', '' , { 'url' : self.feed['rssImage']['url'] })
handler.addQuickElement('itunes:image', '' , { 'href' : self.feed['iTunesImage']['url']})
# handler.addQuickElement('itunes:link', '', { 'href' : self.feed['iTunesImage']['url'],
# 'type' : self.feed['iTunesImage']['type']})
handler.addQuickElement("media:category", self.feed['category']['name'],
{ 'scheme': self.feed['category']['scheme']})
if not (self.feed['category']['subcats'] and len(self.feed['category']['subcats']) > 0):
handler.addQuickElement("itunes:category", '', { 'text': self.feed['category']['name']})
else:
handler.startElement("itunes:category", { 'text': self.feed['category']['name']})
for cc in self.feed['category']['subcats']:
handler.addQuickElement("itunes:category", '', { 'text': cc })
handler.endElement("itunes:category")
handler.addQuickElement("media:keywords", self.feed['keywords'].replace(" ", ","))
handler.startElement("itunes:owner", {})
handler.addQuickElement("itunes:email", self.feed['author_email'])
handler.addQuickElement("itunes:name", self.feed['author_name'])
handler.endElement("itunes:owner")
handler.addQuickElement("itunes:summary", self.feed['description'])
handler.addQuickElement("itunes:subtitle", self.feed['iTunesSubtitle'])
handler.addQuickElement("itunes:author", self.feed['iTunesAuthor'])
handler.addQuickElement('atom:link', '', { 'rel' : "self", 'href' : self.feed['feed_url'],
'type' : "application/rss+xml"})
years = {}
for ii in self.items: years[ii['year']] = 1
copyrightString = ""
ll = years.keys()
sorted(ll)
for yy in ll: copyrightString += "%d, " % yy
copyrightString += "%s. %s" % (self.feed['copyrightHolder'], self.feed['copyrightLicense'])
handler.addQuickElement('copyright', copyrightString)
handler.addQuickElement('media:copyright', "Copyright (C) " + copyrightString)
def podcast_helper_add_item_elements(self, handler, item):
handler.addQuickElement("itunes:explicit", self.feed['iTunesExplicit'])
handler.addQuickElement("itunes:block", self.feed['iTunesBlock'])
handler.addQuickElement("itunes:keywords", self.feed['keywords'])
# handler.addQuickElement('dc:creator', self.feed['dcCreator'])
handler.addQuickElement("itunes:author", item['author_name'])
handler.addQuickElement("itunes:duration", item['duration'])
if 'enclosure' in item:
handler.addQuickElement('media:content', '', { 'url' : item['enclosure'].url,
'fileSize' : item['enclosure'].length,
'type' : item['enclosure'].mime_type})
class OmnibusFeedType(Rss201rev2Feed):
def root_attributes(self):
attrs = super().root_attributes()
attrs['xmlns:itunes'] = 'http://www.itunes.com/dtds/podcast-1.0.dtd'
attrs['xmlns:atom'] = 'http://www.w3.org/2005/Atom'
attrs['xmlns:media'] = 'http://search.yahoo.com/mrss/'
# attrs['xmlns:dc'] = "http://purl.org/dc/elements/1.1/"
return attrs
def add_root_elements(self, handler):
super().add_root_elements(handler)
podcast_helper_add_root_elements(self, handler)
def add_item_elements(self, handler, item):
super().add_item_elements(handler, item)
# The below is a bit of a cheat, I assume anything in the ominbus
# feed that has an enclosure must be a podcast. Probably not true
# as such in the general case, but enough for this case, I think.
if item['enclosure']:
podcast_helper_add_item_elements(self, handler, item)
else:
# Block things that don't have an enclosure from iTunes in
# case someone uploads this feed there.
handler.addQuickElement("itunes:block", 'Yes')
# http://www.feedforall.com/itune-tutorial-tags.htm
# http://www.feedforall.com/mediarss.htm
class iTunesFeedType(Rss201rev2Feed):
def root_attributes(self):
attrs = super().root_attributes()
attrs['xmlns:itunes'] = 'http://www.itunes.com/dtds/podcast-1.0.dtd'
attrs['xmlns:atom'] = 'http://www.w3.org/2005/Atom'
attrs['xmlns:media'] = 'http://search.yahoo.com/mrss/'
# attrs['xmlns:dc'] = "http://purl.org/dc/elements/1.1/"
return attrs
def add_root_elements(self, handler):
super().add_root_elements(handler)
podcast_helper_add_root_elements(self, handler)
def add_item_elements(self, handler, item):
super().add_item_elements(handler, item)
podcast_helper_add_item_elements(self, handler, item)
class CastFeed(CastFeedBase):
feed_type = iTunesFeedType
title = "Free as in Freedom"
link = "/cast/"
description = "A bi-weekly discussion of legal, policy, and other issues in the open source and software freedom community (including occasional interviews) from Brooklyn, New York, USA. Presented by Karen Sandler and Bradley M. Kuhn."
author_email = "podcast@faif.us"
author_link = "http://www.faif.us/"
author_name = "Free as in Freedom"
title_template = "feeds/podcast_title.html"
description_template = "feeds/podcast_description.html"
def items(self):
return Cast.objects.filter(pub_date__lte=datetime.now()).order_by('-pub_date')
def item_pubdate(self, item):
return item.pub_date
def item_link(self, item):
return item.get_absolute_url()
def item_author_email(self, obj):
return "oggcast@faif.us"
def item_author_name(self, obj):
return "Free as in Freedom"
def item_author_link(self, obj):
return "http://faif.us"
def item_categories(self, item):
return ('Technology',)
def copyright_holder(self): return "Free as in Freedom"
def license_no_html(self): return "Licensed under a Creative Commons Attribution-Share Alike 3.0 USA License."
def feed_extra_kwargs(self, obj):
return for_podcast_feed_extra_kwargs(self, obj)
def item_extra_kwargs(self, item):
return for_podcast_item_extra_kwargs(self, item)
# FIXME:
# GUEST NAME GOES HERE!!!
#<itunes:author>
# If applicable, at the item level, this tag can contain information
# about the person(s) featured on a specific episode.
class Mp3CastFeed(CastFeed):
def item_enclosure_mime_type(self): return "audio/mpeg"
def item_enclosure_url(self, item):
return "http://faif.us" + item.mp3_path
def item_enclosure_length(self, item):
return item.mp3_length
class OggCastFeed(CastFeed):
def item_enclosure_mime_type(self): return "audio/ogg"
def item_enclosure_url(self, item):
return "http://faif.us" + item.ogg_path
def item_enclosure_length(self, item):
return item.ogg_length
feed_dict = {
'cast-ogg': OggCastFeed,
'cast-mp3': Mp3CastFeed,
}
# make each feed know its canonical url
for k, v in feed_dict.items():
v.get_absolute_url = '/feeds/%s/' % k
def view(request):
"""Listing of all available feeds
"""
feeds = feed_dict.values()
return render(request, "feeds.html", {'feeds': feeds})

34
www/podjango/frontpage.py Normal file
View file

@ -0,0 +1,34 @@
# Copyright 2010 Bradley M. Kuhn <bkuhn@ebb.org>
# Copyright 2005-2008 James Garrison
# This software's license gives you freedom; you can copy, convey,
# propagate, redistribute, modify and/or redistribute modified versions of
# this program under the terms of the GNU Affero General Public License
# (AGPL) as published by the Free Software Foundation (FSF), either
# version 3 of the License, or (at your option) any later version of the
# AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
from django.shortcuts import render
from podjango.apps.cast.models import Cast
from datetime import datetime, timedelta
def view(request):
"""Cast front page view
Performs all object queries necessary to render the front page.
"""
cast = Cast.objects.all().filter(pub_date__lte=datetime.now())[:3]
c = {
'cast': cast,
}
return render(request, "podjango/frontpage.html", c)

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 B

View file

@ -0,0 +1,50 @@
{% extends "base_standard.html" %}
{% block content %}
<h1>License and Source For This Website</h1>
<p>The software running this website is copyrighted &copy; 2007-2008,
Software Freedom Law Center, Inc. and copyrighted &copy; 2010, 2016 Bradley M.
Kuhn. The software licensed under the terms of
the <a href="http://www.gnu.org/licenses/agpl-3.0.html">AGPLv3</a>-or-later.
You can find a <a href="/source/">copy the sources of the Debian packages on
this site</a>, but it may be more interesting to know that the site is
running on stock Debian lenny with Apache, Django, mod_python, and
sqlite installed, and that the main sources for the site's code itself
is <a href="https://github.com/bkuhn/podjango/tree/deployments/faif">hosted
on Github (temporarily, hopefully)</a>.</p>
<p>The documents on this website are copyrighted &copy; 2010, 2012, 2015, 2016 Bradley
M. Kuhn and Karen M. Sandler, and are licensed
as <a href="http://creativecommons.org/licenses/by-sa/3.0/">CC-By-SA-4.0
International</a>. In some cases, the documents are dual-licensed
(AGPLv3-or-later|CC-By-SA-4.0-International), to make it more
convenient to intermix the document with code.</p>
<p><a rel="license"
href="http://creativecommons.org/licenses/by-sa/3.0/us/"><img alt="Creative
Commons License" style="border-width:0"
src="http://i.creativecommons.org/l/by-sa/3.0/us/88x31.png" hspace=10
/></a>The content of <span xmlns:dc="http://purl.org/dc/elements/1.1/"
href="http://purl.org/dc/dcmitype/Sound" rel="dc:type">this
audcast</span>, the accompanying show notes and music are copyrighted
&copy; 2010, 2011, 2012, 2013, 2014, 2015, 2016 by <a href="http://ebb.org/bkuhn">Bradley M. Kuhn</a>,
<a href="http://gnomg.org">Karen M. Sandler</a>, <a href="http://danlynch.org/">Dan Lynch</a>, and <a href="http://www.miketarantino.com">Mike
Tarantino</a>. Some speech recording and other materials are
copyrighted by the individual speakers.</p>
<p><p><a rel="license"
href="https://creativecommons.org/licenses/by-sa/4.0/"><img alt="Creative Commons License" style="border-width:0"
src="http://i.creativecommons.org/l/by-sa/4.0/88x31.png" hspace=10 /></a>
The content
of <span xmlns:dc="http://purl.org/dc/elements/1.1/" href="http://purl.org/dc/dcmitype/Sound" rel="dc:type">this
audcast</span>, and the accompanying show notes and music are licensed
under the <a rel="license"
href="https://creativecommons.org/licenses/by-sa/4.0/">Creative
Commons Attribution-Share-Alike 3.0 USA license (CC BY-SA 4.0)</a>.
<p>The icons for the RSS audio feeds and direct audio links are borrowed from
the <a href="http://www.mightyseek.com/podpress/">PodPress</a> project
and are licensed under <a href="/gplv2.txt">GPLv2</a>.</p>
{% endblock %}

View file

@ -0,0 +1,241 @@
* { margin: 0; padding: 0; }
img { border: 0; }
body { margin: 0; padding: 0; }
.clear { clear: both; }
.hidden { display: none; }
p, h1, h2, h3, h4, h5, h6, #mainContent ul, #mainContent ol {
margin-top: 1em;
margin-bottom: 1em;
}
body {
/* font-family: "Lucida Grande",Verdana,Lucida,Helvetica,Arial,sans-serif; */
font-size: 95%;
}
a { text-decoration: none; color: #146624; }
a:hover { text-decoration: underline; color: #665f14; }
h1 { margin-top: .75em; margin-bottom: .5em; }
h2 { margin-top: .75em; margin-bottom: .5em; }
h3 { margin-top: .6em; margin-bottom: .4em; }
#mainContent h1 { border-bottom: 1px solid #00334b; }
#mainContent h2 { border-bottom: 1px solid #aaa; }
#mainContent ul, #mainContent ol { padding-left: 1.5em; }
#mainContent img { margin: 3px; }
.internalNavigate { width: 19%; float: right; }
#mainContent .internalNavigate ul { list-style-type: none; padding-left: 0; }
.internalNavigate ul li { margin-top: .3em; margin-bottom: .3em; }
.affiliate-link { float: right; }
#affiliate-podjango, #affiliate-mr {
padding: 2px 10px;
white-space: nowrap;
font-size: x-small;
text-align: right;
}
#affiliate-podjango { background: #d4ff9d; }
#affiliate-mr { background: #ddd; }
/* Header */
#podjangoheader {
height: 3.5em;
background-color: #12d5bc;
border-bottom: 1px solid #808080;
}
#podjangoheader h1 {
font-size: 3em;
/* -indent: -5000px; */
/* margin: 0; /\* hide in favor of image *\/ */
}
/* #logobutton { */
/* display: block; */
/* position: absolute; left: 17px; top: 10px; height: 120px; width: 440px; */
/* background: url(/img/podjango-header.png) left center no-repeat; */
/* } */
/* Navigation bar */
#navbar-outer { background: #CDDBDC; }
#navbar { margin-left: 104px; margin-top: 3px; float: left; display: inline; }
#navbar ul { list-style: none; }
#navbar-clear { clear: both; border-bottom: 1px solid #808080; }
#navbar li a {
display: block;
background: #fafafe;
padding: .2em .8em;
margin-right: 3px;
border: 1px solid #808080;
font-size: .8em;
background: #fff url(/img/nav-bg.png) bottom repeat-x;
}
#navbar li:hover a { background: #fff99d; }
#navbar li { float: left; display: inline; margin-bottom: 3px; }
#mainContent {
margin-left: 50px;
margin-right: 50px;
}
#container {
width: 100%;
overflow: hidden;
}
#container #mainContent {
background: #fffff;
margin-left: 210px;
margin-right: 50px;
padding-bottom: 32767px;
margin-bottom: -32767px;
}
#container #sidebar {
width: 200px;
float: left;
padding-bottom: 32767px;
margin-bottom: -32767px;
background-color: #e5e5e5;
}
#container #sidebar li {
text-align: center;
list-style: none;
padding: 3px 10px 3px 10px;
margin: 5px;
border: 1px solid #CCC;
background: #fff url(/img/nav-bg.png) bottom repeat-x;
}
#container #sidebar.Directors ul li.Directors,
#container #sidebar.Contact ul li.Contact,
#container #sidebar.Officers ul li.Officers,
#container #sidebar.Staff ul li.Staff,
#container #sidebar.Current ul li.Current,
#container #sidebar.Services ul li.Services,
#container #sidebar.Applying ul li.Applying
{
color: #000033;
font-weight: bold;
background: #fff url(/img/nav-bg-up.png) top repeat-x;
}
#container #sidebar h2 {
text-align: center;
font-size: 150%;
margin: 1.5em 0 0.8em 0;
}
#container #sidebar hr {
width: 75%;
float: center;
clear: all;
}
/* Navbar Submenus (currently unused) */
#navbar li ul { display: none; border: 1px solid #444; }
#navbar li:hover ul { display: block; position: absolute; }
#navbar li ul li { float: none; }
#navbar li ul li a { border: 0px; margin: 0px; }
.shaded { background: #ffeab7; padding: .1em .5em; margin-bottom: .5em; }
.columns {
width: 100%;
overflow: hidden;
}
.column-small {
width: 31%;
padding-bottom: 32767px;
margin-bottom: -32767px;
}
.column-large {
margin-left: 35%;
margin-right: 50px;
padding-bottom: 32767px;
margin-bottom: -32767px;
}
.column-left { float: left; }
.column-right { float: right; }
.column h2 { font-size: 1.25em; }
.column h3 { font-size: 1.1em; }
.column h2 a { text-decoration: none; color: #000000; }
.column h2 a:hover { text-decoration: underline; }
#podjangofooter {
margin-top: 1em;
border-top: 1px solid #ccc;
text-align: center;
clear: both;
}
span.continued {
display: block;
font-size: .83em;
font-weight: bold;
margin-top: 1em;
margin-bottom: 1em;
}
p.date {
font-style: italic;
font-size: .83em;
margin-bottom: .3em;
margin-top: .3em;
}
a.feedlink /* RSS icon */ { display: block; float: right; font-size: 10pt; }
blockquote, div.quote /* div.quote is used by podjango whitepaper */ {
margin-left: 2em;
margin-right: 2em;
padding-left: 1em;
padding-right: 1em;
border: 1px solid #fff;
background: #eee;
}
.newsgraphic { float: right; }
.newsgraphic img { border: 1px solid #000; }
.secondary_info { font-size: 83%; }
.next_page_button { float: right; }
.document_format { border: 1px solid #888; padding: .2em; background: #fff99d;}
.copyright_info { font-size: 90%; }
hr.footnote-separator { width: 80%; margin-left: auto; margin-right: auto; }
/* Resources pages */
div.download-formats { margin-top: 2em; margin-bottom: 2em; }
.download-formats p { display: inline; }
#mainContent .download-formats ul { display: inline; list-style: none;
padding-left: 0; }
.download-formats ul li { display: inline; padding-left: 2em; }
/* Resources - book styles */
hr.chapter-separator { display: none; }
h2.likechapterHead { text-align: center; }
h2.chapterHead { text-align: center; }
#mainContent ul.author { list-style-type: none; padding-left: 0; }
#mainContent div.footnotes { font-style: normal; } /* remove italics */
span.sectionToc { padding-left: 2em; } /* indent TOC properly */
span.subsectionToc { padding-left: 4em; }
span.subsubsectionToc { padding-left: 6em;}
.js, .js p, .js p.bibitem, .js p.bibitem-p { background-color: #cde7e9; }
body.podjango-overview #navbar ul li.overview a,
body.podjango-Members #navbar ul li.Members a,
body.podjango-news #navbar ul li.news a,
body.podjango-blog #navbar ul li.blog a,
body.podjango-About #navbar ul li.About a,
body.podjango-donate #navbar ul li.donate a /* NO COMMA HERE! */
{
color: #000033;
font-weight: bold;
background: #fff url(/img/nav-bg-up.png) top repeat-x;
}

View file

@ -0,0 +1,26 @@
<!-- FIXME: SFLC SPECIFIC CONTENT!!! -->
<!-- Copyright (C) 2008 Bradley M. Kuhn <bkuhn@ebb.org> -->
<!-- Permission is granted to copy, modify, redistribute, propagate, -->
<!-- and/or convey this template in any form. -->
{% extends "base_standard.html" %}
{% block head %}
<link rel="alternate" type="application/rss+xml" title="MP3 Audio Feed for Free as in Freedom" href="{% url 'podjango:feed-mp3' %}" />
<link rel="alternate" type="application/rss+xml" title="Ogg/Vorbis Audio Feed for Free as in Freedom" href="{% url 'podjango:feed-ogg' %}" />
{% endblock %}
{% block internal_navigate %}
<h3>Tags</h3>
<ul>
{% for tag in all_tags %}
<li><a href="{{ tag.get_absolute_url }}">{{ tag.label }}</a></li>
{% endfor %}
</ul>
<p><a href="{% url 'podjango:cast' }">All oggcasts&hellip;</a></p>
<!-- Query is not working right now! -->
<!-- <p><a href="/podcast/query/">Query...</a></p> -->
{% endblock %}

View file

@ -0,0 +1,48 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>{% block title %}{% block subtitle %}{% endblock %}Free as in Freedom{% endblock %}</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="description" content="Free as in Freedom is a bi-weekly oggcast about legal, policy and other issues in Free, Libre and Open Source Software" />
<meta name="keywords" content="software, freedom, podjango, open source, gnu, GNU, Open Source, Free and Open Source, Free and Open Source Software, FLOSS, FOSS, protect, protection, help, policy, linux, non-profit, oggcast, podcast" />
<link rel="stylesheet" type="text/css" media="screen, projection" href="/podjango.css" />
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
{% block head %}{% endblock %}
</head>
<body class="faif-{% block category %}other{% endblock %}">
<div id="podjangoheader">
<h1><a href="/">Free as in Freedom</a></h1>
</div>
{% block outercontent %}<div id="mainContent">
<p>We raised $2,515.72 toward Dan Lynch's trip to a conference to represent
the show and record content! We'll be coordinated with Dan about what
conference he wants to attend.</p>
<p>If you'd like to further support Free as in Freedom,
please <a href="https://sfconservancy.org/supporter/">become a supporter of
Software
Freedom Conservancy, the charity</a> where Bradley and Karen work.</p>
{% block content %}{% endblock %}</div>{% endblock %}
<div id="podjangofooter">
{% block copyright_info %}
<p>Free as in Freedom is produced by <a href="http://danlynch.org/blog/">Dan Lynch</a>
of <a href="http://www.danlynch.org">danlynch.org</a>. Theme
music written and performed
by <a href="http://www.miketarantino.com">Mike Tarantino</a>
with <a href="http://www.charliepaxson.com">Charlie Paxson</a> on drums.</p>
<p><a href="/">Main Page</a> | <a href="/license/">License of show
and website</a> | <a href="{% url 'podjango:feed-ogg' %}">Ogg Feed</a> | <a href="{% url 'podjango:feed-mp3' %}">MP3 Feed</a></p>
<p class="copyright_info">Please see the
<a href="/license">license page</a> for details about the licensing
of the show, the content of this website, and the software that runs
this website.</p>
{% endblock %}
</div>
</body>
</html>

View file

@ -0,0 +1,34 @@
<!-- FIXME: SFLC specific content -->
<!-- Copyright (C) 2008 Bradley M. Kuhn <bkuhn@ebb.org> -->
<!-- Permission is granted to copy, modify, redistribute, propagate, -->
<!-- and/or convey this template in any form. -->
{% extends "podjango/base_podcast.html" %}
{% block subtitle %}Free as in Freedom Archive: {{ day|date:"F j, Y" }} - {% endblock %}
{% block content %}
<h1><a href="{% url 'podjango:feed-ogg' %}"
class="feedlink"><img src="/img/cast/rss-audioogg.png"
alt="[Ogg/Vorbis Audio RSS]"/></a>
<a href="{% url 'podjango:feed-mp3' %}"
class="feedlink"><img src="/img/cast/rss-audiomp3.png"
alt="[MP3 Audio RSS]"/></a>
Free as in Freedom</h1>
<h2>Free as in Freedom Archive: {{ day|date:"F j, Y" }}</h2>
{% for object in object_list %}
<div class="shaded">
<p class="date">{{ object.pub_date|date:"F j, Y" }}</p>
<h3><a href="{{ object.get_absolute_url }}">{{ object.title|safe }}</a></h3>
{{ object.summary|safe }}
<p><span class="continued"><a href="{{ object.get_absolute_url
}}">Read More...</a></span></p>
<p>Released on {{ object.pub_date|date:"F j, Y" }}; its running time is {{ object.duration }}</p>
{% if object.tags.all %}<p class="blog-tags small">Tags: {% for tag in object.tags.all %}<a href="{{ tag.get_absolute_url }}">{{ tag.label }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}</p>{% endif %}
</div>
{% endfor %}
{% endblock %}

View file

@ -0,0 +1,34 @@
<!-- FIXME: SFLC specific content -->
<!-- Copyright (C) 2008 Bradley M. Kuhn <bkuhn@ebb.org> -->
<!-- Permission is granted to copy, modify, redistribute, propagate, -->
<!-- and/or convey this template in any form. -->
{% extends "podjango/base_podcast.html" %}
{% block subtitle %}Free as in Freedom Archive: {{ month|date:"F, Y" }} - {% endblock %}
{% block content %}
<h1><a href="{% url 'podjango:feed-ogg' %}"
class="feedlink"><img src="/img/cast/rss-audioogg.png"
alt="[Ogg/Vorbis Audio RSS]"/></a>
<a href="{% url 'podjango:feed-mp3' %}"
class="feedlink"><img src="/img/cast/rss-audiomp3.png"
alt="[MP3 Audio RSS]"/></a>
Free as in Freedom</h1>
<h2>Free as in Freedom Archive: {{ month|date:"F, Y" }}</h2>
{% for object in object_list %}
<div class="shaded">
<p class="date">{{ object.pub_date|date:"F j, Y" }}</p>
<h3><a href="{{ object.get_absolute_url }}">{{ object.title|safe }}</a></h3>
{{ object.summary|safe }}
<p><span class="continued"><a href="{{ object.get_absolute_url
}}">Read More...</a></span></p>
<p>Released on {{ object.pub_date|date:"F j, Y" }}; its running time is {{ object.duration }}</p>
{% if object.tags.all %}<p class="blog-tags small">Tags: {% for tag in object.tags.all %}<a href="{{ tag.get_absolute_url }}">{{ tag.label }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}</p>{% endif %}
</div>
{% endfor %}
{% endblock %}

View file

@ -0,0 +1,28 @@
<!-- FIXME: SFLC specific content -->
<!-- Copyright (C) 2008 Bradley M. Kuhn <bkuhn@ebb.org> -->
<!-- Permission is granted to copy, modify, redistribute, propagate, -->
<!-- and/or convey this template in any form. -->
{% extends "podjango/base_podcast.html" %}
{% block subtitle %}Free as in Freedom Archive: {{ year }} - {% endblock %}
{% block content %}
<h1><a href="{% url 'podjango:feed-ogg' %}"
class="feedlink"><img src="/img/cast/rss-audioogg.png"
alt="[Ogg/Vorbis Audio RSS]"/></a>
<a href="{% url 'podjango:feed-mp3' %}"
class="feedlink"><img src="/img/cast/rss-audiomp3.png"
alt="[MP3 Audio RSS]"/></a>
Free as in Freedom</h1>
<h2>Free as in Freedom Archive: {{ year }}</h2>
<ul>
{% for object in object_list %}
<li><a href="{{ object.get_absolute_url }}"><b>{{ object.title|safe }}</b></a><br/>
<i>{{ object.pub_date|date:"F j, Y" }}; duration: {{ object.duration }}</i></li>
{% endfor %}
</ul>
{% endblock %}

View file

@ -0,0 +1,57 @@
<!-- FIXME: SFLC specific content -->
<!-- Copyright (C) 2008 Bradley M. Kuhn <bkuhn@ebb.org> -->
<!-- Permission is granted to copy, modify, redistribute, propagate, -->
<!-- and/or convey this template in any form. -->
{% extends "podjango/base_podcast.html" %}
{% block subtitle %}{{ object.title|striptags }} - Free as in Freedom - {% endblock %}
{% block content %}
<h1><a href="{% url 'podjango:feed-ogg' %}"
class="feedlink"><img src="/img/cast/rss-audioogg.png"
alt="[Ogg/Vorbis Audio RSS]"/></a>
<a href="{% url 'podjango:feed-mp3' %}"
class="feedlink"><img src="/img/cast/rss-audiomp3.png"
alt="[MP3 Audio RSS]"/></a>
Free as in Freedom</h1>
<h2><a class="feedlink" href="{{ object.ogg_path }}"><img alt="[Get cast in Ogg/Vorbis
format]" src="/img/cast/audio_ogg_button.png"/></a>
<a class="feedlink" href="{{ object.mp3_path }}"><img alt="[Get cast in MP3 format]"
src="/img/cast/audio_mp3_button.png"/></a>
{{ object.title|safe }}</h2>
<p class="date">{{ object.pub_date|date:"j F Y" }}</p>
<h3>Download</h3>
<a href="{{ object.ogg_path }}" class="document_format">ogg</a><a href="{{ object.mp3_path }}" class="document_format">mp3</a>
<h3>Summary</h3>
{{ object.summary|safe }}
<p>
This show was released on {{ object.pub_date|date:"l j F Y" }}; its
running time is {{ object.duration}}.</p>
<h3>Show Notes</h3>
{{ object.body|safe }}
<br/>
<hr width="80%"/>
<p>Send feedback and comments on the oggcast
to <a href="mailto:oggcast@faif.us">&lt;oggcast@faif.us&gt;</a>.
You can keep in touch with <a href="http://faif.us">Free as in Freedom</a>
on our IRC channel, #faif on irc.freenode.net, and
by <a href="http://identi.ca/faif">following @faif on identi.ca</a>.</p>
{% if object.tags.all %}<p class="blog-tags">Tags: {% for tag in object.tags.all %}<a href="{{ tag.get_absolute_url }}">{{ tag.label }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}</p>{% endif %}
<p><span class="continued"><a href="{% url 'podjango:cast' }">Other FaiF oggcasts&hellip;</a></span></p>
{% endblock %}

View file

@ -0,0 +1,62 @@
<!-- FIXME: SFLC specific content -->
<!-- Copyright (C) 2008 Bradley M. Kuhn <bkuhn@ebb.org> -->
<!-- Permission is granted to copy, modify, redistribute, propagate, -->
<!-- and/or convey this template in any form. -->
{% extends "podjango/base_podcast.html" %}
{% block subtitle %}Free as in Freedom - {% endblock %}
{% block content %}
<h1><a href="{% url 'podjango:feed-ogg' %}"
class="feedlink"><img src="/img/cast/rss-audioogg.png"
alt="[Ogg/Vorbis Audio RSS]"/></a>
<a href="{% url 'podjango:feed-mp3' %}"
class="feedlink"><img src="/img/cast/rss-audiomp3.png"
alt="[MP3 Audio RSS]"/></a>
Free as in Freedom</h1>
{% if tags %}
<p>Displaying casts
tagged {% for tag in tags %}{% if not forloop.last %}{% if not forloop.first %}, {% endif %}{% endif %}<a href="{{ tag.get_absolute_url }}">{{ tag.label }}</a>{% ifequal forloop.revcounter 2 %} or {% endifequal %}{% endfor %}
</p>
{% endif %}
{% for object in object_list %}
<div class="shaded">
<p class="date">{{ object.pub_date|date:"F j, Y" }}</p>
<h3>
<a class="feedlink" href="{{ object.ogg_path }}"><img alt="[Get cast in Ogg/Vorbis
format]" src="/img/cast/audio_ogg_button.png"/></a>
<a class="feedlink" href="{{ object.mp3_path }}"><img alt="[Get cast in MP3 format]"
src="/img/cast/audio_mp3_button.png"/></a>
<a href="{{ object.get_absolute_url }}">{{ object.title|safe }}</a></h3>
<h4>Summary</h4>
{{ object.summary|safe }}
<p>
This show was released on {{ object.pub_date|date:"l j F Y" }}; its
running time is {{ object.duration}}.</p>
<h4>Show Notes</h4>
{{ object.body|safe }}
<br/>
{% if object.tags.all %}<p class="cast-tags small">Tags: {% for tag in object.tags.all %}<a href="{{ tag.get_absolute_url }}">{{ tag.label }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}</p>{% endif %}
</div>
{% endfor %}
<p>
{% if has_next %}<a class="next_page_button" href="?page={{ next }}{% if query_string %}&amp;{{ query_string|escape }}{% endif %}">Next page (older) &raquo;</a>{% endif %}
{% if has_previous %}<a href="?page={{ previous }}{% if query_string %}&amp;{{ query_string|escape }}{% endif %}">&laquo; Previous page (newer)</a>{% endif %}
</p>
<div class="clear"></div>
{% if date_list %}
<h3>Index by date</h3>
<ul>
{% for year in date_list %}<li><a href="{{ year|date:"Y" }}/">{{ year|date:"Y" }}</a></li>{% endfor %}
</ul>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,18 @@
{% extends "base_standard.html" %}
{% block subtitle %}News Feeds - {% endblock %}
{% block content %}
<h1>Feeds Available at faif.us</h1>
<p>All feeds are RSS 2.0.</p>
<ul>
{% for feed in feeds %}
<li><a href="{{ feed.get_absolute_url }}">{{ feed.title }}</a> &mdash;
{{ feed.description|safe }}</li>
{% endfor %}
</ul>
{% endblock %}

View file

@ -0,0 +1,16 @@
<!-- FIXME: SFLC specific content -->
<!-- Copyright (C) 2008 Bradley M. Kuhn <bkuhn@ebb.org> -->
<!-- Permission is granted to copy, modify, redistribute, propagate,
-- and/or convey this template in any form. -->
{% ifequal obj.omnibus_type "news" %}
<p><i>A news item from SFLC.</i></p>
{% endifequal %}
{% ifequal obj.omnibus_type "podcast" %}
<p><i>An episode of the <a href="http://{{ site }}/podcast/">Software Freedom Law Show</a>.</i></p>
{% endifequal %}
{% ifequal obj.omnibus_type "event" %}
<p><i>An <a href="http://{{ site }}/events/">upcoming event</a> related to the SFLC .</i></p>
{% endifequal %}
{% include obj.omnibus_feed_description_template %}

View file

@ -0,0 +1,5 @@
<!-- Copyright (C) 2008 Bradley M. Kuhn <bkuhn@ebb.org> -->
<!-- Permission is granted to copy, modify, redistribute, propagate,
-- and/or convey this template in any form. -->
{% include obj.omnibus_feed_title_template %}

View file

@ -0,0 +1,38 @@
<p>
<a href="http://faif.us{{ obj.ogg_path }}"><img alt="[Direct download of cast in Ogg/Vorbis
format]" src="http://faif.us/img/cast/audio_ogg_button.png"/></a>
<a href="http://faif.us{{ obj.mp3_path }}"><img alt="[Direct download of cast in MP3 format]"
src="http://faif.us/img/cast/audio_mp3_button.png"/></a>
</p>
<p>
{{ obj.summary|safe }}
</p>
<h3>Show Notes:</h3>
{{ obj.body|safe }}
<hr width="80%"/>
<p>Send feedback and comments on the cast
to <a href="mailto:cast@faif.us">&lt;oggcast@faif.us&gt;</a>.
You can keep in touch with <a href="http://faif.us">Free as in Freedom</a> on our IRC channel, #faif on irc.freenode.net, and
by <a href="http://identi.ca/conservancy">following Conservancy on
identi.ca</a> and <a href="https://twitter.com/conservancy">and Twitter</a>.</p>
<p>Free as in Freedom is produced by <a href="http://danlynch.org/blog/">Dan Lynch</a>
of <a href="http://danlynch.org/">danlynch.org</a>.
Theme
music written and performed
by <a href="http://www.miketarantino.com">Mike Tarantino</a>
with <a href="http://www.charliepaxson.com">Charlie Paxson</a> on drums.</p>
<p><a rel="license"
href="https://creativecommons.org/licenses/by-sa/4.0/"><img alt="Creative Commons License" style="border-width:0"
src="http://i.creativecommons.org/l/by-sa/4.0/88x31.png" hspace=10 /></a>
The content
of <span xmlns:dc="http://purl.org/dc/elements/1.1/" href="http://purl.org/dc/dcmitype/Sound" rel="dc:type">this
audcast</span>, and the accompanying show notes and music are licensed
under the <a rel="license"
href="https://creativecommons.org/licenses/by-sa/4.0/">Creative
Commons Attribution-Share-Alike 4.0 license (CC BY-SA 4.0)</a>.
</p>

View file

@ -0,0 +1 @@
{{ obj.title|striptags|safe }}

View file

@ -0,0 +1,59 @@
{% extends "podjango/base_podcast.html" %}
{% block content %}
<div class="singleColumn">
<!-- FIXME: more hard coding here for FaiF -->
<h1>Free as in Freedom</h1>
<p>Free as in Freedom is a bi-weekly oggcast, hosted and presented by
<a href="http://ebb.org/bkuhn">Bradley M. Kuhn</a> and <a href="http://gnomg.org">Karen Sandler</a>.
The discussion includes legal, policy, and many other issues in the Free, Libre,
and Open Source Software (FLOSS) world. Occasionally, guests join
Bradley and Karen to discuss various topics regarding FLOSS.</p>
<p>You can email feedback on the show
to <a href="mailto:oggcast@faif.us">&lt;oggcast@faif.us&gt;</a>, or join
bkuhn and other listeners in our IRC channel, #faif on
irc.freenode.net.</p>
<h2>Follow FaiF's RSS, and Other Feeds</h2>
<p>There is RSS for both <a href="{% url 'podjango:feed-ogg' %}">ogg format</a>
and <a href="{% url 'podjango:feed-mp3' %}">mp3 format</a>. These links <em>might</em>
work if you want to <a href="itpc://faif.us{% url 'podjango:feed-mp3' %}">subscribe to the show</a> <a href="https://itunes.apple.com/us/podcast/free-as-in-freedom/id450458894">with proprietary Apple devices.</a> </p>
<p>If you're interested you can
follow <a href="http://twitter.com/o0karen0o">Karen</a>,
and <a href="http://twitter.com/faifcast">the podcast itself</a>
on Twitter.
Also, <a href="http://ebb.org/bkuhn/blog">Bradley</a>
and <a href="http://gnomg.org">Karen</a> both have blogs you can
read.</p>
<h2>
<a href="{% url 'podjango:feed-ogg' %}"
class="feedlink"><img src="/img/cast/rss-audioogg.png"
alt="[Ogg/Vorbis Audio RSS]"/></a>
<a href="{% url 'podjango:feed-mp3' %}"
class="feedlink"><img src="/img/cast/rss-audiomp3.png"
alt="[MP3 Audio
RSS]"/></a><a href="{% url 'podjango:cast' }">Recent Free as in Freedom Shows</a></h2>
{% for cc in cast %}
<div class="shaded">
<a class="feedlink" href="{{ cc.ogg_path }}"><img alt="[Get cast in Ogg/Vorbis
format]" src="/img/cast/audio_ogg_button.png"/></a>
<a class="feedlink" href="{{ cc.mp3_path }}"><img alt="[Get cast in MP3 format]"
src="/img/cast/audio_mp3_button.png"/></a>
<p class="date">{{ cc.pub_date|date:"F j, Y" }}</p>
<h3><a href="{{ cc.get_absolute_url }}">{{ cc.title|safe }}</a></h3>
{{ cc.summary|safe }}
</div>
{% endfor %}
<p><a href="{% url 'podjango:cast' }">All oggcasts&hellip;</a></p>
</div>
{% endblock %}

43
www/podjango/urls.py Normal file
View file

@ -0,0 +1,43 @@
# Copyright 2010 Bradley M. Kuhn <bkuhn@ebb.org>
# Copyright 2005-2008 James Garrison
# This software's license gives you freedom; you can copy, convey,
# propagate, redistribute, modify and/or redistribute modified versions of
# this program under the terms of the GNU Affero General Public License
# (AGPL) as published by the Free Software Foundation (FSF), either
# version 3 of the License, or (at your option) any later version of the
# AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
from django.conf import settings
from django.conf.urls import url, include
from django.contrib import admin
from django.contrib.syndication.views import Feed
from podjango.feeds import feed_dict, view, OggCastFeed
from podjango import frontpage
#handler404 = 'modpythoncustom.view404'
admin.autodiscover()
app_name = 'podjango'
urlpatterns = [
url(r'^$', frontpage.view),
url(r'^cast/?', include('podjango.apps.cast.urls')),
url(r'^feeds/cast-ogg/?$', OggCastFeed(), name='feed-ogg'),
url(r'^feeds/cast-mp3/?$', OggCastFeed(), name='feed-mp3'),
url(r'^feeds/$', view, name='feeds'),
]
if settings.DEBUG:
from django.conf.urls.static import static
urlpatterns += static('/', document_root='podjango/static')