jdriscoll / django-photolog

Photo galleries for the Django web framework.

Clone this repository (size: 16.2 KB): HTTPS / SSH
$ hg clone http://hg.driscolldev.com/django-photolog

Changed (Δ24.4 KB):

raw changeset »

.hgignore (6 lines added, 0 lines removed)

LICENSE.txt (27 lines added, 0 lines removed)

MANIFEST.in (3 lines added, 0 lines removed)

photolog/__init__.py (1 lines added, 0 lines removed)

photolog/admin.py (23 lines added, 0 lines removed)

photolog/models.py (220 lines added, 0 lines removed)

photolog/specs.py (29 lines added, 0 lines removed)

photolog/templates/photolog/gallery_archive.html (26 lines added, 0 lines removed)

photolog/templates/photolog/gallery_archive_day.html (26 lines added, 0 lines removed)

photolog/templates/photolog/gallery_archive_month.html (26 lines added, 0 lines removed)

photolog/templates/photolog/gallery_archive_year.html (16 lines added, 0 lines removed)

photolog/templates/photolog/gallery_detail.html (19 lines added, 0 lines removed)

photolog/templates/photolog/gallery_list.html (31 lines added, 0 lines removed)

photolog/templates/photolog/photo_archive.html (20 lines added, 0 lines removed)

photolog/templates/photolog/photo_archive_day.html (20 lines added, 0 lines removed)

photolog/templates/photolog/photo_archive_month.html (20 lines added, 0 lines removed)

photolog/templates/photolog/photo_archive_year.html (14 lines added, 0 lines removed)

photolog/templates/photolog/photo_detail.html (23 lines added, 0 lines removed)

photolog/templates/photolog/photo_list.html (26 lines added, 0 lines removed)

photolog/templates/photolog/root.html (1 lines added, 0 lines removed)

photolog/templates/photolog/tags/next_in_gallery.html (3 lines added, 0 lines removed)

photolog/templates/photolog/tags/prev_in_gallery.html (3 lines added, 0 lines removed)

photolog/templatetags/__init__.py (null-size change)

photolog/templatetags/photolog_tags.py (11 lines added, 0 lines removed)

photolog/urls.py (36 lines added, 0 lines removed)

setup.py (40 lines added, 0 lines removed)

Up to file-list .hgignore:

1
syntax: glob
2
3
*.pyc
4
*.bak
5
*.orig
6
.svn

Up to file-list LICENSE.txt:

1
Copyright (c) 2007-2008, Justin C. Driscoll
2
All rights reserved.
3
4
Redistribution and use in source and binary forms, with or without modification,
5
are permitted provided that the following conditions are met:
6
7
    1. Redistributions of source code must retain the above copyright notice,
8
       this list of conditions and the following disclaimer.
9
10
    2. Redistributions in binary form must reproduce the above copyright
11
       notice, this list of conditions and the following disclaimer in the
12
       documentation and/or other materials provided with the distribution.
13
14
    3. Neither the name of django-photolog nor the names of its contributors may be used
15
       to endorse or promote products derived from this software without
16
       specific prior written permission.
17
18
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Up to file-list MANIFEST.in:

1
recursive-include photolog/locale *
2
recursive-include photolog/templates/photolog *.html
3
global-exclude *.pyc

Up to file-list photolog/__init__.py:

1
VERSION = (3, 0, 'b')

Up to file-list photolog/admin.py:

1
""" Newforms Admin configuration for Photologue
2
3
"""
4
from django.contrib import admin
5
from models import *
6
7
class GalleryAdmin(admin.ModelAdmin):
8
    list_display = ('title', 'date_added', 'photo_count', 'is_public')
9
    list_filter = ['date_added', 'is_public']
10
    date_hierarchy = 'date_added'
11
    prepopulated_fields = {'title_slug': ('title',)}
12
    filter_horizontal = ('photos',)
