qrurls/admin.py
author Tero Marttila <terom@paivola.fi>
Wed, 05 Nov 2014 10:35:51 +0100
changeset 90 a193a8dd737c
parent 89 187e874f770c
permissions -rw-r--r--
qrurls.admin: minor usability tweaks to the import url_images select-multiple...
27
ffcffe3024f1 sane defaults for published in urlitems inline form
Tero Marttila <terom@fixme.fi>
parents: 22
diff changeset
     1
import datetime
89
187e874f770c qrurls.admin: URLAdmin: support use of existing URLImages in import
Tero Marttila <terom@paivola.fi>
parents: 66
diff changeset
     2
import random
49
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
     3
import urllib
27
ffcffe3024f1 sane defaults for published in urlitems inline form
Tero Marttila <terom@fixme.fi>
parents: 22
diff changeset
     4
49
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
     5
from django import http, shortcuts, forms
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
     6
from django.conf import settings, urls
2
e838dda048a6 qrurls: basic functionality
Tero Marttila <terom@fixme.fi>
parents:
diff changeset
     7
from django.contrib import admin
49
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
     8
from django.core.urlresolvers import reverse
47
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
     9
from django.utils import timezone, formats
27
ffcffe3024f1 sane defaults for published in urlitems inline form
Tero Marttila <terom@fixme.fi>
parents: 22
diff changeset
    10
import django.utils.html
ffcffe3024f1 sane defaults for published in urlitems inline form
Tero Marttila <terom@fixme.fi>
parents: 22
diff changeset
    11
import django.forms.models
ffcffe3024f1 sane defaults for published in urlitems inline form
Tero Marttila <terom@fixme.fi>
parents: 22
diff changeset
    12
30
d0f56303d8de URLImage
Tero Marttila <terom@fixme.fi>
parents: 27
diff changeset
    13
from qrurls.models import URL, URLItem, URLImage
27
ffcffe3024f1 sane defaults for published in urlitems inline form
Tero Marttila <terom@fixme.fi>
parents: 22
diff changeset
    14
54
Tero Marttila <terom@fixme.fi>
parents: 51
diff changeset
    15
"""
Tero Marttila <terom@fixme.fi>
parents: 51
diff changeset
    16
    Private backend UI, using django-admin.
Tero Marttila <terom@fixme.fi>
parents: 51
diff changeset
    17
"""
Tero Marttila <terom@fixme.fi>
parents: 51
diff changeset
    18
27
ffcffe3024f1 sane defaults for published in urlitems inline form
Tero Marttila <terom@fixme.fi>
parents: 22
diff changeset
    19
class URLItemFormset (django.forms.models.BaseInlineFormSet) :
47
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    20
    """
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    21
        Uses the existing URLItems for the URLFeed to determine the initial publishing
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    22
        times for new items.
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    23
    """
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    24
27
ffcffe3024f1 sane defaults for published in urlitems inline form
Tero Marttila <terom@fixme.fi>
parents: 22
diff changeset
    25
    def __init__ (self, *args, **kwargs) :
49
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
    26
        extra = kwargs.get('extra', 5)
27
ffcffe3024f1 sane defaults for published in urlitems inline form
Tero Marttila <terom@fixme.fi>
parents: 22
diff changeset
    27
56
96e1c616a955 move publishing schedule to URL model, make timetz aware
Tero Marttila <terom@fixme.fi>
parents: 55
diff changeset
    28
        # hack to get at the URLFeed to determine our initial values..
96e1c616a955 move publishing schedule to URL model, make timetz aware
Tero Marttila <terom@fixme.fi>
parents: 55
diff changeset
    29
        urlfeed = kwargs.get('instance')
96e1c616a955 move publishing schedule to URL model, make timetz aware
Tero Marttila <terom@fixme.fi>
parents: 55
diff changeset
    30
        if not isinstance(urlfeed, URL) :
96e1c616a955 move publishing schedule to URL model, make timetz aware
Tero Marttila <terom@fixme.fi>
parents: 55
diff changeset
    31
            urlfeed = None
