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.
|
@ -97,6 +97,8 @@ INSTALLED_APPS = [
|
||||||
'conservancy.apps.fundgoal',
|
'conservancy.apps.fundgoal',
|
||||||
'conservancy.apps.assignment',
|
'conservancy.apps.assignment',
|
||||||
'conservancy.apps.fossy',
|
'conservancy.apps.fossy',
|
||||||
|
'podjango', # Here so that the templates are found
|
||||||
|
'podjango.apps.cast',
|
||||||
]
|
]
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||||
|
|
|
@ -67,4 +67,5 @@ urlpatterns = [
|
||||||
url(r'^assignment/', include('conservancy.apps.assignment.urls')),
|
url(r'^assignment/', include('conservancy.apps.assignment.urls')),
|
||||||
url(r'^fossy/$', static_views.index),
|
url(r'^fossy/$', static_views.index),
|
||||||
url(r'^fossy/', include('conservancy.apps.fossy.urls')),
|
url(r'^fossy/', include('conservancy.apps.fossy.urls')),
|
||||||
|
url(r'^faif/', include('podjango.urls')),
|
||||||
]
|
]
|
||||||
|
|
0
www/podjango/__init__.py
Normal file
0
www/podjango/apps/__init__.py
Normal file
0
www/podjango/apps/cast/__init__.py
Normal file
37
www/podjango/apps/cast/admin.py
Normal 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)
|
50
www/podjango/apps/cast/migrations/0001_initial.py
Normal 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',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
0
www/podjango/apps/cast/migrations/__init__.py
Normal file
76
www/podjango/apps/cast/models.py
Normal 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?
|
0
www/podjango/apps/cast/templatetags/__init__.py
Normal file
9
www/podjango/apps/cast/templatetags/date_within.py
Normal 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)))
|
82
www/podjango/apps/cast/urls.py
Normal 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
|
110
www/podjango/apps/cast/views.py
Normal 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
|
@ -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
|
@ -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)
|
BIN
www/podjango/static/img/cast/audio_mp3_button.png
Normal file
After Width: | Height: | Size: 631 B |
BIN
www/podjango/static/img/cast/audio_ogg_button.png
Normal file
After Width: | Height: | Size: 574 B |
BIN
www/podjango/static/img/cast/faif_144x144.jpg
Normal file
After Width: | Height: | Size: 6 KiB |
BIN
www/podjango/static/img/cast/faif_200x200.jpg
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
www/podjango/static/img/cast/faif_300x300.jpg
Normal file
After Width: | Height: | Size: 8.2 KiB |
BIN
www/podjango/static/img/cast/faif_300x300.xcf
Normal file
BIN
www/podjango/static/img/cast/halfbakedmedia-logo.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
www/podjango/static/img/cast/halfbakedmedia-logo_100px.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
www/podjango/static/img/cast/rss-audiomp3.png
Normal file
After Width: | Height: | Size: 661 B |
BIN
www/podjango/static/img/cast/rss-audioogg.png
Normal file
After Width: | Height: | Size: 643 B |
BIN
www/podjango/static/img/cast/rss-miro.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
www/podjango/static/img/feed-icon-14x14.png
Normal file
After Width: | Height: | Size: 689 B |
50
www/podjango/static/license/index.html
Normal 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 © 2007-2008,
|
||||||
|
Software Freedom Law Center, Inc. and copyrighted © 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 © 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
|
||||||
|
© 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 %}
|
241
www/podjango/static/podjango.css
Normal 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;
|
||||||
|
}
|
26
www/podjango/templates/podjango/base_podcast.html
Normal 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…</a></p>
|
||||||
|
<!-- Query is not working right now! -->
|
||||||
|
<!-- <p><a href="/podcast/query/">Query...</a></p> -->
|
||||||
|
|
||||||
|
{% endblock %}
|
48
www/podjango/templates/podjango/base_standard.html
Normal 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>
|
34
www/podjango/templates/podjango/cast/cast_archive_day.html
Normal 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 %}
|
34
www/podjango/templates/podjango/cast/cast_archive_month.html
Normal 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 %}
|
28
www/podjango/templates/podjango/cast/cast_archive_year.html
Normal 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 %}
|
57
www/podjango/templates/podjango/cast/cast_detail.html
Normal 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"><oggcast@faif.us></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…</a></span></p>
|
||||||
|
|
||||||
|
{% endblock %}
|
62
www/podjango/templates/podjango/cast/cast_list.html
Normal 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 %}&{{ query_string|escape }}{% endif %}">Next page (older) »</a>{% endif %}
|
||||||
|
{% if has_previous %}<a href="?page={{ previous }}{% if query_string %}&{{ query_string|escape }}{% endif %}">« 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 %}
|
18
www/podjango/templates/podjango/feeds.html
Normal 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> —
|
||||||
|
{{ feed.description|safe }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -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 %}
|
5
www/podjango/templates/podjango/feeds/omnibus_title.html
Normal 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 %}
|
|
@ -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"><oggcast@faif.us></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>
|
1
www/podjango/templates/podjango/feeds/podcast_title.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{{ obj.title|striptags|safe }}
|
59
www/podjango/templates/podjango/frontpage.html
Normal 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"><oggcast@faif.us></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…</a></p>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
43
www/podjango/urls.py
Normal 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')
|