13
14
class PhotoAdmin(admin.ModelAdmin):
15
    list_display = ('title', 'date_added', 'is_public', 'view_count', 'admin_thumbnail_view')
16
    list_filter = ['date_added', 'is_public']
17
    list_per_page = 10
18
    prepopulated_fields = {'title_slug': ('title',)}
19
20
21
admin.site.register(Gallery, GalleryAdmin)
22
admin.site.register(GalleryUpload)
23
admin.site.register(Photo, PhotoAdmin)

Up to file-list photolog/models.py:

1
import os
2
import random
3
import zipfile
4
5
from datetime import datetime
6
from django.conf import settings
7
from django.core.files.base import ContentFile
8
from django.core.files.storage import FileSystemStorage
9
from django.core.urlresolvers import reverse
10
from django.db import models
11
from django.template.defaultfilters import slugify
12
from django.utils.translation import ugettext_lazy as _
13
14
from imagekit.models import ImageModel
15
from imagekit.lib import Image
16
17
18
# Allow user to specify their own spec module in their Django setting file
19
PHOTOLOG_SPEC_MODULE = getattr(settings, 'PHOTOLOG_SPEC_MODULE', 'photolog.specs')
20
21
22
class Gallery(models.Model):
23
    date_added = models.DateTimeField(_('date published'), default=datetime.now)
24
    title = models.CharField(_('title'), max_length=100, unique=True)
25
    title_slug = models.SlugField(_('title slug'), unique=True,
26
                                  help_text=_('A "slug" is a unique URL-friendly title for an object.'))
27
    description = models.TextField(_('description'), blank=True)
28
    is_public = models.BooleanField(_('is public'), default=True,
29
                                    help_text=_('Public galleries will be displayed in the default views.'))
30
    photos = models.ManyToManyField('Photo', related_name='galleries', verbose_name=_('photos'),
31
                                    null=True, blank=True)
32
33
    class Meta:
34
        ordering = ['-date_added']
35
        get_latest_by = 'date_added'
36
        verbose_name = _('gallery')
37
        verbose_name_plural = _('galleries')
38
39
    def __unicode__(self):
40
        return self.title
41
42
    def __str__(self):
43
        return self.__unicode__()
44
45
    def get_absolute_url(self):
46
        return reverse('pl-gallery', args=[self.title_slug])
47
48
    def latest(self, limit=0, public=True):
49
        if limit == 0:
50
            limit = self.photo_count()
51
        if public:
52
            return self.public()[:limit]
53
        else:
54
            return self.photos.all()[:limit]
55
56
    def sample(self, count=0, public=True):
57
        if count == 0 or count > self.photo_count():
58
            count = self.photo_count()
59
        if public:
60
            photo_set = self.public()
61
        else:
62
            photo_set = self.photos.all()
63
        return random.sample(photo_set, count)
64
65
    def photo_count(self, public=True):
66
        if public:
67
            return self.public().count()
68
        else:
69
            return self.photos.all().count()
70
    photo_count.short_description = _('count')
71
72
    def public(self):
73
        return self.photos.filter(is_public=True)
74
75
76
class GalleryUpload(models.Model):
77
    zip_file = models.FileField(_('images file (.zip)'),
78
                                upload_to='photolog/tmp',
79
                                storage=FileSystemStorage(),
80
                                help_text=_('Select a .zip file of images to upload into a new Gallery.'))
81
    gallery = models.ForeignKey(Gallery, null=True, blank=True, help_text=_('Select a gallery to add these images to. leave this empty to create a new gallery from the supplied title.'))
82
    title = models.CharField(_('title'), max_length=75, help_text=_('All photos in the gallery will be given a title made up of the gallery title + a sequential number.'))
83
    caption = models.TextField(_('caption'), blank=True, help_text=_('Caption will be added to all photos.'))
84
    description = models.TextField(_('description'), blank=True, help_text=_('A description of this Gallery.'))
85
    is_public = models.BooleanField(_('is public'), default=True, help_text=_('Uncheck this to make the uploaded gallery and included photographs private.'))