96e1c616a955 move publishing schedule to URL model, make timetz aware
Tero Marttila <terom@fixme.fi>
parents: 55
diff changeset
    32
        
65
b6ce70e51bce fix some issues with dynamic initial values confusing inline formsets (for inline formsets not bound to a urlfeed).
Tero Marttila <terom@fixme.fi>
parents: 64
diff changeset
    33
        # XXX: initial values must be stable across form GET/POST, or django will be confused and think the form has been filled out
27
ffcffe3024f1 sane defaults for published in urlitems inline form
Tero Marttila <terom@fixme.fi>
parents: 22
diff changeset
    34
        kwargs.update(initial=[
56
96e1c616a955 move publishing schedule to URL model, make timetz aware
Tero Marttila <terom@fixme.fi>
parents: 55
diff changeset
    35
            # Either generic from today, or based on the actual urlfeed
96e1c616a955 move publishing schedule to URL model, make timetz aware
Tero Marttila <terom@fixme.fi>
parents: 55
diff changeset
    36
            dict(published=publish) for publish in URLAdmin.publishing_schedule(urlfeed, count=extra)
27
ffcffe3024f1 sane defaults for published in urlitems inline form
Tero Marttila <terom@fixme.fi>
parents: 22
diff changeset
    37
        ])
ffcffe3024f1 sane defaults for published in urlitems inline form
Tero Marttila <terom@fixme.fi>
parents: 22
diff changeset
    38
        super(URLItemFormset, self).__init__(*args, **kwargs)
2
e838dda048a6 qrurls: basic functionality
Tero Marttila <terom@fixme.fi>
parents:
diff changeset
    39
3
e7d3f1a28b24 touchup admin ui
Tero Marttila <terom@fixme.fi>
parents: 2
diff changeset
    40
class URLItemInline (admin.TabularInline) :
47
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    41
    """
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    42
        Inline set of URLItems for an URLFeed.
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    43
    """
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    44
3
e7d3f1a28b24 touchup admin ui
Tero Marttila <terom@fixme.fi>
parents: 2
diff changeset
    45
    model = URLItem
27
ffcffe3024f1 sane defaults for published in urlitems inline form
Tero Marttila <terom@fixme.fi>
parents: 22
diff changeset
    46
    formset = URLItemFormset
3
e7d3f1a28b24 touchup admin ui
Tero Marttila <terom@fixme.fi>
parents: 2
diff changeset
    47
2
e838dda048a6 qrurls: basic functionality
Tero Marttila <terom@fixme.fi>
parents:
diff changeset
    48
class URLAdmin (admin.ModelAdmin) :
49
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
    49
    @classmethod
56
96e1c616a955 move publishing schedule to URL model, make timetz aware
Tero Marttila <terom@fixme.fi>
parents: 55
diff changeset
    50
    def publishing_schedule (cls, urlfeed, count):
96e1c616a955 move publishing schedule to URL model, make timetz aware
Tero Marttila <terom@fixme.fi>
parents: 55
diff changeset
    51
        """Yield URLItem.published values for feed, or defaults."""
96e1c616a955 move publishing schedule to URL model, make timetz aware
Tero Marttila <terom@fixme.fi>
parents: 55
diff changeset
    52
        if urlfeed :
96e1c616a955 move publishing schedule to URL model, make timetz aware
Tero Marttila <terom@fixme.fi>
parents: 55
diff changeset
    53
            date, time, offset = urlfeed.publishing_schedule()
65
b6ce70e51bce fix some issues with dynamic initial values confusing inline formsets (for inline formsets not bound to a urlfeed).
Tero Marttila <terom@fixme.fi>
parents: 64
diff changeset
    54
b6ce70e51bce fix some issues with dynamic initial values confusing inline formsets (for inline formsets not bound to a urlfeed).
Tero Marttila <terom@fixme.fi>
parents: 64
diff changeset
    55
            return URL.apply_publishing_schedule(date, time, offset, count)
49
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
    56
        else:
65
b6ce70e51bce fix some issues with dynamic initial values confusing inline formsets (for inline formsets not bound to a urlfeed).
Tero Marttila <terom@fixme.fi>
parents: 64
diff changeset
    57
            # no data... no defaults
