jdriscoll / django-photolog
Photo galleries for the Django web framework.
$ hg clone http://hg.driscolldev.com/django-photolog
| commit 0: | 1c1b6b06a6ee |
| branch: | default |
Changed (Δ24.4 KB):
.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)
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. |
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" %} |
1 |
{% if photo %} |
|
2 |
<a title="{{ photo.title }}" href="{{ photo.get_absolute_url }}"><img src="{{ photo.get_thumbnail_url }}"/></a> |
|
3 |
{% endif %} |
1 |
{% if photo %} |
|
2 |
<a title="{{ photo.title }}" href="{{ photo.get_absolute_url }}"><img src="{{ photo.get_thumbnail_url }}"/></a> |
|
3 |
{% endif %} |
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 |
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 |
) |