86
87
    class Meta:
88
        verbose_name = _('gallery upload')
89
        verbose_name_plural = _('gallery uploads')
90
91
    def save(self, *args, **kwargs):
92
        super(GalleryUpload, self).save(*args, **kwargs)
93
        gallery = self.process_zipfile()
94
        super(GalleryUpload, self).delete()
95
        return gallery
96
97
    def process_zipfile(self):
98
        if os.path.isfile(self.zip_file.path):
99
            # TODO: implement try-except here
100
            zip = zipfile.ZipFile(self.zip_file.path)
101
            bad_file = zip.testzip()
102
            if bad_file:
103
                raise Exception('"%s" in the .zip archive is corrupt.' % bad_file)
104
            count = 1
105
            if self.gallery:
106
                gallery = self.gallery
107
            else:
108
                gallery = Gallery.objects.create(title=self.title,
109
                                                 title_slug=slugify(self.title),
110
                                                 description=self.description,
111
                                                 is_public=self.is_public)
112
            from cStringIO import StringIO
113
            for filename in zip.namelist():
114
                if filename.startswith('__'): # do not process meta files
115
                    continue
116
                data = zip.read(filename)
117
                if len(data):
118
                    try:
119
                        # the following is taken from django.newforms.fields.ImageField:
120
                        #  load() is the only method that can spot a truncated JPEG,
121
                        #  but it cannot be called sanely after verify()
122
                        trial_image = Image.open(StringIO(data))
123
                        trial_image.load()
124
                        # verify() is the only method that can spot a corrupt PNG,
125
                        #  but it must be called immediately after the constructor
126
                        trial_image = Image.open(StringIO(data))
127
                        trial_image.verify()
128
                    except Exception, e:
129
                        # if a "bad" file is found we just skip it.
130
                        raise e
131
                        continue
132
                    while 1:
133
                        title = ' '.join([self.title, str(count)])
134
                        slug = slugify(title)
135
                        try:
136
                            p = Photo.objects.get(title_slug=slug)
137
                        except Photo.DoesNotExist:
138
                            photo = Photo(title=title,
139
                                          title_slug=slug,
140
                                          caption=self.caption,
141
                                          is_public=self.is_public)
142
                            photo.image.save(filename, ContentFile(data))
143
                            gallery.photos.add(photo)
144
                            count = count + 1
145
                            break
146
                        count = count + 1
147
            zip.close()
148
            return gallery
149
150
151
class Photo(ImageModel):
152
    crop_horz_choices = (
153
        (0, 'left'),
154
        (1, 'center'),
155
        (2, 'right'),
156
    )
157
    crop_vert_choices = (
158
        (0, 'top'),
159
        (1, 'center'),
160
        (2, 'bottom'),
161
    )
162
    image = models.ImageField(_('image'), upload_to='photolog')
163
    crop_horz = models.PositiveIntegerField(_('crop horizontal'),
164
                                            choices=crop_horz_choices,
165
                                            default=1)
166
    crop_vert = models.PositiveIntegerField(_('crop vertical'),
167
                                            choices=crop_vert_choices,
168
                                            default=1)
169
    title = models.CharField(_('title'), max_length=100, unique=True)
170
    title_slug = models.SlugField(_('slug'), unique=True,
171
                                  help_text=('A "slug" is a unique URL-friendly title for an object.'))
172
    caption = models.TextField(_('caption'), blank=True)
173
    date_added = models.DateTimeField(_('date added'), default=datetime.now, editable=False)
174
    view_count = models.PositiveIntegerField(default=0, editable=False)
175
    is_public = models.BooleanField(_('is public'), default=True, help_text=_('Public photographs will be displayed in the default views.'))
176
177
    class Meta:
178
        ordering = ['-date_added']
179
        get_latest_by = 'date_added'
180
        verbose_name = _("photo")
181
        verbose_name_plural = _("photos")