b6ce70e51bce fix some issues with dynamic initial values confusing inline formsets (for inline formsets not bound to a urlfeed).
Tero Marttila <terom@fixme.fi>
parents: 64
diff changeset
    58
            return []
49
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
    59
47
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    60
    def timezone (self, obj) :
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    61
        now = timezone.localtime(obj.now())
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    62
        tz = now.tzinfo
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    63
        td = now.tzinfo.utcoffset(now)
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    64
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    65
        if td :
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    66
            minutes, seconds = divmod(td.total_seconds(), 60)
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    67
            hours, minutes = divmod(minutes, 60)
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    68
            offset = "(UTC%+03d:%02d)" % (hours, minutes)
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    69
        else :
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    70
            offset = ""
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    71
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    72
        return u"%s %s" % (tz, offset)
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    73
22
28a72692b749 keep shorturl case sensitive, but warn if not uppercase
Tero Marttila <terom@paivola.fi>
parents: 11
diff changeset
    74
    def qrcode_url (self, obj) :
28a72692b749 keep shorturl case sensitive, but warn if not uppercase
Tero Marttila <terom@paivola.fi>
parents: 11
diff changeset
    75
        warn = None
28a72692b749 keep shorturl case sensitive, but warn if not uppercase
Tero Marttila <terom@paivola.fi>
parents: 11
diff changeset
    76
28a72692b749 keep shorturl case sensitive, but warn if not uppercase
Tero Marttila <terom@paivola.fi>
parents: 11
diff changeset
    77
        if obj.shorturl.upper() != obj.shorturl :
28a72692b749 keep shorturl case sensitive, but warn if not uppercase
Tero Marttila <terom@paivola.fi>
parents: 11
diff changeset
    78
            warn = "Shorturl should be UPPERCASE for most compact QR code"
28a72692b749 keep shorturl case sensitive, but warn if not uppercase
Tero Marttila <terom@paivola.fi>
parents: 11
diff changeset
    79
28a72692b749 keep shorturl case sensitive, but warn if not uppercase
Tero Marttila <terom@paivola.fi>
parents: 11
diff changeset
    80
        return '<a href="{url}">{url}</a>{warn}'.format(
28a72692b749 keep shorturl case sensitive, but warn if not uppercase
Tero Marttila <terom@paivola.fi>
parents: 11
diff changeset
    81
                url     = django.utils.html.escape(obj.qrcode_url()),
28a72692b749 keep shorturl case sensitive, but warn if not uppercase
Tero Marttila <terom@paivola.fi>
parents: 11
diff changeset
    82
                warn    = '<p class="errornote">{warn}</p>'.format(warn=warn) if warn else '',
28a72692b749 keep shorturl case sensitive, but warn if not uppercase
Tero Marttila <terom@paivola.fi>
parents: 11
diff changeset
    83
        )
28a72692b749 keep shorturl case sensitive, but warn if not uppercase
Tero Marttila <terom@paivola.fi>
parents: 11
diff changeset
    84
    qrcode_url.allow_tags = True
28a72692b749 keep shorturl case sensitive, but warn if not uppercase
Tero Marttila <terom@paivola.fi>
parents: 11
diff changeset
    85
4
d56f48f6c94b URL: show qrcode for URL in admin (using google charts)
Tero Marttila <terom@fixme.fi>
parents: 3
diff changeset
    86
    def qrcode_img (self, obj) :
d56f48f6c94b URL: show qrcode for URL in admin (using google charts)
Tero Marttila <terom@fixme.fi>
parents: 3
diff changeset
    87
        return '<img src="{img}" />'.format(
d56f48f6c94b URL: show qrcode for URL in admin (using google charts)
Tero Marttila <terom@fixme.fi>
parents: 3
diff changeset
    88
                img=django.utils.html.escape(obj.qrcode_img()),
d56f48f6c94b URL: show qrcode for URL in admin (using google charts)
Tero Marttila <terom@fixme.fi>
parents: 3
diff changeset
    89
        )
