Commits

Mikhail Korobov committed d63266e

Initial commit

Comments (0)

Files changed (9)

+syntax: glob
+
+#IDE files
+.settings/*
+.project
+.pydevproject
+.externalToolBuilders*
+.cache/*
+nbproject/*
+.buildpath
+build.properties
+MANIFEST.MF
+
+#temp files
+*.pyc
+*.pyo
+*.orig
+*~
+
+#misc files
+pip-log.txt
+
+#os files
+.DS_Store
+Thumbs.db
+
+
+#project-specific files
+stuff/
+generic_images/migrations
+
+#setup files
+build/
+dist/
+.build/

photo_albums/__init__.py

Empty file added.

photo_albums/forms.py

+#coding: utf-8
+
+from django import forms
+from django.forms.models import modelformset_factory
+
+from generic_images.models import AttachedImage
+from generic_images.forms import AttachedImageForm
+
+
+PhotoFormSet = modelformset_factory(AttachedImage, 
+                                    extra=3, 
+                                    fields = ['image', 'caption']
+                                   )
+

photo_albums/models.py

+# empty models file is needed for templatetags to work

photo_albums/templatetags/__init__.py

Empty file added.

photo_albums/test_utils.py

+#coding: utf-8
+
+from django.core.urlresolvers import reverse
+from generic_utils.test_helpers import ViewTest
+from generic_images.models import AttachedImage
+
+class AlbumTest(ViewTest):
+        
+    username = None
+    password = None
+    fixtures = []
+    album_site = None
+    excluded_views = []
+    
+    album_for_id = None
+    non_existing_object_id = 0
+    
+    image_in_album_id = None
+    image2_in_album_id = None
+    image_in_other_album_id = None
+    non_existing_image_id = 0
+    
+    def check(self, view, status, kwargs=None):
+        if not kwargs:
+            kwargs = {}
+        if view not in self.excluded_views:            
+            if not 'object_id' in kwargs:
+                kwargs['object_id'] = self.album_for_id
+            name = '%s:%s' % (self.album_site.instance_name, view,)
+            return self.check_url(name, status, kwargs=kwargs, current_app=self.album_site.app_name)                
+                
+    def test_public_views(self):
+        self.check('show_album', 200)
+        self.check('show_album', 404, kwargs={'object_id':self.non_existing_object_id})
+
+        self.check('show_image', 200, kwargs={'image_id': self.image_in_album_id})
+        self.check('show_image', 404, kwargs={'image_id': self.image_in_other_album_id})
+        self.check('show_image', 404, kwargs={'image_id': self.non_existing_image_id})
+        
+    def test_forbidden_views(self):
+        self.check('edit_album', 302)
+        self.check('upload_main_image', 302)
+        self.check('upload_images', 302)
+        self.check('edit_image', 302, kwargs={'image_id': self.image_in_album_id})
+        self.check('delete_image', 302, kwargs={'image_id': self.image_in_album_id})
+        self.check('set_as_main_image', 302, kwargs={'image_id': self.image_in_album_id})
+        self.check('clear_main_image', 302, kwargs={'image_id':  self.image_in_album_id})
+        self.check('set_image_order', 302)
+        
+    def test_auth_views(self):
+        self.assertTrue(self.client.login(username=self.username, password=self.password))
+
+        self.check('edit_album', 200)
+        self.check('upload_main_image', 200)
+        self.check('upload_images', 200)
+        self.check('show_album', 200)
+        self.check('show_image', 200, kwargs={'image_id': self.image_in_album_id})
+        
+        self.check('reorder_images', 200)
+        self.check('set_image_order', 404)
+
+        self.check('edit_image', 200, kwargs={'image_id': self.image_in_album_id})
+        self.check('delete_image', 200, kwargs={'image_id': self.image_in_album_id})
+        self.check('set_as_main_image', 302, kwargs={'image_id': self.image_in_album_id})
+        self.check('clear_main_image', 302, kwargs={'image_id': self.image_in_album_id})
+        
+    def test_reorder(self):
+        if self.image2_in_album_id is None:
+            return
+
+        name = '%s:%s' % (self.album_site.instance_name, 'set_image_order',)
+        url = reverse(name, kwargs={'object_id': self.album_for_id}, current_app = self.album_site.app_name)   
+        
+        img1 = AttachedImage.objects.get(id=self.image_in_album_id)
+        img2 = AttachedImage.objects.get(id=self.image2_in_album_id)
+
+        #correct:
+        self.assertTrue(self.client.login(username=self.username, password=self.password))
+        response = self.client.post(url, 
+                                   {'items': '[{"id":"%d","order":"%d"},{"id":"%d","order":"%d"}]' % 
+                                               (img1.id, img2.order,img2.id, img1.order)}, 
+                                   HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+
+        self.assertEqual(response.content, '{"done": true}')
+        
+        img1_new = AttachedImage.objects.get(id=self.image_in_album_id)
+        img2_new = AttachedImage.objects.get(id=self.image2_in_album_id)
+        
+        self.assertEqual(img1_new.id, img1.id)
+        self.assertEqual(img1_new.order, img2.order)
+        self.assertEqual(img2_new.id, img2.id)        
+        self.assertEqual(img2_new.order, img1.order)

photo_albums/urls.py

+#coding: utf-8
+
+from django.conf.urls.defaults import *
+from generic_utils.app_utils import PluggableSite
+
+class PhotoAlbumSite(PluggableSite):
+    
+    def __init__(self, instance_name, queryset, app_name='album', 
+                 extra_context=None, template_object_name = None,
+                 has_edit_permission = lambda request, obj: True):
+        
+        super(PhotoAlbumSite, self).__init__(instance_name, queryset, app_name, 
+                                             extra_context, template_object_name,
+                                             has_edit_permission)
+                
+    def patterns(self):
+        return patterns('photo_albums.views',
+                        
+                        #album-level views
+                        url(
+                            r'^(?P<object_id>\d+)/%s/$' % self.app_name,
+                            'show_album', 
+                            {'album_site': self},
+                            name = 'show_album',
+                        ),
+                        url(
+                            r'^(?P<object_id>\d+)/%s/edit$' % self.app_name,
+                            'edit_album', 
+                            {'album_site': self},
+                            name = 'edit_album',
+                        ),
+                        url(
+                            r'^(?P<object_id>\d+)/%s/upload_main/$' % self.app_name,
+                            'upload_main_image',
+                            {'album_site': self},
+                            name = 'upload_main_image',
+                        ),
+                        url(
+                            r'^(?P<object_id>\d+)/%s/upload_images/$' % self.app_name,
+                            'upload_images',
+                            {'album_site': self},
+                            name = 'upload_images',
+                        ),
+                        
+                        
+                        #one image views
+                        url(
+                            r'^(?P<object_id>\d+)/%s/(?P<image_id>\d+)/$' % self.app_name,
+                            'show_image',
+                            {'album_site': self},
+                            name = 'show_image',
+                        ),
+                        url(
+                            r'^(?P<object_id>\d+)/%s/(?P<image_id>\d+)/edit$' % self.app_name,
+                            'edit_image',
+                            {'album_site': self},
+                            name = 'edit_image',
+                        ),
+                        url(
+                            r'^(?P<object_id>\d+)/%s/(?P<image_id>\d+)/delete$' % self.app_name,
+                            'delete_image',
+                            {'album_site': self},
+                            name = 'delete_image',
+                        ),
+                        url(
+                            r'^(?P<object_id>\d+)/%s/(?P<image_id>\d+)/set-as-main$' % self.app_name,
+                            'set_as_main_image',
+                            {'album_site': self},
+                            name = 'set_as_main_image',
+                        ),
+                        url(
+                            r'^(?P<object_id>\d+)/%s/(?P<image_id>\d+)/clear-main$' % self.app_name,
+                            'clear_main_image',
+                            {'album_site': self},
+                            name = 'clear_main_image',
+                        ),
+                                                
+                        #reorder
+                        url(
+                            r'^(?P<object_id>\d+)/%s/reorder/$' % self.app_name,
+                            'edit_album', 
+                            {'album_site': self, 'template_name': 'reorder_images.html'},
+                            name = 'reorder_images',
+                        ),
+                        url(
+                            r'^(?P<object_id>\d+)/%s/set_image_order$' % self.app_name,
+                            'set_image_order', 
+                            {'album_site': self},
+                            name = 'set_image_order',
+                        ),
+                    )                    
+   

photo_albums/views.py

+from django.conf import settings
+from django.core.urlresolvers import reverse
+from django.http import HttpResponseRedirect, Http404
+from django.shortcuts import render_to_response, get_object_or_404
+from django.template import RequestContext
+from django.contrib.auth import authenticate, login, logout
+from django.contrib.auth.decorators import login_required
+from django.contrib.auth.models import User
+from django.views.generic.create_update import create_object, delete_object, update_object
+from django.utils import simplejson
+from django.core.paginator import Paginator
+from django.contrib.contenttypes.models import ContentType
+
+from annoying.decorators import ajax_request
+
+from photo_albums.forms import PhotoFormSet
+from generic_images.forms import AttachedImageForm
+from generic_images.models import AttachedImage
+from generic_utils import get_template_search_list
+
+
+def get_template_names(object, template_name):
+    return get_template_search_list('albums', object, template_name)
+     
+
+def show_album(request, object_id, album_site, template_name='show_album.html'):
+        
+    object, context = album_site.get_object_and_context(object_id)
+    
+    images = AttachedImage.objects.get_for_model(object)
+    context.update({'images': images})
+    return render_to_response(get_template_names(object, template_name), 
+                              context,
+                              context_instance=RequestContext(request),
+                              )
+    
+
+@login_required
+def edit_album(request, object_id, album_site, template_name='edit_album.html'):
+    object, context = album_site.get_object_and_context(object_id)
+    album_site.check_permissions(request, object)
+    
+    images = AttachedImage.objects.get_for_model(object)
+    context.update({'images': images})
+        
+    return render_to_response(get_template_names(object, template_name), 
+                              context,
+                              context_instance=RequestContext(request),
+                              )
+    
+    
+@login_required
+def upload_main_image(request, object_id, album_site):    
+
+    object, context = album_site.get_object_and_context(object_id)
+    album_site.check_permissions(request, object)
+    
+    success_url = album_site.reverse('show_album', args=[object_id])
+    
+    if request.method == 'POST':
+        form = AttachedImageForm(request.POST, request.FILES)
+        if form.is_valid():
+            photo = form.save(commit=False)
+            photo.user = request.user
+            photo.content_object = object
+            photo.is_main = True
+            photo.save()
+            return HttpResponseRedirect(success_url) # Redirect after POST
+    else:
+        form = AttachedImageForm()        
+
+    context.update({'form': form})
+
+    return render_to_response(
+                                get_template_names(object, 'upload_main_image.html'),
+                                context,
+                                context_instance=RequestContext(request),
+                             )
+
+
+@login_required
+def upload_images(request, object_id, album_site):    
+    object, context = album_site.get_object_and_context(object_id)
+    album_site.check_permissions(request, object)
+    
+    success_url = album_site.reverse('show_album', args=[object_id])
+    
+    if request.method == 'POST':
+        formset = PhotoFormSet(request.POST, request.FILES, queryset = AttachedImage.objects.none())
+        if formset.is_valid():
+            instances = formset.save(commit=False)
+            for photo in instances:
+                photo.user = request.user
+                photo.content_object = object
+                photo.save()
+            return HttpResponseRedirect(success_url) # Redirect after POST
+    else:
+        formset = PhotoFormSet(queryset = AttachedImage.objects.none())        
+
+    context.update({'formset': formset})
+    
+    return render_to_response(get_template_names(object, 'upload_images.html'), 
+                              context, 
+                              context_instance=RequestContext(request))
+
+
+def show_image(request, object_id, image_id, album_site, template_name = 'show_image.html'):
+    object, context = album_site.get_object_and_context(object_id)
+    
+    images = AttachedImage.objects.get_for_model(object) 
+    image = get_object_or_404(images, id=image_id)    
+    
+    try:
+        next_id = images.filter(order__lt=image.order).order_by('-order')[0].id
+    except IndexError:
+        next_id = None
+    
+    try:    
+        prev_id = images.filter(order__gt=image.order).order_by('order')[0].id    
+    except IndexError:
+        prev_id = None
+
+    context.update({'image': image, 'prev': prev_id, 'next': next_id})
+
+    return render_to_response(get_template_names(object, template_name), 
+                              context, 
+                              context_instance=RequestContext(request))
+
+
+@login_required
+def edit_image(request, object_id, image_id, album_site):    
+    object, context = album_site.get_object_and_context(object_id)
+    album_site.check_permissions(request, object)
+    return show_image(request, object_id, image_id, album_site, template_name='edit_image.html')
+
+
+@login_required
+def delete_image(request, object_id, image_id, album_site):
+    object, context = album_site.get_object_and_context(object_id)
+    album_site.check_permissions(request, object)
+    
+    image = get_object_or_404(AttachedImage.objects.get_for_model(object), id=image_id)
+    next_url = album_site.reverse('show_album', args=[object_id])
+    
+    return delete_object(request, 
+                         model=AttachedImage, 
+                         post_delete_redirect = next_url, 
+                         object_id = image_id,
+                         extra_context = context, 
+                         template_name = get_template_names(object, 'confirm_delete.html')[1])
+
+
+    
+@login_required
+def set_as_main_image(request, object_id, image_id, album_site):
+    object, context = album_site.get_object_and_context(object_id)
+    album_site.check_permissions(request, object)
+    
+    image = get_object_or_404(AttachedImage.objects.get_for_model(object), id=image_id)
+    image.is_main = True
+    image.save()
+    
+    next_url = album_site.reverse('show_image', args=[object_id, image_id])    
+    return HttpResponseRedirect(next_url)
+
+    
+@login_required
+def clear_main_image(request, object_id, image_id, album_site):
+    object, context = album_site.get_object_and_context(object_id)
+    album_site.check_permissions(request, object)
+
+    image = AttachedImage.objects.get_main_for(object)
+    if image:
+        image.is_main = False
+        image.save()
+        
+    next_url = album_site.reverse('show_image', args=[object_id, image_id])    
+    return HttpResponseRedirect(next_url)
+
+
+@login_required
+@ajax_request
+def set_image_order(request, object_id, album_site):
+    object, context = album_site.get_object_and_context(object_id)
+    album_site.check_permissions(request, object)
+    
+    if request.is_ajax():        
+        data_str = request.POST.get('items','')
+        items = simplejson.loads(data_str)
+        for item in items:
+            image_id = item['id']
+            order = item['order']
+            try:
+                #check that image belongs to proper object
+                image = AttachedImage.objects.get_for_model(object).get(id=image_id)
+                image.order = order
+                image.save()
+            except AttachedImage.DoesNotExist:
+                return {'done': False, 'reason': 'Invalid data.'}            
+        return {'done': True}
+    raise Http404
+#!/usr/bin/env python
+from setuptools import setup
+
+setup(
+      name='django-photo-albums',
+      version='0.1',
+      author='Mikhail Korobov',
+      author_email='kmike84@gmail.com',
+      url='http://bitbucket.org/kmike/django-photo-albums/',      
+      
+      description = 'Pluggable Django image gallery app.',
+      license = 'MIT license',
+      packages=['photo_albums'],
+      requires = ['django (>=1.1)'],
+      install_requires=['django-generic-images (>=0.12)', 'django-annoying'],
+      
+      classifiers=(
+          'Development Status :: 3 - Alpha',
+          'Environment :: Plugins',
+          'Framework :: Django',
+          'Intended Audience :: Developers',
+          'License :: OSI Approved :: MIT License',
+          'Natural Language :: English',
+          'Natural Language :: Russian',
+          'Programming Language :: Python',
+          'Topic :: Software Development :: Libraries :: Python Modules'
+        ),
+)