182
        
183
    class IKOptions:
184
        spec_module = PHOTOLOG_SPEC_MODULE
185
        save_count_as = 'view_count'
186
        cache_dir = 'photolog'
187
        cache_filename_format = "%(specname)s/%(filename)s.%(extension)s"
188
189
    def __unicode__(self):
190
        return self.title
191
192
    def __str__(self):
193
        return self.__unicode__()
194
195
    def save(self, *args, **kwargs):
196
        if self.title_slug is None:
197
            self.title_slug = slugify(self.title)
198
        super(Photo, self).save(*args, **kwargs)
199
200
    def get_absolute_url(self):
201
        return reverse('pl-photo', args=[self.title_slug])
202
203
    def public_galleries(self):
204
        """Return the public galleries to which this photo belongs."""
205
        return self.galleries.filter(is_public=True)
206
207
    def get_previous_in_gallery(self, gallery):
208
        try:
209
            return self.get_previous_by_date_added(galleries__exact=gallery,
210
                                                   is_public=True)
211
        except Photo.DoesNotExist:
212
            return None
213
214
    def get_next_in_gallery(self, gallery):
215
        try:
216
            return self.get_next_by_date_added(galleries__exact=gallery,
217
                                               is_public=True)
218
        except Photo.DoesNotExist:
219
            return None
220

Up to file-list photolog/specs.py:

1
""" Default Photologue image specifications """
2
3
from imagekit.specs import ImageSpec
4
from imagekit import processors
5
    
6
7
class ResizeThumbnail(processors.Resize):
8
    width = 100
9
    height = 75
10
    crop = True
11
    
12
class ResizeDisplay(processors.Resize):
13
    width = 600
14
    
15
class EnhanceSmall(processors.Adjustment):
16
    contrast = 1.2
17
    sharpness = 1.1
18
    
19
class AdminThumbnail(ImageSpec):
20
    access_as = 'admin_thumbnail'
21
    processors = [ResizeThumbnail, EnhanceSmall]
22
23
class Display(ImageSpec):
24
    increment_count = True
25
    processors = [ResizeDisplay]
26
        
27
class Thumbnail(ImageSpec):
28
    processors = [ResizeThumbnail, EnhanceSmall]
29
    pre_cache = True

Up to file-list photolog/templates/photolog/gallery_archive.html:

1
{% extends "photolog/root.html" %}
2
3
{% block title %}Latest Photo Galleries{% endblock %}
4
5
{% block content %}
6
7
<h1>Latest Photo Galleries</h1>
8
9
{% if latest %}
10
    {% for gallery in latest %}
11
    <div class="photo-gallery">
12
        <h2><a href="{{ gallery.get_absolute_url }}">{{ gallery.title }}</a></h2>
13
        {% for photo in gallery.sample|slice:sample_size %}
14
        <div class="gallery-photo">
15
            <a href="{{ photo.get_absolute_url }}"><img src="{{ photo.thumbnail.url }}" alt="{{ photo.title }}"/></a>
16
        </div>
17
        {% endfor %}
18
    </div>
19
    {% endfor %}
20
{% else %}
21
    <p>No galleries were found.</p>
22
{% endif %}
23
24
<p><a href="{% url pl-gallery-list 1 %}">View all galleries.</a></p>
25
26
{% endblock %}

Up to file-list photolog/templates/photolog/gallery_archive_day.html:

1
{% extends "photolog/root.html" %}
2
3
{% block title %}Galleries for {{ day|date }}{% endblock %}
4
5
{% block content %}
6
7
<h1>Galleries for {{ day|date }}</h1>
8
9
{% if object_list %}
10
    {% for gallery in object_list %}
11
    <div class="photo-gallery">
12
        <h2>{{ gallery.title }}</h2>
13
        {% for photo in gallery.sample|slice:sample_size %}
14
        <div class="gallery-photo">