d56f48f6c94b URL: show qrcode for URL in admin (using google charts)
Tero Marttila <terom@fixme.fi>
parents: 3
diff changeset
    90
    qrcode_img.allow_tags = True
47
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    91
    
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    92
    # XXX: a whole bunch of ugly datetime-formatting for display...
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    93
    def active (self, obj) :
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    94
        item = obj.active_item()
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    95
        if item :
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    96
            return "%s %s" % (formats.localize(timezone.localtime(item.published)), item)
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    97
        else :
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    98
            return ""
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
    99
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   100
    def now (self, obj) :
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   101
        return formats.localize(timezone.localtime(obj.now()))
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   102
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   103
    def upcoming (self, obj) :
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   104
        item = obj.upcoming_item()
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   105
        if item :
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   106
            return "%s %s" % (formats.localize(timezone.localtime(item.published)), item)
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   107
        else :
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   108
            return ""
4
d56f48f6c94b URL: show qrcode for URL in admin (using google charts)
Tero Marttila <terom@fixme.fi>
parents: 3
diff changeset
   109
d56f48f6c94b URL: show qrcode for URL in admin (using google charts)
Tero Marttila <terom@fixme.fi>
parents: 3
diff changeset
   110
    readonly_fields = (
47
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   111
        'timezone',
4
d56f48f6c94b URL: show qrcode for URL in admin (using google charts)
Tero Marttila <terom@fixme.fi>
parents: 3
diff changeset
   112
        'qrcode_url',
d56f48f6c94b URL: show qrcode for URL in admin (using google charts)
Tero Marttila <terom@fixme.fi>
parents: 3
diff changeset
   113
        'qrcode_img',
7
8202196630d3 better reporting of active/upcoming items
Tero Marttila <terom@fixme.fi>
parents: 6
diff changeset
   114
        'active_item',
8202196630d3 better reporting of active/upcoming items
Tero Marttila <terom@fixme.fi>
parents: 6
diff changeset
   115
        'upcoming_item',
47
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   116
        'active',
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   117
        'now',
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   118
        'upcoming',
4
d56f48f6c94b URL: show qrcode for URL in admin (using google charts)
Tero Marttila <terom@fixme.fi>
parents: 3
diff changeset
   119
    )
2
e838dda048a6 qrurls: basic functionality
Tero Marttila <terom@fixme.fi>
parents:
diff changeset
   120
    list_display = (
11
b975f7973402 show absolute url for url admin
Tero Marttila <terom@fixme.fi>
parents: 8
diff changeset
   121
        'get_absolute_url',
8
5c8a19fa6a29 display active/upcoming in list
Tero Marttila <terom@fixme.fi>
parents: 7
diff changeset
   122
        'active_item',
5c8a19fa6a29 display active/upcoming in list
Tero Marttila <terom@fixme.fi>
parents: 7
diff changeset
   123
        'upcoming_item',
2
e838dda048a6 qrurls: basic functionality
Tero Marttila <terom@fixme.fi>
parents:
diff changeset
   124
    )
4
d56f48f6c94b URL: show qrcode for URL in admin (using google charts)
Tero Marttila <terom@fixme.fi>
parents: 3
diff changeset
   125
    fieldsets = (
d56f48f6c94b URL: show qrcode for URL in admin (using google charts)
Tero Marttila <terom@fixme.fi>
parents: 3
diff changeset
   126
        (None, {
47
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   127
            'fields': (
63
9d1184cdbd16 make URLFeed publishing_offset user-defineable in days
Tero Marttila <terom@fixme.fi>
parents: 61
diff changeset
   128
                'shorturl',
9d1184cdbd16 make URLFeed publishing_offset user-defineable in days
Tero Marttila <terom@fixme.fi>
parents: 61
diff changeset
   129
                'publishing_days',
47
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   130
                'publishing_time', 
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   131
                'timezone',
51
6f35a169ef01 Make URLImage title optional, use URLFeed title
Tero Marttila <terom@fixme.fi>
parents: 50
diff changeset
   132
                'title',
47
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   133
            ),
4
d56f48f6c94b URL: show qrcode for URL in admin (using google charts)
Tero Marttila <terom@fixme.fi>
parents: 3
diff changeset
   134
        }),
d56f48f6c94b URL: show qrcode for URL in admin (using google charts)
Tero Marttila <terom@fixme.fi>
parents: 3
diff changeset
   135
        ("QRCode", {
7
8202196630d3 better reporting of active/upcoming items
Tero Marttila <terom@fixme.fi>
parents: 6
diff changeset
   136
            'fields': ('qrcode_url', 'qrcode_img'),
4
d56f48f6c94b URL: show qrcode for URL in admin (using google charts)
Tero Marttila <terom@fixme.fi>
parents: 3
diff changeset
   137
        }),
47
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   138
        ("Item publishing overview", {
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   139
            'fields': (
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   140
                'active',
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   141
                'now',
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   142
                'upcoming',
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   143
            ),
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   144
        }),
4
d56f48f6c94b URL: show qrcode for URL in admin (using google charts)
Tero Marttila <terom@fixme.fi>
parents: 3
diff changeset
   145
    )
