Browse Source

Merge pull request #345 from saimn/special-chars

Quote special characters in urls
pull/349/head
Simon Conseil 8 years ago committed by GitHub
parent
commit
8b38f32b8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      .gitignore
  2. 3
      setup.cfg
  3. 51
      sigal/gallery.py
  4. 30
      sigal/themes/colorbox/templates/album_list.html
  5. 11
      sigal/themes/colorbox/templates/media.html
  6. 12
      sigal/themes/galleria/templates/album.html
  7. 15
      sigal/themes/photoswipe/templates/album_list.html
  8. 9
      sigal/utils.py
  9. BIN
      tests/sample/pictures/accentué/test?<special> chars#.jpg
  10. 9
      tests/test_gallery.py

3
.gitignore vendored

@ -3,6 +3,7 @@
*.pyc
*_flymake
.coverage
.DS_Store
.pytest_cache/
.ropeproject/
.tox/
@ -13,4 +14,4 @@ dist/
docs/_build
htmlcov/
output/
.DS_Store
tags

3
setup.cfg

@ -6,3 +6,6 @@ ignore =
.coveragerc
tests
readthedocs.yml
[flake8]
ignore = E731

51
sigal/gallery.py

@ -60,6 +60,7 @@ class Media:
- ``filename``: Filename of the resized image.
- ``thumbnail``: Location of the corresponding thumbnail image.
- ``big``: If not None, location of the unmodified image.
- ``big_url``: If not None, url of the unmodified image.
- ``exif``: If not None contains a dict with the most common tags. For more
information, see :ref:`simple-exif-data`.
- ``raw_exif``: If not ``None``, it contains the raw EXIF tags.
@ -69,7 +70,7 @@ class Media:
type = ''
def __init__(self, filename, path, settings):
self.src_filename = self.filename = self.url = filename
self.src_filename = self.filename = filename
self.path = path
self.settings = settings
self.ext = os.path.splitext(filename)[1].lower()
@ -82,6 +83,9 @@ class Media:
self.logger = logging.getLogger(__name__)
self._get_metadata()
# default: title is the filename
if not self.title:
self.title = self.filename
signals.media_initialized.send(self)
def __repr__(self):
@ -90,6 +94,11 @@ class Media:
def __str__(self):
return join(self.path, self.filename)
@property
def url(self):
"""URL of the media."""
return url_from_path(self.filename)
@property
def big(self):
"""Path to the original image, if ``keep_orig`` is set (relative to the
@ -104,9 +113,14 @@ class Media:
check_or_create_dir(orig_path)
big_path = join(orig_path, self.src_filename)
if not isfile(big_path):
copy(self.src_path, big_path,
symlink=s['orig_link'])
return url_from_path(join(s['orig_dir'], self.src_filename))
copy(self.src_path, big_path, symlink=s['orig_link'])
return join(s['orig_dir'], self.src_filename)
@property
def big_url(self):
"""URL of the original media."""
if self.big is not None:
return url_from_path(self.big)
@property
def thumbnail(self):
@ -219,7 +233,7 @@ class Video(Media):
if not settings['use_orig'] or not is_valid_html5_video(ext):
video_format = settings['video_format']
ext = '.' + video_format
self.filename = self.url = base + ext
self.filename = base + ext
self.mime = get_mime(ext)
self.dst_path = join(settings['destination'], path, base + ext)
else:
@ -409,16 +423,16 @@ class Album:
if self._thumbnail:
# stop if it is already set
return url_from_path(self._thumbnail)
return self._thumbnail
# Test the thumbnail from the Markdown file.
thumbnail = self.meta.get('thumbnail', [''])[0]
if thumbnail and isfile(join(self.src_path, thumbnail)):
self._thumbnail = join(self.name, get_thumb(self.settings,
thumbnail))
self._thumbnail = url_from_path(join(
self.name, get_thumb(self.settings, thumbnail)))
self.logger.debug("Thumbnail for %r : %s", self, self._thumbnail)
return url_from_path(self._thumbnail)
return self._thumbnail
else:
# find and return the first landscape image
for f in self.medias:
@ -431,17 +445,19 @@ class Album:
size = get_size(f.src_path)
if size['width'] > size['height']:
self._thumbnail = join(self.name, f.thumbnail)
self._thumbnail = (url_quote(self.name) + '/' +
f.thumbnail)
self.logger.debug(
"Use 1st landscape image as thumbnail for %r :"
" %s", self, self._thumbnail)
return url_from_path(self._thumbnail)
"Use 1st landscape image as thumbnail for %r : %s",
self, self._thumbnail)
return self._thumbnail
# else simply return the 1st media file
if not self._thumbnail and self.medias:
for media in self.medias:
if media.thumbnail is not None:
self._thumbnail = join(self.name, media.thumbnail)
self._thumbnail = (url_quote(self.name) + '/' +
media.thumbnail)
break
else:
self.logger.warning("No thumbnail found for %r", self)
@ -449,17 +465,18 @@ class Album:
self.logger.debug("Use the 1st image as thumbnail for %r : %s",
self, self._thumbnail)
return url_from_path(self._thumbnail)
return self._thumbnail
# use the thumbnail of their sub-directories
if not self._thumbnail:
for path, album in self.gallery.get_albums(self.path):
if album.thumbnail:
self._thumbnail = join(self.name, album.thumbnail)
self._thumbnail = (url_quote(self.name) + '/' +
album.thumbnail)
self.logger.debug(
"Using thumbnail from sub-directory for %r : %s",
self, self._thumbnail)
return url_from_path(self._thumbnail)
return self._thumbnail
self.logger.error('Thumbnail not found for %r', self)
return None

30
sigal/themes/colorbox/templates/album_list.html

@ -35,7 +35,7 @@
{% if album.medias %}
{% macro img_description(media) -%}
{% if media.big %} data-big="{{ media.big }}"{% endif %}
{% if media.big %} data-big="{{ media.big_url }}"{% endif %}
{% if media.exif %}
{% if media.exif.datetime %}
data-date=", {{ media.exif.datetime }}"
@ -48,39 +48,37 @@
{% if loop.index % nb_columns == 1 %}
<div id="albums" class="row">
{% endif%}
{% set media_title = media.title if media.title else media.filename %}
{% if media.type == "image" %}
<div class="{{ column_size_t }} columns thumbnail">
{% if 'sigal.plugins.media_page' in settings.plugins %}
<a href="{{ media.filename}}.html" class="gallery"
title="{{ media_title }}"
data-href="{{ media.filename }}" {{ img_description(media) }}>
<a href="{{ media.url}}.html" class="gallery"
title="{{ media.title }}"
data-href="{{ media.url }}" {{ img_description(media) }}>
{% else %}
<a href="{{ media.filename }}" class="gallery"
title="{{ media_title }}"
<a href="{{ media.url }}" class="gallery" title="{{ media.title }}"
{{ img_description(media) }}>
{% endif %}
<img src="{{ media.thumbnail }}" alt="{{ media.filename }}"
title="{{ media_title }}" /></a>
<img src="{{ media.thumbnail }}" alt="{{ media.url }}"
title="{{ media.title }}" /></a>
</div>
{% endif %}
{% if media.type == "video" %}
{% set mhash = media.filename|replace('.', '')|replace(' ', '') %}
{% set mhash = media.url|replace('.', '')|replace(' ', '') %}
<div class="{{ column_size_t }} columns thumbnail">
{% if 'sigal.plugins.media_page' in settings.plugins %}
<a href="{{ media.filename }}.html" data-href="#{{ mhash }}"
<a href="{{ media.url }}.html" data-href="#{{ mhash }}"
{% else %}
<a href="#{{ mhash }}" class="gallery" inline='yes' title="{{ media.filename }}"
<a href="#{{ mhash }}" class="gallery" inline='yes' title="{{ media.url }}"
{% endif %}
{% if media.big %} data-big="{{ media.big }}"{% endif %}>
<img src="{{ media.thumbnail }}" alt="{{ media.filename }}"
title="{{ media_title }}" /></a>
{% if media.big %} data-big="{{ media.big_url }}"{% endif %}>
<img src="{{ media.thumbnail }}" alt="{{ media.url }}"
title="{{ media.title }}" /></a>
</div>
<!-- This contains the hidden content for the video -->
<div style='display:none'>
<div id="{{ mhash }}">
<video controls>
<source src='{{ media.filename }}' type='{{ media.mime }}' />
<source src='{{ media.url }}' type='{{ media.mime }}' />
</video>
</div>
</div>

11
sigal/themes/colorbox/templates/media.html

@ -1,13 +1,12 @@
{% extends "base.html" %}
{% block content %}
{% set media_title = media.title if media.title else media.filename %}
<header>
{% if album.breadcrumb %}
<h2>
{% for url, title in album.breadcrumb %}
<a href="{{ url }}">{{ title }}</a> »
{% endfor -%}
<a href="#">{{ media_title }}</a>
<a href="#">{{ media.title }}</a>
</h2>
<hr>
{% endif %}
@ -16,20 +15,20 @@
{% if media %}
<div class="thumbnail">
{% if media.type == "image" %}
<img src="{{ media.filename }}" alt="{{ media_title }}" title="{{ media_title }}" />
<img src="{{ media.url }}" alt="{{ media.title }}" title="{{ media.title }}" />
{% endif %}
{% if media.type == "video" %}
<video controls>
<source src='{{ media.filename }}' type='video/webm' />
<source src='{{ media.url }}' type='video/webm' />
</video>
{% endif %}
<p>
{% if previous_media %}
<a href="{{ previous_media.filename }}.html">« previous</a>
<a href="{{ previous_media.url }}.html">« previous</a>
{% endif %}
{% if next_media %}
<a href="{{ next_media.filename }}.html">next »</a>
<a href="{{ next_media.url }}.html">next »</a>
{% endif %}
</p>
</div>

12
sigal/themes/galleria/templates/album.html

@ -7,7 +7,7 @@
<style>
body::after{
position:absolute; width:0; height:0; overflow:hidden; z-index:-1;
content:url({{ album.medias[0].filename }});
content:url({{ album.medias[0].url }});
}</style>
{% endif %}
@ -47,7 +47,7 @@
{% block late_js %}
{% macro img_description(media) -%}
{%- if media.big -%}<a href='{{ media.big }}'>Full size</a>{%- endif -%}
{%- if media.big -%}<a href='{{ media.big_url }}'>Full size</a>{%- endif -%}
{# clean up tags and whitespace, including newlines, in the description #}
{%- if media.description -%}<br>{{ media.description | striptags }}{%- endif -%}
{%- if media.exif -%}
@ -81,18 +81,18 @@
var data = [
{% for media in album.medias -%}
{
title: "{{ media.title if media.title else media.filename }}",
title: "{{ media.title }}",
description: "{{ img_description(media) | e }}",
thumb: "{{ media.thumbnail }}",
{% if media.big %}
big: "{{ media.big }}",
big: "{{ media.big_url }}",
{% endif %}
{% if media.type == "image" %}
image: "{{ media.filename }}"
image: "{{ media.url }}"
{% endif %}
{% if media.type == "video" %}
image: "{{ theme.url }}/img/empty.png",
layer: "<video controls><source src='{{ media.filename }}' type='{{ media.mime }}' /></video>"
layer: "<video controls><source src='{{ media.url }}' type='{{ media.mime }}' /></video>"
{% endif %}
},
{% endfor %}

15
sigal/themes/photoswipe/templates/album_list.html

@ -67,27 +67,26 @@
{% if album.medias %}
<div class="gallery" itemscope itemtype="http://schema.org/ImageGallery">
{% for media in album.medias %}
{% set media_title = media.title if media.title else media.filename %}
{% if media.type == "image" %}
{% if loop.first %}
<figure class="gallery__img--main thumbnail" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a href="{{ media.filename }}" itemprop="contentUrl" data-size="{{media.size.width}}x{{media.size.height}}">
<img src="{{ theme.url }}/echo/blank.gif" data-echo="{{ media.filename }}" alt="{{ media.filename }}" itemprop="thumbnail" title="{{ media.exif.datetime }}" />
<a href="{{ media.url }}" itemprop="contentUrl" data-size="{{media.size.width}}x{{media.size.height}}">
<img src="{{ theme.url }}/echo/blank.gif" data-echo="{{ media.url }}" alt="{{ media.url }}" itemprop="thumbnail" title="{{ media.exif.datetime }}" />
</a>
<figcaption itemprop="caption description">{{ media_title }} - {{ media.exif.datetime }}</figcaption>
<figcaption itemprop="caption description">{{ media.title }} - {{ media.exif.datetime }}</figcaption>
</figure>
{% else %}
<figure class="gallery__img--secondary thumbnail" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a href="{{ media.filename }}" itemprop="contentUrl" data-size="{{media.size.width}}x{{media.size.height}}">
<img src="{{ theme.url }}/echo/blank.gif" data-echo="{{ media.thumbnail }}" alt="{{ media.filename }}" itemprop="thumbnail" title="{{ media.exif.datetime }}" />
<a href="{{ media.url }}" itemprop="contentUrl" data-size="{{media.size.width}}x{{media.size.height}}">
<img src="{{ theme.url }}/echo/blank.gif" data-echo="{{ media.thumbnail }}" alt="{{ media.url }}" itemprop="thumbnail" title="{{ media.exif.datetime }}" />
</a>
<figcaption itemprop="caption description">{{ media_title }} - {{ media.exif.datetime }}</figcaption>
<figcaption itemprop="caption description">{{ media.title }} - {{ media.exif.datetime }}</figcaption>
</figure>
{% endif %}
{% endif %}
{% if media.type == "video" %}
<figure class="gallery__img--secondary thumbnail" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a href="{{ media.filename }}" itemprop="contentUrl" data-type="video" data-video='<div class="video"><div class="video__container"><video controls><source src="{{ media.filename }}" type="{{ media.mime }}" /></video></div></div>'>
<a href="{{ media.url }}" itemprop="contentUrl" data-type="video" data-video='<div class="video"><div class="video__container"><video controls><source src="{{ media.url }}" type="{{ media.mime }}" /></video></div></div>'>
<img src="{{ theme.url }}/echo/blank.gif" data-echo="{{ media.thumbnail }}" alt="{{ media.thumbnail }}" itemprop="thumbnail" title="" />
</a>
<figcaption itemprop="caption description">{{ media_title }}</figcaption>

9
sigal/utils.py

@ -23,6 +23,8 @@ import shutil
from markdown import Markdown
from markupsafe import Markup
from urllib.parse import quote
VIDEO_MIMES = {'.mp4': 'video/mp4',
'.webm': 'video/webm',
'.ogv': 'video/ogg'}
@ -58,10 +60,9 @@ def check_or_create_dir(path):
def url_from_path(path):
"""Transform path to url, converting backslashes to slashes if needed."""
if os.sep == '/':
return path
else:
return '/'.join(path.split(os.sep))
if os.sep != '/':
path = '/'.join(path.split(os.sep))
return quote(path)
def read_markdown(filename):

BIN
tests/sample/pictures/accentué/test?<special> chars#.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

9
tests/test_gallery.py

@ -4,6 +4,7 @@ import pytest
import datetime
from os.path import join
from urllib.parse import quote
from sigal.gallery import Album, Media, Image, Video, Gallery
from sigal.video import SubprocessException
@ -53,7 +54,7 @@ REF = {
'accentué': {
'title': 'accentué',
'name': 'accentué',
'thumbnail': 'accentué/thumbnails/hélicoïde.tn.jpg',
'thumbnail': 'accentu%C3%A9/thumbnails/h%C3%A9lico%C3%AFde.tn.jpg',
'subdirs': [],
'medias': ['hélicoïde.jpg', '11.jpg'],
},
@ -61,7 +62,7 @@ REF = {
'title': 'video',
'name': 'video',
'thumbnail': ('video/thumbnails/'
'example video.tn.jpg'),
'example%20video.tn.jpg'),
'subdirs': [],
'medias': ['example video.ogv']
}
@ -99,7 +100,7 @@ def test_media_orig(settings, tmpdir):
m = Video('example video.ogv', 'video', settings)
assert m.filename == 'example video.webm'
assert m.big == 'original/example video.ogv'
assert m.big_url == 'original/example%20video.ogv'
assert os.path.isfile(join(settings['destination'], m.path, m.big))
settings['use_orig'] = True
@ -144,7 +145,7 @@ def test_video(settings, tmpdir):
assert m.dst_path == join(settings['destination'], file_path)
os.makedirs(join(settings['destination'], 'video', 'thumbnails'))
assert m.thumbnail == join('thumbnails', 'example video.tn.jpg')
assert m.thumbnail == join('thumbnails', 'example%20video.tn.jpg')
assert os.path.isfile(m.thumb_path)

Loading…
Cancel
Save