15
            <a href="{{ photo.get_absolute_url }}"><img src="{{ photo.thumbnail.url }}" alt="{{ photo.title }}"/></a>
16
        </div>
17
        {% endfor %}
18
    </div>
19
    {% endfor %}
20
{% else %}
21
    <p>No galleries were found.</p>
22
{% endif %}
23
24
<p><a href="{% url pl-gallery-list 1 %}">View all galleries.</a></p>
25
26
{% endblock %}

Up to file-list photolog/templates/photolog/gallery_archive_month.html:

1
{% extends "photolog/root.html" %}
2
3
{% block title %}Galleries for {{ month|date:"F Y" }}{% endblock %}
4
5
{% block content %}
6
7
<h1>Galleries for {{ month|date:"F Y" }}</h1>
8
9
{% if object_list %}
10
    {% for gallery in object_list %}
11
    <div class="photo-gallery">
12
        <h2>{{ gallery.title }}</h2>
13
        {% for photo in gallery.sample|slice:sample_size %}
14
        <div class="gallery-photo">
15
            <a href="{{ photo.get_absolute_url }}"><img src="{{ photo.thumbnail.url }}" alt="{{ photo.title }}"/></a>
16
        </div>
17
        {% endfor %}
18
    </div>
19
    {% endfor %}
20
{% else %}
21
    <p>No galleries were found.</p>
22
{% endif %}
23
24
<p><a href="{% url pl-gallery-list 1 %}">View all galleries.</a></p>
25
26
{% endblock %}

Up to file-list photolog/templates/photolog/gallery_archive_year.html:

1
{% extends "photolog/root.html" %}
2
3
{% block title %}Galleries for {{ year }}{% endblock %}
4
5
{% block content %}
6
7
<h1>Galleries for {{ year }}</h1>
8
<ul>
9
{% for date in date_list %}
10
<li><a href="{{ date|date:"M"|lower }}/">{{ date|date:"F" }}</a></li>
11
{% endfor %}
12
</ul>
13
14
<p><a href="{% url pl-gallery-list 1 %}">View all galleries.</a></p>
15
16
{% endblock %}

Up to file-list photolog/templates/photolog/gallery_detail.html:

1
{% extends "photolog/root.html" %}
2
3
{% block title %}{{ object.title }}{% endblock %}
4
5
{% block content %}
6
7
<h1>{{ object.title }}</h1>
8
<h2>Originally published {{ object.date_added|date:"l, F jS, Y" }}</h2>
9
{% if object.description %}<p>{{ object.description }}</p>{% endif %}
10
<div class="photo-gallery">
11
    {% for photo in object.public %}
12
    <div class="gallery-photo">
13
        <a href="{{ photo.get_absolute_url }}"><img src="{{ photo.thumbnail.url }}" alt="{{ photo.title }}"/></a>
14
    </div>
15
    {% endfor %}
16
</div>
17
<p><a href="{% url pl-gallery-list 1 %}">View all galleries</a></p>
18
19
{% endblock %}

Up to file-list photolog/templates/photolog/gallery_list.html:

1
{% extends "photolog/root.html" %}
2
3
{% block title %}All Galleries{% endblock %}
4
5
{% block content %}
6
7
<h1>All galleries</h1>
8
9
{% if object_list %}
10
    {% for gallery in object_list %}
11
    <div class="photo-gallery">
12
        <h2><a href="{{ gallery.get_absolute_url }}">{{ gallery.title }}</a></h2>
13
        {% for photo in gallery.sample|slice:sample_size %}
14
        <div class="gallery-photo">
15
            <a href="{{ photo.get_absolute_url }}"><img src="{{ photo.thumbnail.url }}" alt="{{ photo.title }}"/></a>
16
        </div>
17
        {% endfor %}
18
    </div>
19
    {% endfor %}
20
{% else %}
21
    <p>No galleries were found.</p>