3
e7d3f1a28b24 touchup admin ui
Tero Marttila <terom@fixme.fi>
parents: 2
diff changeset
   146
    inlines = (URLItemInline, )
2
e838dda048a6 qrurls: basic functionality
Tero Marttila <terom@fixme.fi>
parents:
diff changeset
   147
49
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   148
    ## Import
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   149
    class ImportForm (forms.Form) :
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   150
        shorturl = forms.ModelChoiceField(URL.objects)
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   151
        publishing_date = forms.DateField(widget=admin.widgets.AdminDateWidget)
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   152
        publishing_time = forms.TimeField(widget=admin.widgets.AdminTimeWidget)
89
187e874f770c qrurls.admin: URLAdmin: support use of existing URLImages in import
Tero Marttila <terom@paivola.fi>
parents: 66
diff changeset
   153
        
55
eb36c4d046c1 use <input type=file multiple> to handle multi-file uploads
Tero Marttila <terom@fixme.fi>
parents: 54
diff changeset
   154
        # Django forms doesn't really understand multi-value fields, so we just hack this ourselves.
eb36c4d046c1 use <input type=file multiple> to handle multi-file uploads
Tero Marttila <terom@fixme.fi>
parents: 54
diff changeset
   155
        #image = forms.ImageField()
49
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   156
89
187e874f770c qrurls.admin: URLAdmin: support use of existing URLImages in import
Tero Marttila <terom@paivola.fi>
parents: 66
diff changeset
   157
        # Existing URLImages
90
a193a8dd737c qrurls.admin: minor usability tweaks to the import url_images select-multiple...
Tero Marttila <terom@paivola.fi>
parents: 89
diff changeset
   158
        url_images = forms.ModelMultipleChoiceField(queryset=URLImage.objects.all(),
a193a8dd737c qrurls.admin: minor usability tweaks to the import url_images select-multiple...
Tero Marttila <terom@paivola.fi>
parents: 89
diff changeset
   159
                widget  = forms.SelectMultiple(attrs={'size': 20}),
a193a8dd737c qrurls.admin: minor usability tweaks to the import url_images select-multiple...
Tero Marttila <terom@paivola.fi>
parents: 89
diff changeset
   160
        )
89
187e874f770c qrurls.admin: URLAdmin: support use of existing URLImages in import
Tero Marttila <terom@paivola.fi>
parents: 66
diff changeset
   161
55
eb36c4d046c1 use <input type=file multiple> to handle multi-file uploads
Tero Marttila <terom@fixme.fi>
parents: 54
diff changeset
   162
    def import_images_handler (self, url_feed, data, images=()) :
49
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   163
        """Custom for backend for mass-importing images into a feed."""
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   164
        
56
96e1c616a955 move publishing schedule to URL model, make timetz aware
Tero Marttila <terom@fixme.fi>
parents: 55
diff changeset
   165
        # series of datetimes
