Dodanie sorl.thumbnail i generowanie miniaturek logo na potrzeby aplikacji sponsors.
[wolnelektury.git] / apps / sorl / thumbnail / templatetags / thumbnail.py
diff --git a/apps/sorl/thumbnail/templatetags/thumbnail.py b/apps/sorl/thumbnail/templatetags/thumbnail.py
new file mode 100755 (executable)
index 0000000..e7c2177
--- /dev/null
@@ -0,0 +1,251 @@
+import re
+import math
+from django.template import Library, Node, VariableDoesNotExist, \
+    TemplateSyntaxError
+from sorl.thumbnail.main import DjangoThumbnail, get_thumbnail_setting
+from sorl.thumbnail.processors import dynamic_import, get_valid_options
+from sorl.thumbnail.utils import split_args
+
+register = Library()
+
+size_pat = re.compile(r'(\d+)x(\d+)$')
+
+filesize_formats = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
+filesize_long_formats = {
+    'k': 'kilo', 'M': 'mega', 'G': 'giga', 'T': 'tera', 'P': 'peta',
+    'E': 'exa', 'Z': 'zetta', 'Y': 'yotta',
+}
+
+try:
+    PROCESSORS = dynamic_import(get_thumbnail_setting('PROCESSORS'))
+    VALID_OPTIONS = get_valid_options(PROCESSORS)
+except:
+    if get_thumbnail_setting('DEBUG'):
+        raise
+    else:
+        PROCESSORS = []
+        VALID_OPTIONS = []
+TAG_SETTINGS = ['quality']
+
+
+class ThumbnailNode(Node):
+    def __init__(self, source_var, size_var, opts=None,
+                 context_name=None, **kwargs):
+        self.source_var = source_var
+        self.size_var = size_var
+        self.opts = opts
+        self.context_name = context_name
+        self.kwargs = kwargs
+
+    def render(self, context):
+        # Note that this isn't a global constant because we need to change the
+        # value for tests.
+        DEBUG = get_thumbnail_setting('DEBUG')
+        try:
+            # A file object will be allowed in DjangoThumbnail class
+            relative_source = self.source_var.resolve(context)
+        except VariableDoesNotExist:
+            if DEBUG:
+                raise VariableDoesNotExist("Variable '%s' does not exist." %
+                        self.source_var)
+            else:
+                relative_source = None
+        try:
+            requested_size = self.size_var.resolve(context)
+        except VariableDoesNotExist:
+            if DEBUG:
+                raise TemplateSyntaxError("Size argument '%s' is not a"
+                        " valid size nor a valid variable." % self.size_var)
+            else:
+                requested_size = None
+        # Size variable can be either a tuple/list of two integers or a valid
+        # string, only the string is checked.
+        else:
+            if isinstance(requested_size, basestring):
+                m = size_pat.match(requested_size)
+                if m:
+                    requested_size = (int(m.group(1)), int(m.group(2)))
+                elif DEBUG:
+                    raise TemplateSyntaxError("Variable '%s' was resolved but "
+                            "'%s' is not a valid size." %
+                            (self.size_var, requested_size))
+                else:
+                    requested_size = None
+        if relative_source is None or requested_size is None:
+            thumbnail = ''
+        else:
+            try:
+                kwargs = {}
+                for key, value in self.kwargs.items():
+                    kwargs[key] = value.resolve(context)
+                opts = dict([(k, v and v.resolve(context))
+                             for k, v in self.opts.items()])
+                thumbnail = DjangoThumbnail(relative_source, requested_size,
+                                opts=opts, processors=PROCESSORS, **kwargs)
+            except:
+                if DEBUG:
+                    raise
+                else:
+                    thumbnail = ''
+        # Return the thumbnail class, or put it on the context
+        if self.context_name is None:
+            return thumbnail
+        # We need to get here so we don't have old values in the context
+        # variable.
+        context[self.context_name] = thumbnail
+        return ''
+
+
+def thumbnail(parser, token):
+    """
+    Creates a thumbnail of for an ImageField.
+
+    To just output the absolute url to the thumbnail::
+
+        {% thumbnail image 80x80 %}
+
+    After the image path and dimensions, you can put any options::
+
+        {% thumbnail image 80x80 quality=95 crop %}
+
+    To put the DjangoThumbnail class on the context instead of just rendering
+    the absolute url, finish the tag with ``as [context_var_name]``::
+
+        {% thumbnail image 80x80 as thumb %}
+        {{ thumb.width }} x {{ thumb.height }}
+    """
+    args = token.split_contents()
+    tag = args[0]
+    # Check to see if we're setting to a context variable.
+    if len(args) > 4 and args[-2] == 'as':
+        context_name = args[-1]
+        args = args[:-2]
+    else:
+        context_name = None
+
+    if len(args) < 3:
+        raise TemplateSyntaxError("Invalid syntax. Expected "
+            "'{%% %s source size [option1 option2 ...] %%}' or "
+            "'{%% %s source size [option1 option2 ...] as variable %%}'" %
+            (tag, tag))
+
+    # Get the source image path and requested size.
+    source_var = parser.compile_filter(args[1])
+    # If the size argument was a correct static format, wrap it in quotes so
+    # that it is compiled correctly.
+    m = size_pat.match(args[2])
+    if m:
+        args[2] = '"%s"' % args[2]
+    size_var = parser.compile_filter(args[2])
+
+    # Get the options.
+    args_list = split_args(args[3:]).items()
+
+    # Check the options.
+    opts = {}
+    kwargs = {} # key,values here override settings and defaults
+
+    for arg, value in args_list:
+        value = value and parser.compile_filter(value)
+        if arg in TAG_SETTINGS and value is not None:
+            kwargs[str(arg)] = value
+            continue
+        if arg in VALID_OPTIONS:
+            opts[arg] = value
+        else:
+            raise TemplateSyntaxError("'%s' tag received a bad argument: "
+                                      "'%s'" % (tag, arg))
+    return ThumbnailNode(source_var, size_var, opts=opts,
+                         context_name=context_name, **kwargs)
+
+
+def filesize(bytes, format='auto1024'):
+    """
+    Returns the number of bytes in either the nearest unit or a specific unit
+    (depending on the chosen format method).
+
+    Acceptable formats are:
+
+    auto1024, auto1000
+      convert to the nearest unit, appending the abbreviated unit name to the
+      string (e.g. '2 KiB' or '2 kB').
+      auto1024 is the default format.
+    auto1024long, auto1000long
+      convert to the nearest multiple of 1024 or 1000, appending the correctly
+      pluralized unit name to the string (e.g. '2 kibibytes' or '2 kilobytes').
+    kB, MB, GB, TB, PB, EB, ZB or YB
+      convert to the exact unit (using multiples of 1000).
+    KiB, MiB, GiB, TiB, PiB, EiB, ZiB or YiB
+      convert to the exact unit (using multiples of 1024).
+
+    The auto1024 and auto1000 formats return a string, appending the correct
+    unit to the value. All other formats return the floating point value.
+
+    If an invalid format is specified, the bytes are returned unchanged.
+    """
+    format_len = len(format)
+    # Check for valid format
+    if format_len in (2, 3):
+        if format_len == 3 and format[0] == 'K':
+            format = 'k%s' % format[1:]
+        if not format[-1] == 'B' or format[0] not in filesize_formats:
+            return bytes
+        if format_len == 3 and format[1] != 'i':
+            return bytes
+    elif format not in ('auto1024', 'auto1000',
+                        'auto1024long', 'auto1000long'):
+        return bytes
+    # Check for valid bytes
+    try:
+        bytes = long(bytes)
+    except (ValueError, TypeError):
+        return bytes
+
+    # Auto multiple of 1000 or 1024
+    if format.startswith('auto'):
+        if format[4:8] == '1000':
+            base = 1000
+        else:
+            base = 1024
+        logarithm = bytes and math.log(bytes, base) or 0
+        index = min(int(logarithm) - 1, len(filesize_formats) - 1)
+        if index >= 0:
+            if base == 1000:
+                bytes = bytes and bytes / math.pow(1000, index + 1)
+            else:
+                bytes = bytes >> (10 * (index))
+                bytes = bytes and bytes / 1024.0
+            unit = filesize_formats[index]
+        else:
+            # Change the base to 1000 so the unit will just output 'B' not 'iB'
+            base = 1000
+            unit = ''
+        if bytes >= 10 or ('%.1f' % bytes).endswith('.0'):
+            bytes = '%.0f' % bytes
+        else:
+            bytes = '%.1f' % bytes
+        if format.endswith('long'):
+            unit = filesize_long_formats.get(unit, '')
+            if base == 1024 and unit:
+                unit = '%sbi' % unit[:2]
+            unit = '%sbyte%s' % (unit, bytes != '1' and 's' or '')
+        else:
+            unit = '%s%s' % (base == 1024 and unit.upper() or unit,
+                             base == 1024 and 'iB' or 'B')
+
+        return '%s %s' % (bytes, unit)
+
+    if bytes == 0:
+        return bytes
+    base = filesize_formats.index(format[0]) + 1
+    # Exact multiple of 1000
+    if format_len == 2:
+        return bytes / (1000.0 ** base)
+    # Exact multiple of 1024
+    elif format_len == 3:
+        bytes = bytes >> (10 * (base - 1))
+        return bytes / 1024.0
+
+
+register.tag(thumbnail)
+register.filter(filesize)