22
{% endif %}
23
24
{% if is_paginated %}
25
<p>{{ hits }} galleries total.</p>
26
<div id="page_controls">
27
    <p>{% if has_previous %}<a href="{% url pl-gallery-list previous %}">Previous</a> | {% endif %} page {{ page }} of {{ pages }} {% if has_next %}| <a href="{% url pl-gallery-list next %}">Next</a>{% endif %}</p>
28
</div>
29
{% endif %}
30
31
{% endblock %}

Up to file-list photolog/templates/photolog/photo_archive.html:

1
{% extends "photolog/root.html" %}
2
3
{% block title %}Latest Photos{% endblock %}
4
5
{% block content %}
6
7
<h1>Latest Photos</h1>
8
9
{% if latest %}
10
    {% for photo in latest %}
11
    <div class="gallery-photo">
12
        <a href="{{ photo.get_absolute_url }}"><img src="{{ photo.thumbnail.url }}" alt="{{ photo.title }}"/></a>
13
    </div>
14
    {% endfor %}
15
{% else %}
16
<p>No photos were found.</p>
17
{% endif %}
18
<p><a href="{% url pl-photo-list 1 %}">View all photographs</a></p>
19
20
{% endblock %}

Up to file-list photolog/templates/photolog/photo_archive_day.html:

1
{% extends "photolog/root.html" %}
2
3
{% block title %}Photos for {{ day|date }}{% endblock %}
4
5
{% block content %}
6
7
<h1>Photos for {{ day|date }}</h1>
8
9
{% if object_list %}
10
    {% for photo in object_list %}
11
    <div class="gallery-photo">
12
        <a href="{{ photo.get_absolute_url }}"><img src="{{ photo.thumbnail.url }}" alt="{{ photo.title }}"/></a>
13
    </div>
14
    {% endfor %}
15
{% else %}
16
<p>No photos were found.</p>
17
{% endif %}
18
<p><a href="{% url pl-photo-list 1 %}">View all photographs</a></p>
19
20
{% endblock %}

Up to file-list photolog/templates/photolog/photo_archive_month.html:

1
{% extends "photolog/root.html" %}
2
3
{% block title %}Photos for {{ month|date:"F Y" }}{% endblock %}
4
5
{% block content %}
6
7
<h1>Photos for {{ month|date:"F Y" }}</h1>
8
9
{% if object_list %}
10
    {% for photo in object_list %}
11
    <div class="gallery-photo">
12
        <a href="{{ photo.get_absolute_url }}"><img src="{{ photo.thumbnail.url }}" alt="{{ photo.title }}"/></a>
13
    </div>
14
    {% endfor %}
15
{% else %}
16
<p>No photos were found.</p>
17
{% endif %}
18
<p><a href="{% url pl-photo-list 1 %}">View all photographs</a></p>
19
20
{% endblock %}

Up to file-list photolog/templates/photolog/photo_archive_year.html:

1
{% extends "photolog/root.html" %}
2
3
{% block title %}Galleries for {{ year }}{% endblock %}
4
5
{% block content %}
6
7
<h1>Photos for {{ year }}</h1>
8
<ul>
9
{% for date in date_list %}
10
<li><a href="{{ date|date:"M"|lower }}/">{{ date|date:"F" }}</a></li>
11
{% endfor %}
12
</ul>
13
14
{% endblock %}

Up to file-list photolog/templates/photolog/photo_detail.html:

1
{% extends "photolog/root.html" %}
2
3
{% load photolog_tags %}
4
5
{% block title %}{{ object.title }}{% endblock %}
6
7
{% block content %}
8
9
<h1>{{ object.title }}</h1>
10
<div class="gallery-photo">
11
    <a href="{{ object.image.url }}"><img src="{{ object.display.url }}" alt="{{ object.title }}"/></a>
12
    {% if object.caption %}<p>{{ object.caption }}</p>{% endif %}
