Changeset - c0acfde9c303
[Not reviewed]
0 2 41
Ben Sturmfels (bsturmfels) - 7 months ago 2023-10-05 08:26:42
ben@sturm.com.au
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.
43 files changed with 1438 insertions and 0 deletions:
0 comments (0 inline, 0 general)
www/conservancy/settings.py
Show inline comments
...
 
@@ -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'
www/conservancy/urls.py
Show inline comments
...
 
@@ -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')),
 
]
www/podjango/__init__.py
Show inline comments
 
new file 100644
www/podjango/apps/__init__.py
Show inline comments
 
new file 100644
www/podjango/apps/cast/__init__.py
Show inline comments
 
new file 100644
www/podjango/apps/cast/admin.py
Show inline comments
 
new file 100644
 
#  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)
www/podjango/apps/cast/migrations/0001_initial.py
Show inline comments
 
new file 100644
 
# 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',
 
            },
 
        ),
 
    ]
www/podjango/apps/cast/migrations/__init__.py
Show inline comments
 
new file 100644
www/podjango/apps/cast/models.py
Show inline comments
 
new file 100644
 
#  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?
www/podjango/apps/cast/templatetags/__init__.py
Show inline comments
 
new file 100644
www/podjango/apps/cast/templatetags/date_within.py
Show inline comments
 
new file 100644
 
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)))
www/podjango/apps/cast/urls.py
Show inline comments
 
new file 100644
 
#  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
www/podjango/apps/cast/views.py
Show inline comments
 
new file 100644
 
#  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)
www/podjango/feeds.py
Show inline comments
 
new file 100644
 
#  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})
www/podjango/frontpage.py
Show inline comments
 
new file 100644
 
# 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)
www/podjango/static/img/cast/audio_mp3_button.png
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
www/podjango/static/img/cast/audio_ogg_button.png
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
www/podjango/static/img/cast/faif_144x144.jpg
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
www/podjango/static/img/cast/faif_200x200.jpg
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
www/podjango/static/img/cast/faif_300x300.jpg
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
www/podjango/static/img/cast/faif_300x300.xcf
Show inline comments
 
new file 100644
 
binary diff not shown
www/podjango/static/img/cast/halfbakedmedia-logo.png
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
www/podjango/static/img/cast/halfbakedmedia-logo_100px.png
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
www/podjango/static/img/cast/rss-audiomp3.png
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
www/podjango/static/img/cast/rss-audioogg.png
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
www/podjango/static/img/cast/rss-miro.png
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
www/podjango/static/img/feed-icon-14x14.png
Show inline comments
 
new file 100644
 
binary diff not shown
Show images
www/podjango/static/license/index.html
Show inline comments
 
new file 100644
 
{% 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 %}
www/podjango/static/podjango.css
Show inline comments
 
new file 100644
 
* { 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;
 
}
...
 
\ No newline at end of file
www/podjango/templates/podjango/base_podcast.html
Show inline comments
 
new file 100644
 
<!-- 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 %}
www/podjango/templates/podjango/base_standard.html
Show inline comments
 
new file 100644
 
<!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>
www/podjango/templates/podjango/cast/cast_archive_day.html
Show inline comments
 
new file 100644
 
<!-- 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 %}
www/podjango/templates/podjango/cast/cast_archive_month.html
Show inline comments
 
new file 100644
 
<!-- 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 %}
www/podjango/templates/podjango/cast/cast_archive_year.html
Show inline comments
 
new file 100644
 
<!-- 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 %}
www/podjango/templates/podjango/cast/cast_detail.html
Show inline comments
 
new file 100644
 
<!-- 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 %}
www/podjango/templates/podjango/cast/cast_list.html
Show inline comments
 
new file 100644
 
<!-- 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 %}
www/podjango/templates/podjango/feeds.html
Show inline comments
 
new file 100644
 
{% 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 %}
www/podjango/templates/podjango/feeds/omnibus_description.html
Show inline comments
 
new file 100644
 
<!-- 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  %}
www/podjango/templates/podjango/feeds/omnibus_title.html
Show inline comments
 
new file 100644
 
<!--  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  %}
www/podjango/templates/podjango/feeds/podcast_description.html
Show inline comments
 
new file 100644
 
<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>
www/podjango/templates/podjango/feeds/podcast_title.html
Show inline comments
 
new file 100644
 
{{ obj.title|striptags|safe }}
www/podjango/templates/podjango/frontpage.html
Show inline comments
 
new file 100644
 
{% 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 %}
www/podjango/urls.py
Show inline comments
 
new file 100644
 
# 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')
0 comments (0 inline, 0 general)