96e1c616a955 move publishing schedule to URL model, make timetz aware
Tero Marttila <terom@fixme.fi>
parents: 55
diff changeset
   166
        publishing = url_feed.apply_publishing_schedule(
89
187e874f770c qrurls.admin: URLAdmin: support use of existing URLImages in import
Tero Marttila <terom@paivola.fi>
parents: 66
diff changeset
   167
            data['publishing_date'], data['publishing_time'], url_feed.publishing_offset)
49
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   168
89
187e874f770c qrurls.admin: URLAdmin: support use of existing URLImages in import
Tero Marttila <terom@paivola.fi>
parents: 66
diff changeset
   169
        url_images = list(data['url_images'])
187e874f770c qrurls.admin: URLAdmin: support use of existing URLImages in import
Tero Marttila <terom@paivola.fi>
parents: 66
diff changeset
   170
        
187e874f770c qrurls.admin: URLAdmin: support use of existing URLImages in import
Tero Marttila <terom@paivola.fi>
parents: 66
diff changeset
   171
        for image in images:
55
eb36c4d046c1 use <input type=file multiple> to handle multi-file uploads
Tero Marttila <terom@fixme.fi>
parents: 54
diff changeset
   172
            url_image = URLImage(image=image)
49
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   173
            url_image.save()
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   174
89
187e874f770c qrurls.admin: URLAdmin: support use of existing URLImages in import
Tero Marttila <terom@paivola.fi>
parents: 66
diff changeset
   175
            url_images.append(url_image)
187e874f770c qrurls.admin: URLAdmin: support use of existing URLImages in import
Tero Marttila <terom@paivola.fi>
parents: 66
diff changeset
   176
        
187e874f770c qrurls.admin: URLAdmin: support use of existing URLImages in import
Tero Marttila <terom@paivola.fi>
parents: 66
diff changeset
   177
        # randomize
187e874f770c qrurls.admin: URLAdmin: support use of existing URLImages in import
Tero Marttila <terom@paivola.fi>
parents: 66
diff changeset
   178
        random.shuffle(url_images)
187e874f770c qrurls.admin: URLAdmin: support use of existing URLImages in import
Tero Marttila <terom@paivola.fi>
parents: 66
diff changeset
   179
187e874f770c qrurls.admin: URLAdmin: support use of existing URLImages in import
Tero Marttila <terom@paivola.fi>
parents: 66
diff changeset
   180
        for url_image, publish in zip(url_images, publishing) :
49
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   181
            url_item = URLItem(shorturl=url_feed, published=publish, image=url_image)
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   182
            url_item.save()
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   183
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   184
        return http.HttpResponseRedirect(reverse('admin:qrurls_url_change', args=(url_feed.id, )))
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   185
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   186
    def import_images (self, request, shorturl_id) :
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   187
        """Custom form frontend for mass-importing images into a feed."""
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   188
        url_feed = URL.objects.get(id=int(shorturl_id))
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   189
        
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   190
        if request.method == 'POST' :
55
eb36c4d046c1 use <input type=file multiple> to handle multi-file uploads
Tero Marttila <terom@fixme.fi>
parents: 54
diff changeset
   191
            form = self.ImportForm(request.POST)
eb36c4d046c1 use <input type=file multiple> to handle multi-file uploads
Tero Marttila <terom@fixme.fi>
parents: 54
diff changeset
   192
            images = request.FILES.getlist('image')
49
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   193
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   194
            if form.is_valid() :
55
eb36c4d046c1 use <input type=file multiple> to handle multi-file uploads
Tero Marttila <terom@fixme.fi>
parents: 54
diff changeset
   195
                return self.import_images_handler(url_feed, form.cleaned_data, images)
49
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   196
        else :
56
96e1c616a955 move publishing schedule to URL model, make timetz aware
Tero Marttila <terom@fixme.fi>
parents: 55
diff changeset
   197
            publishing_date, publishing_time, publishing_offset = url_feed.publishing_schedule()