13
</div>
14
{% if object.public_galleries %}
15
<h2>This photo is found in the following galleries:</h2>
16
<ol>
17
{% for gallery in object.public_galleries %}
18
    <li>{%previous_in_gallery object gallery%} <a href="{{ gallery.get_absolute_url }}">{{ gallery.title }}</a> {%next_in_gallery object gallery%}</li>
19
{% endfor %}
20
</ol>
21
{% endif %}
22
23
{% endblock %}

Up to file-list photolog/templates/photolog/photo_list.html:

1
{% extends "photolog/root.html" %}
2
3
{% block title %}All Photos{% endblock %}
4
5
{% block content %}
6
7
<h1>All Photos</h1>
8
9
{% if object_list %}
10
    {% for photo in object_list %}
11
    <div class="gallery-photo">
12
        <a href="{{ photo.get_absolute_url }}"><img src="{{ photo.thumbnail.url }}" alt="{{ photo.title }}"/></a>
13
    </div>
14
    {% endfor %}
15
{% else %}
16
<p>No photos were found.</p>
17
{% endif %}
18
19
{% if is_paginated %}
20
<p>{{ hits }} photos total.</p>
21
<div id="page_controls">
22
    <p>{% if has_previous %}<a href="{% url pl-photo-list previous %}">Previous</a> | {% endif %} page {{ page }} of {{ pages }} {% if has_next %}| <a href="{% url pl-photo-list next %}">Next</a>{% endif %}</p>
23
</div>
24
{% endif %}
25
26
{% endblock %}

Up to file-list photolog/templates/photolog/root.html:

1
{% extends "base.html" %}

Up to file-list photolog/templates/photolog/tags/next_in_gallery.html:

1
{% if photo %}
2
<a title="{{ photo.title }}" href="{{ photo.get_absolute_url }}"><img src="{{ photo.get_thumbnail_url }}"/></a>
3
{% endif %}

Up to file-list photolog/templates/photolog/tags/prev_in_gallery.html:

1
{% if photo %}
2
<a title="{{ photo.title }}" href="{{ photo.get_absolute_url }}"><img src="{{ photo.get_thumbnail_url }}"/></a>
3
{% endif %}

Up to file-list photolog/templatetags/photolog_tags.py:

1
from django import template
2
3
register = template.Library()
4
5
@register.inclusion_tag('photolog/tags/next_in_gallery.html')
6
def next_in_gallery(photo, gallery):
7
    return {'photo': photo.get_next_in_gallery(gallery)}
8
9
@register.inclusion_tag('photolog/tags/prev_in_gallery.html')
10
def previous_in_gallery(photo, gallery):
11
    return {'photo': photo.get_previous_in_gallery(gallery)}

Up to file-list photolog/urls.py:

1
from django.conf import settings
2
from django.conf.urls.defaults import *
3
from models import *
4
5
# Number of random images from the gallery to display.
6
SAMPLE_SIZE = ":%s" % getattr(settings, 'GALLERY_SAMPLE_SIZE', 5)
7
8
# galleries
9
gallery_args = {'date_field': 'date_added', 'allow_empty': True, 'queryset': Gallery.objects.filter(is_public=True), 'extra_context':{'sample_size':SAMPLE_SIZE}}
10
urlpatterns = patterns('django.views.generic.date_based',
11
    url(r'^gallery/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\w{1,2})/(?P<slug>[\-\d\w]+)/$', 'object_detail', {'date_field': 'date_added', 'slug_field': 'title_slug', 'queryset': Gallery.objects.filter(is_public=True), 'extra_context':{'sample_size':SAMPLE_SIZE}}, name='pl-gallery-detail'),
12
    url(r'^gallery/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\w{1,2})/$', 'archive_day', gallery_args, name='pl-gallery-archive-day'),
13
    url(r'^gallery/(?P<year>\d{4})/(?P<month>\d{2})/$', 'archive_month', gallery_args, name='pl-gallery-archive-month'),
14
    url(r'^gallery/(?P<year>\d{4})/$', 'archive_year', gallery_args, name='pl-gallery-archive-year'),
15
    url(r'^gallery/?$', 'archive_index', gallery_args, name='pl-gallery-archive'),
16
)
17
urlpatterns += patterns('django.views.generic.list_detail',
18
    url(r'^gallery/(?P<slug>[\-\d\w]+)/$', 'object_detail', {'slug_field': 'title_slug', 'queryset': Gallery.objects.filter(is_public=True), 'extra_context':{'sample_size':SAMPLE_SIZE}}, name='pl-gallery'),
19
    url(r'^gallery/page/(?P<page>[0-9]+)/$', 'object_list', {'queryset': Gallery.objects.filter(is_public=True), 'allow_empty': True, 'paginate_by': 5, 'extra_context':{'sample_size':SAMPLE_SIZE}}, name='pl-gallery-list'),
20
)
21
22
# photographs
23
photo_args = {'date_field': 'date_added', 'allow_empty': True, 'queryset': Photo.objects.filter(is_public=True)}
24
urlpatterns += patterns('django.views.generic.date_based',
25
    url(r'^photo/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\w{1,2})/(?P<slug>[\-\d\w]+)/$', 'object_detail', {'date_field': 'date_added', 'slug_field': 'title_slug', 'queryset': Photo.objects.filter(is_public=True)}, name='pl-photo-detail'),
26
    url(r'^photo/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\w{1,2})/$', 'archive_day', photo_args, name='pl-photo-archive-day'),
27
    url(r'^photo/(?P<year>\d{4})/(?P<month>\d{2})/$', 'archive_month', photo_args, name='pl-photo-archive-month'),
28
    url(r'^photo/(?P<year>\d{4})/$', 'archive_year', photo_args, name='pl-photo-archive-year'),
29
    url(r'^photo/$', 'archive_index', photo_args, name='pl-photo-archive'),
30
)
31
urlpatterns += patterns('django.views.generic.list_detail',
32
    url(r'^photo/(?P<slug>[\-\d\w]+)/$', 'object_detail', {'slug_field': 'title_slug', 'queryset': Photo.objects.filter(is_public=True)}, name='pl-photo'),
33
    url(r'^photo/page/(?P<page>[0-9]+)/$', 'object_list', {'queryset': Photo.objects.filter(is_public=True), 'allow_empty': True, 'paginate_by': 20}, name='pl-photo-list'),
34
)
35
36

Up to file-list setup.py:

1
#/usr/bin/env python
2
import os
3
from setuptools import setup, find_packages
4
5
ROOT_DIR = os.path.dirname(__file__)
6
SOURCE_DIR = os.path.join(ROOT_DIR)
7
8
# Dynamically calculate the version based on photolog.VERSION
9
version_tuple = __import__('photolog').VERSION
10
if len(version_tuple) == 3:
11
    version = "%d.%d_%s" % version_tuple
12
else:
13
    version = "%d.%d" % version_tuple[:2]
14
15
setup(
16
    name = "django-photolog",
17
    version = version,
18
    description = "Photo gallery application for the Django web framework",
19
    author = "Justin Driscoll",
20
    author_email = "justin@driscolldev.com",
21
    url = "http://hg.driscolldev.com/django-photolog",
22
    packages = find_packages(),
23
    package_data = {
24
        'photolog': [
25
            'locale/*/LC_MESSAGES/*',
26
            'templates/photolog/*.html',
27
            'templates/photolog/tags/*.html',
28
        ]
29
    },
30
    install_requires = ['django-imagekit>=0.3.2'],
31
    zip_safe = False,
32
    classifiers = ['Development Status :: 5 - Production/Stable',
33
                   'Environment :: Web Environment',
34
                   'Framework :: Django',
35
                   'Intended Audience :: Developers',
36
                   'License :: OSI Approved :: BSD License',
37
                   'Operating System :: OS Independent',
38
                   'Programming Language :: Python',
39
                   'Topic :: Utilities'],
40
)