Changeset 129

Show
Ignore:
Timestamp:
02/06/10 23:32:29 (6 months ago)
Author:
max
Message:

Added ability to upload zip file of images to create (or add to an existing) gallery.

Location:
trunk/courant/core/media
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • trunk/courant/core/media/admin.py

    r128 r129  
    3434        }), 
    3535    ) 
    36      
     36 
    3737    inlines = [ 
    3838        MediaBylineInline, 
     
    4747        }), 
    4848        ("Image", { 
    49             'fields': ('image',)  
     49            'fields': ('image',) 
    5050        }), 
    5151        ("Tags", { 
     
    6666        }), 
    6767        ("Details", { 
    68             'fields': ('url','image')  
     68            'fields': ('url','image') 
    6969        }), 
    7070        ("Tags", { 
     
    8585        }), 
    8686        ("Details", { 
    87             'fields': ('file',)  
     87            'fields': ('file',) 
    8888        }), 
    8989        ("Tags", { 
     
    102102    raw_id_fields = ('media_item',) 
    103103    fk_name = 'gallery' 
    104      
     104 
    105105class GalleryAdmin(MediaAdmin): 
    106106    list_filter = ['created_at'] 
     
    111111admin.site.register(Gallery, GalleryAdmin) 
    112112 
     113class GalleryUploadAdmin(admin.ModelAdmin): 
     114    raw_id_fields = ('staffer',) 
     115    def has_change_permission(self, request, obj=None): 
     116        return False # To remove the 'Save and continue editing' button 
     117admin.site.register(GalleryUpload, GalleryUploadAdmin) 
     118 
     119 
    113120class FileAdmin(MediaAdmin): 
    114121    list_filter = ['created_at'] 
     
    118125        }), 
    119126        ("File", { 
    120             'fields': ('file','image','width','height')  
     127            'fields': ('file','image','width','height') 
    121128        }), 
    122129        ("Tags", { 
  • trunk/courant/core/media/models.py

    r128 r129  
    44from django.conf import settings 
    55from django.core.cache import cache 
     6from django.template.defaultfilters import slugify 
     7from django.core.files.base import ContentFile 
    68 
    79from courant.core.staff.models import Staffer, ContentByline 
     
    1517from django_extensions.db.fields import CreationDateTimeField, ModificationDateTimeField 
    1618 
    17 from courant.core.media.utils import get_file_path, get_storage_path, get_image_path 
     19from courant.core.media.utils import get_file_path, get_storage_path, get_image_path, get_temp_file_path 
    1820 
    1921from sorl.thumbnail.main import DjangoThumbnail 
     
    2426import urllib 
    2527import os 
     28import zipfile 
     29 
     30try: 
     31    import Image 
     32except ImportError: 
     33    try: 
     34        from PIL import Image 
     35    except ImportError: 
     36        raise ImportError('Courant was unable to import the Python Imaging Library. Please confirm it`s installed and available on your current Python path.') 
     37 
    2638 
    2739class MediaFolder(models.Model): 
     
    3244    name = models.CharField(max_length=255) 
    3345    parent = models.ForeignKey('self', null=True, blank=True, related_name='children') 
    34      
     46 
    3547    def __unicode__(self): 
    3648        return "%s/%s" % ('/'.join(self.get_ancestors().values_list('name', flat=True)), self.name) 
     
    6880                                     limit_choices_to={'app_label': 'media'}, 
    6981                                     editable=False, null=True) 
    70      
     82 
    7183    staffers = models.ManyToManyField(Staffer, through='MediaByline', related_name='media') 
    7284    staffers_override = models.CharField(max_length=255, blank=True, 
     
    7890                                                      associations, esp. for one-off \ 
    7991                                                      contributors.") 
    80      
     92 
    8193    slug = models.SlugField() 
    82      
     94 
    8395    created_at = CreationDateTimeField() 
    8496    modified_at = ModificationDateTimeField() 
    8597    published_at = models.DateTimeField() 
    86      
     98 
    8799    tags = TagField() 
    88      
     100 
    89101    def default_comment_option(): 
    90102        try: 
     
    98110 
    99111    comment_options = models.ForeignKey(CommentOptions, null=True, default=default_comment_option) 
    100      
    101     objects = SubclassManager() 
    102      
    103     class Meta: 
    104         ordering = ["-created_at"] 
    105         get_latest_by = "-created_at" 
    106      
     112 
     113    objects = SubclassManager() 
     114 
     115    class Meta: 
     116        ordering = ["-created_at"] 
     117        get_latest_by = "-created_at" 
     118 
    107119    def __unicode__(self): 
    108120        return "Media: %s" % self.name 
    109      
     121 
    110122    def save(self, *args, **kwargs): 
    111123        if(not self.content_type): 
    112124            self.content_type = ContentType.objects.get_for_model(self.__class__) 
    113125        self.save_base(*args, **kwargs) 
    114          
     126 
    115127    @models.permalink 
    116128    def get_absolute_url(self): 
     
    131143            return self 
    132144        return model.objects.get(id=self.id) 
    133          
     145 
    134146    def admin_thumbnail(self): 
    135147        """ 
     
    153165    """ 
    154166    media_item = models.ForeignKey(MediaItem) 
    155      
     167 
    156168class ContentMedia(models.Model): 
    157169    """ 
     
    161173    media_item = models.ForeignKey(MediaItem) 
    162174    order = models.PositiveSmallIntegerField() 
    163      
     175 
    164176    class Meta: 
    165177        abstract = True 
    166178        ordering = ['order'] 
    167      
     179 
    168180class Photo(MediaItem): 
    169181    """ 
     
    171183    """ 
    172184    image = models.ImageField(_('image'), upload_to=get_storage_path) 
    173      
    174     objects = SubclassManager() 
    175      
    176     class Meta: 
    177         ordering = ["-created_at"] 
    178         get_latest_by = "-created_at" 
    179          
     185 
     186    objects = SubclassManager() 
     187 
     188    class Meta: 
     189        ordering = ["-created_at"] 
     190        get_latest_by = "-created_at" 
     191 
    180192    def __unicode__(self): 
    181193        return u"Photo: %s" % self.name 
    182      
     194 
    183195    def thumbnail(self): 
    184196        return self.image 
     
    195207    image = models.ImageField("Thumbnail", upload_to=get_storage_path, blank=True, 
    196208                              help_text="Leave blank to automatically fetch from external service.") 
    197      
    198     objects = SubclassManager() 
    199      
    200     class Meta: 
    201         ordering = ["-created_at"] 
    202         get_latest_by = "-created_at" 
    203          
     209 
     210    objects = SubclassManager() 
     211 
     212    class Meta: 
     213        ordering = ["-created_at"] 
     214        get_latest_by = "-created_at" 
     215 
    204216    def __unicode__(self): 
    205217        return u"Video: %s" % self.name 
    206      
     218 
    207219    def save(self, **kwargs): 
    208220        if not self.image: 
     
    218230            self.image = filename 
    219231        super(Video, self).save(**kwargs) 
    220          
     232 
    221233    def thumbnail(self): 
    222234        return self.image 
     
    228240    """ 
    229241    file = models.FileField(upload_to=get_file_path) 
    230      
    231     class Meta: 
    232         ordering = ["-created_at"] 
    233         get_latest_by = "-created_at" 
    234          
     242 
     243    class Meta: 
     244        ordering = ["-created_at"] 
     245        get_latest_by = "-created_at" 
     246 
    235247    def __unicode__(self): 
    236248        return u"Audio: %s" % self.name 
    237249gettag.register(Audio) 
    238    
     250 
    239251class Gallery(MediaItem): 
    240252    """ 
     
    244256    media = models.ManyToManyField(MediaItem, through='GalleryMedia', 
    245257                                   related_name='galleries', symmetrical=False) 
    246      
    247     objects = SubclassManager() 
    248      
     258 
     259    objects = SubclassManager() 
     260 
    249261    def __init__(self, *args, **kwargs): 
    250262        super(Gallery, self).__init__(*args, **kwargs) 
    251          
     263 
    252264        # fetch the first item in the gallery to be used for thumbnail purposes 
    253265        try: 
     
    255267        except: 
    256268            pass 
    257      
     269 
    258270    class Meta: 
    259271        verbose_name_plural = 'galleries' 
    260272        ordering = ["-created_at"] 
    261273        get_latest_by = "-created_at" 
    262          
     274 
    263275    def __unicode__(self): 
    264276        return u"Gallery: %s" % self.name 
    265      
     277 
    266278    def thumbnail(self): 
    267279        return self.image 
    268 gettag.register(Gallery)     
     280gettag.register(Gallery) 
    269281 
    270282class GalleryMedia(ContentMedia): 
     
    273285    """ 
    274286    gallery = models.ForeignKey(Gallery, related_name='gallery_media') 
    275      
     287 
     288""" 
     289GalleryUpload model originally based on model from django-photologue (r405, February 6, 2010). 
     290django-photologue is Copyright (c) 2007-2008, Justin C. Driscoll. All rights reserved. 
     291""" 
     292class GalleryUpload(models.Model): 
     293    zip_file = models.FileField(_('images file (.zip)'), upload_to=get_temp_file_path, 
     294                                help_text=_('Select a .zip file of images to upload into a new Gallery.')) 
     295    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.')) 
     296    name = models.CharField(max_length=255, help_text=_('All photos in the gallery will be given a name made up of the gallery name + a sequential number.')) 
     297    caption = models.TextField(help_text=_('Caption will be added to all photos.')) 
     298    caption_photos = models.BooleanField(default=True, help_text=_('Gallery caption will also be applied to each photo.')) 
     299    staffer = models.ForeignKey(Staffer) 
     300    published_at = models.DateTimeField() 
     301 
     302    class Meta: 
     303        verbose_name = _('gallery upload') 
     304        verbose_name_plural = _('gallery uploads') 
     305 
     306    def save(self, *args, **kwargs): 
     307        super(GalleryUpload, self).save(*args, **kwargs) 
     308        gallery = self.process_zipfile() 
     309        super(GalleryUpload, self).delete() 
     310        return gallery 
     311 
     312    def process_zipfile(self): 
     313        if os.path.isfile(self.zip_file.path): 
     314            # TODO: implement try-except here 
     315            zip = zipfile.ZipFile(self.zip_file.path) 
     316            bad_file = zip.testzip() 
     317            if bad_file: 
     318                raise Exception('"%s" in the .zip archive is corrupt.' % bad_file) 
     319            count = 1 
     320            if self.gallery: 
     321                gallery = self.gallery 
     322            else: 
     323                gallery = Gallery.objects.create(name=self.name, 
     324                                                 slug=slugify(self.name), 
     325                                                 caption=self.caption, 
     326                                                 published_at=self.published_at) 
     327 
     328                MediaByline(media_item=gallery, staffer=self.staffer, order=1).save() 
     329 
     330            from cStringIO import StringIO 
     331            for filename in sorted(zip.namelist()): 
     332                if filename.startswith('__'): # do not process meta files 
     333                    continue 
     334                data = zip.read(filename) 
     335                if len(data): 
     336                    try: 
     337                        # the following is taken from django.newforms.fields.ImageField: 
     338                        #  load() is the only method that can spot a truncated JPEG, 
     339                        #  but it cannot be called sanely after verify() 
     340                        trial_image = Image.open(StringIO(data)) 
     341                        trial_image.load() 
     342                        # verify() is the only method that can spot a corrupt PNG, 
     343                        #  but it must be called immediately after the constructor 
     344                        trial_image = Image.open(StringIO(data)) 
     345                        trial_image.verify() 
     346                    except Exception, e: 
     347                        # if a "bad" file is found we just skip it. 
     348                        assert False, ('Bad file in zip upload', e) 
     349                        continue 
     350                    while 1: 
     351                        name = ' '.join([self.name, str(count)]) 
     352                        slug = slugify(name) 
     353                        try: 
     354                            p = Photo.objects.get(slug=slug) 
     355                        except Photo.DoesNotExist: 
     356                            photo = Photo(name=name, 
     357                                          slug=slug, 
     358                                          published_at=self.published_at) 
     359                            photo.caption = self.caption if self.caption_photos else '' 
     360                            photo.image.save(filename, ContentFile(data)) 
     361                            photo.save() 
     362 
     363                            MediaByline(media_item=photo, staffer=self.staffer, order=1).save() 
     364 
     365                            GalleryMedia(gallery=gallery, media_item=photo, order=count).save() 
     366                            count = count + 1 
     367                            break 
     368                        count = count + 1 
     369            zip.close() 
     370            return gallery 
     371 
    276372class File(MediaItem): 
    277373    """ 
     
    281377    image = models.ImageField("Thumbnail", upload_to=get_storage_path, blank=True, 
    282378                              help_text="Leave blank to generate automatically.") 
    283      
     379 
    284380    width = models.PositiveIntegerField(blank=True, null=True, help_text="Only required for certain file types (e.g., SWF)") 
    285381    height = models.PositiveIntegerField(blank=True, null=True, help_text="Only required for certain file types (e.g., SWF)") 
    286      
    287     objects = SubclassManager() 
    288      
    289     class Meta: 
    290         ordering = ["-created_at"] 
    291         get_latest_by = "-created_at" 
    292          
     382 
     383    objects = SubclassManager() 
     384 
     385    class Meta: 
     386        ordering = ["-created_at"] 
     387        get_latest_by = "-created_at" 
     388 
    293389    def thumbnail(self): 
    294390        return self.image if self.image else self.file 
    295      
     391 
    296392    def extension(self): 
    297393        """