49
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   198
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   199
            form = self.ImportForm(initial=dict(
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   200
                shorturl        = url_feed.id,
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   201
                publishing_date = publishing_date,
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   202
                publishing_time = publishing_time,
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   203
            ))
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   204
        
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   205
        return shortcuts.render(request, 'admin/qrurls_import_images.html', dict(
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   206
            current_app     = self.admin_site.name,
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   207
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   208
            media           = self.media + form.media,
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   209
            form_url        = reverse('admin:qrurls_import_images', kwargs=dict(shorturl_id=url_feed.id)),
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   210
            form            = form,
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   211
        ))
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   212
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   213
    def get_urls (self) :
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   214
        return urls.patterns('',
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   215
            urls.url(r'^(?P<shorturl_id>\d+)/import$', self.admin_site.admin_view(self.import_images),
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   216
                name='qrurls_import_images'),
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   217
        ) + super(URLAdmin, self).get_urls()
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   218
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   219
    def import_images_action (self, request, queryset) :
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   220
        url_feed, = [url_feed for url_feed in queryset]
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   221
        return http.HttpResponseRedirect(
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   222
                reverse('admin:qrurls_import_images', kwargs=dict(shorturl_id=url_feed.id)) # ) + '?' + urllib.urlencode(qs, doseq=True)
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   223
        )
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   224
    import_images_action.short_description = "Import images for selected feeds"
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   225
    
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   226
    actions = [import_images_action]
7c0dcf6603f5 urlfeed admin: custom image import form
Tero Marttila <terom@fixme.fi>
parents: 47
diff changeset
   227
2
e838dda048a6 qrurls: basic functionality
Tero Marttila <terom@fixme.fi>
parents:
diff changeset
   228
class URLItemAdmin (admin.ModelAdmin) :
e838dda048a6 qrurls: basic functionality
Tero Marttila <terom@fixme.fi>
parents:
diff changeset
   229
    list_display = (
47
35a8e63648c8 clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
Tero Marttila <terom@fixme.fi>
parents: 45
diff changeset
   230
        'shorturl', 'published_state', 'get_absolute_url', 'image', 'published',
3
e7d3f1a28b24 touchup admin ui
Tero Marttila <terom@fixme.fi>
parents: 2
diff changeset
   231
    )
61
c961c16a19dd urlitem published should not be readonly
Tero Marttila <terom@fixme.fi>
parents: 56
diff changeset
   232
    readonly_fields = ('published_state', )
3
e7d3f1a28b24 touchup admin ui
Tero Marttila <terom@fixme.fi>
parents: 2
diff changeset
   233
    fieldsets = (
e7d3f1a28b24 touchup admin ui
Tero Marttila <terom@fixme.fi>
parents: 2
diff changeset
   234
        ("Publishing", {
e7d3f1a28b24 touchup admin ui
Tero Marttila <terom@fixme.fi>
parents: 2
diff changeset
   235
            'fields': ('shorturl', 'published', ),
e7d3f1a28b24 touchup admin ui
Tero Marttila <terom@fixme.fi>
parents: 2
diff changeset
   236
        }),
e7d3f1a28b24 touchup admin ui
Tero Marttila <terom@fixme.fi>
parents: 2
diff changeset
   237
        ("Target", {
32
82eae853e240 tie in URLImage to URLItem
Tero Marttila <terom@fixme.fi>
parents: 30
diff changeset
   238
            'fields': ('url', 'image'),
3
e7d3f1a28b24 touchup admin ui
Tero Marttila <terom@fixme.fi>
parents: 2
diff changeset
   239
        }),
2
e838dda048a6 qrurls: basic functionality
Tero Marttila <terom@fixme.fi>
parents:
diff changeset
   240
    )
e838dda048a6 qrurls: basic functionality
Tero Marttila <terom@fixme.fi>
parents:
diff changeset
   241
30
d0f56303d8de URLImage
Tero Marttila <terom@fixme.fi>
parents: 27
diff changeset
   242
class URLImageAdmin (admin.ModelAdmin) :
64
b968f33d780c show uploaded URLImage in admin
Tero Marttila <terom@fixme.fi>
parents: 63
diff changeset
   243
    def show_image(self, obj):
b968f33d780c show uploaded URLImage in admin
Tero Marttila <terom@fixme.fi>
parents: 63
diff changeset
   244
        return """<img src="{url}" style="max-width: 480px"/>""".format(url=obj.get_absolute_url())
b968f33d780c show uploaded URLImage in admin
Tero Marttila <terom@fixme.fi>
parents: 63
diff changeset
   245
    show_image.short_description = "Image"
b968f33d780c show uploaded URLImage in admin
Tero Marttila <terom@fixme.fi>
parents: 63
diff changeset
   246
    show_image.allow_tags = True
b968f33d780c show uploaded URLImage in admin
Tero Marttila <terom@fixme.fi>
parents: 63
diff changeset
   247
38
ed2fd40bb3f4 improved URLImage admin
Tero Marttila <terom@fixme.fi>
parents: 32
diff changeset
   248
    list_display = (
66
8ca335eab457 store filename for URLImages
Tero Marttila <terom@fixme.fi>
parents: 65
diff changeset
   249
        'name',
39
e0f2c1597f87 fixup urlimage admin list_display order
Tero Marttila <terom@fixme.fi>
parents: 38
diff changeset
   250
        'uploaded',
38
ed2fd40bb3f4 improved URLImage admin
Tero Marttila <terom@fixme.fi>
parents: 32
diff changeset
   251
    )
30
d0f56303d8de URLImage
Tero Marttila <terom@fixme.fi>
parents: 27
diff changeset
   252
    # hide the "uploaded" field
38
ed2fd40bb3f4 improved URLImage admin
Tero Marttila <terom@fixme.fi>
parents: 32
diff changeset
   253
    fields = (
ed2fd40bb3f4 improved URLImage admin
Tero Marttila <terom@fixme.fi>
parents: 32
diff changeset
   254
        'image',
66
8ca335eab457 store filename for URLImages
Tero Marttila <terom@fixme.fi>
parents: 65
diff changeset
   255
        'name',
8ca335eab457 store filename for URLImages
Tero Marttila <terom@fixme.fi>
parents: 65
diff changeset
   256
        'uploaded',
40
5eefe5a294dc qrurl image title
Tero Marttila <terom@fixme.fi>
parents: 39
diff changeset
   257
        'title',
64
b968f33d780c show uploaded URLImage in admin
Tero Marttila <terom@fixme.fi>
parents: 63
diff changeset
   258
        'show_image',
38
ed2fd40bb3f4 improved URLImage admin
Tero Marttila <terom@fixme.fi>
parents: 32
diff changeset
   259
    )
30
d0f56303d8de URLImage
Tero Marttila <terom@fixme.fi>
parents: 27
diff changeset
   260
    readonly_fields = (
66
8ca335eab457 store filename for URLImages
Tero Marttila <terom@fixme.fi>
parents: 65
diff changeset
   261
        'name',
30
d0f56303d8de URLImage
Tero Marttila <terom@fixme.fi>
parents: 27
diff changeset
   262
        'uploaded',
64
b968f33d780c show uploaded URLImage in admin
Tero Marttila <terom@fixme.fi>
parents: 63
diff changeset
   263
        'show_image',
30
d0f56303d8de URLImage
Tero Marttila <terom@fixme.fi>
parents: 27
diff changeset
   264
    )
32
82eae853e240 tie in URLImage to URLItem
Tero Marttila <terom@fixme.fi>
parents: 30
diff changeset
   265
    
82eae853e240 tie in URLImage to URLItem
Tero Marttila <terom@fixme.fi>
parents: 30
diff changeset
   266
    inlines = (URLItemInline, )
30
d0f56303d8de URLImage
Tero Marttila <terom@fixme.fi>
parents: 27
diff changeset
   267
2
e838dda048a6 qrurls: basic functionality
Tero Marttila <terom@fixme.fi>
parents:
diff changeset
   268
admin.site.register(URL, URLAdmin)
e838dda048a6 qrurls: basic functionality
Tero Marttila <terom@fixme.fi>
parents:
diff changeset
   269
admin.site.register(URLItem, URLItemAdmin)
32
82eae853e240 tie in URLImage to URLItem
Tero Marttila <terom@fixme.fi>
parents: 30
diff changeset
   270
admin.site.register(URLImage, URLImageAdmin)