changeset 29:b45621706d83

use Bootstrap carousels to display images and videos galeries: - one carousel for a 3x3 thumbnails grid - a second one in a modal window to view bigger pictures when a thumbnail is clicked
author souliane <souliane@mailoo.org>
date Wed, 21 Jan 2015 20:13:19 +0100
parents 30a1edf90fae
children 565d653a15d3
files README sat_website/screenshots.py sat_website/templatetags/partition.py sat_website/templatetags/utils.py sat_website/views.py static/css/sat_website.css static/js/sat_website.js templates/sat_website/base.html templates/sat_website/gallery.html templates/sat_website/screenshots.html templates/sat_website/screenshots_tech.html
diffstat 11 files changed, 252 insertions(+), 241 deletions(-) [+]
line wrap: on
line diff
--- a/README	Wed Jan 21 20:08:26 2015 +0100
+++ b/README	Wed Jan 21 20:13:19 2015 +0100
@@ -75,7 +75,6 @@
 
     - Bootstrap v3.3.1 (http://getbootstrap.com) | Copyright 2011-2014 Twitter, Inc. | MIT licence: https://github.com/twbs/bootstrap/blob/master/LICENSE
     - jQuery v1.11.1 (http://jquery.org) | Copyright 2005-2014 jQuery Foundation, Inc. | MIT licence: jquery.org/license
-    - Partition (http://djangosnippets.org/snippets/401/) | Copyright 2007 Smiley Chris | Custom free (libre) licence: http://djangosnippets.org/about/tos
 
 In addition, this project use Django to run.
 
--- a/sat_website/screenshots.py	Wed Jan 21 20:08:26 2015 +0100
+++ b/sat_website/screenshots.py	Wed Jan 21 20:13:19 2015 +0100
@@ -23,7 +23,7 @@
 from collections import namedtuple
 
 ImageDesc = namedtuple("ImageDesc","path description")
-VideoDesc = namedtuple("VideoDesc","path description poster")
+VideoDesc = namedtuple("VideoDesc", "path description poster lang subtitles")
 
 #list the pictures
 
@@ -33,16 +33,16 @@
     ImageDesc("images/screenshots/primitivus/primitivus_tarot.png", _(u"Primitivus showing a french Tarot play")),
     ImageDesc("images/screenshots/jp/jp.png", _(u"cowsay sent in conversation through jp")),
     ImageDesc("images/screenshots/wix/wix_tarot.png", _(u"Wix showing a french Tarot play")),
+    VideoDesc("videos/screencasts/présentation_SàT.webm", _(u"The first video show wix, primitivus and jp"), "videos/screencasts/posters/présentation_SàT.jpg", 'fr', ''),
+    VideoDesc("videos/screencasts/présentation_SàT_2.webm", _(u"This video show french Tarot game, and how to use Thunderbird with SàT"), "videos/screencasts/posters/présentation_SàT_2.jpg", 'fr', ''),
+    VideoDesc("videos/screencasts/présentation_SàT_3.webm", _(u"This video focuses on Libervia. The UI is really outdated, but we can see some features"), "videos/screencasts/posters/présentation_SàT_3.jpg", 'fr', ''),
+    VideoDesc("videos/screencasts/présentation_SàT_5_radio_collective.webm", _(u"Demo of the new Libervia UI, and of the collective radio feature"), "videos/screencasts/posters/radiocol.jpg", 'fr', ''),
     ]
 
-screencasts = [
-    VideoDesc("videos/screencasts/présentation_SàT.webm", _(u"The first video show wix, primitivus and jp"), "videos/screencasts/posters/présentation_SàT.jpg"),
-    VideoDesc("videos/screencasts/présentation_SàT_2.webm", _(u"This video show french Tarot game, and how to use Thunderbird with SàT"), "videos/screencasts/posters/présentation_SàT_2.jpg"),
-    VideoDesc("videos/screencasts/présentation_SàT_3.webm", _(u"This video focuses on Libervia. The UI is really outdated, but we can see some features"),  "videos/screencasts/posters/présentation_SàT_3.jpg"),
-    VideoDesc("videos/screencasts/présentation_SàT_4_copie_et_pipe.webm", _(u"How to copy and pipe streams over XMPP"), "videos/screencasts/posters/présentation_SàT_4.jpg"),
-    VideoDesc("videos/screencasts/présentation_SàT_5_radio_collective.webm", _(u"Demo of the new Libervia UI, and of the collective radio feature"), "videos/screencasts/posters/radiocol.jpg"),
-    VideoDesc("videos/screencasts/présentation_SàT_6_export_commande.webm", _(u"Exporting a command: an FTP client is exported to a Gajim contact"), "videos/screencasts/posters/présentation_SàT_6_export_commande.jpg"),
-    VideoDesc("videos/screencasts/présentation_SàT_7_télécommande_universelle.webm", _(u"Use ad-hoc commands to control a VLC player from Libervia"), "videos/screencasts/posters/présentation_SàT_7_télécommande_universelle.png"),
+screenshots_tech = [
+    VideoDesc("videos/screencasts/présentation_SàT_7_télécommande_universelle.webm", _(u"Use ad-hoc commands to control a VLC player from Libervia"), "videos/screencasts/posters/présentation_SàT_7_télécommande_universelle.png", 'fr', ''),
+    VideoDesc("videos/screencasts/présentation_SàT_6_export_commande.webm", _(u"Exporting a command: an FTP client is exported to a Gajim contact"), "videos/screencasts/posters/présentation_SàT_6_export_commande.jpg", 'fr', ''),
+    VideoDesc("videos/screencasts/présentation_SàT_4_copie_et_pipe.webm", _(u"How to copy and pipe streams over XMPP"), "videos/screencasts/posters/présentation_SàT_4.jpg", 'fr', ''),
     ]
 
 
--- a/sat_website/templatetags/partition.py	Wed Jan 21 20:08:26 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,166 +0,0 @@
-"""
-(c) Smiley Chris 2007
-
-This code comes from Django snippets ( http://djangosnippets.org/snippets/401/ )
-According to the Terms of Service ( http://djangosnippets.org/about/tos/ ), the code can be freely used
-
-Template filters to partition lists into rows or columns.
-
-A common use-case is for splitting a list into a table with columns::
-
-    {% load partition %}
-    <table>
-    {% for row in mylist|columns:3 %}
-        <tr>
-        {% for item in row %}
-            <td>{{ item }}</td>
-        {% endfor %}
-        </tr>
-    {% endfor %}
-    </table>
-"""
-
-from django.template import Library
-
-register = Library()
-
-def rows(thelist, n):
-    """
-    Break a list into ``n`` rows, filling up each row to the maximum equal
-    length possible. For example::
-
-        >>> l = range(10)
-
-        >>> rows(l, 2)
-        [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]
-
-        >>> rows(l, 3)
-        [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9]]
-
-        >>> rows(l, 4)
-        [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
-
-        >>> rows(l, 5)
-        [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]]
-
-        >>> rows(l, 9)
-        [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [], [], [], []]
-
-        # This filter will always return `n` rows, even if some are empty:
-        >>> rows(range(2), 3)
-        [[0], [1], []]
-    """
-    try:
-        n = int(n)
-        thelist = list(thelist)
-    except (ValueError, TypeError):
-        return [thelist]
-    list_len = len(thelist)
-    split = list_len // n
-
-    if list_len % n != 0:
-        split += 1
-    return [thelist[split*i:split*(i+1)] for i in range(n)]
-
-def rows_distributed(thelist, n):
-    """
-    Break a list into ``n`` rows, distributing columns as evenly as possible
-    across the rows. For example::
-
-        >>> l = range(10)
-
-        >>> rows_distributed(l, 2)
-        [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]
-
-        >>> rows_distributed(l, 3)
-        [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]
-
-        >>> rows_distributed(l, 4)
-        [[0, 1, 2], [3, 4, 5], [6, 7], [8, 9]]
-
-        >>> rows_distributed(l, 5)
-        [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]]
-
-        >>> rows_distributed(l, 9)
-        [[0, 1], [2], [3], [4], [5], [6], [7], [8], [9]]
-
-        # This filter will always return `n` rows, even if some are empty:
-        >>> rows(range(2), 3)
-        [[0], [1], []]
-    """
-    try:
-        n = int(n)
-        thelist = list(thelist)
-    except (ValueError, TypeError):
-        return [thelist]
-    list_len = len(thelist)
-    split = list_len // n
-
-    remainder = list_len % n
-    offset = 0
-    rows = []
-    for i in range(n):
-        if remainder:
-            start, end = (split+1)*i, (split+1)*(i+1)
-        else:
-            start, end = split*i+offset, split*(i+1)+offset
-        rows.append(thelist[start:end])
-        if remainder:
-            remainder -= 1
-            offset += 1
-    return rows
-
-def columns(thelist, n):
-    """
-    Break a list into ``n`` columns, filling up each column to the maximum equal
-    length possible. For example::
-
-        >>> from pprint import pprint
-        >>> for i in range(7, 11):
-        ...     print '%sx%s:' % (i, 3)
-        ...     pprint(columns(range(i), 3), width=20)
-        7x3:
-        [[0, 3, 6],
-         [1, 4],
-         [2, 5]]
-        8x3:
-        [[0, 3, 6],
-         [1, 4, 7],
-         [2, 5]]
-        9x3:
-        [[0, 3, 6],
-         [1, 4, 7],
-         [2, 5, 8]]
-        10x3:
-        [[0, 4, 8],
-         [1, 5, 9],
-         [2, 6],
-         [3, 7]]
-
-        # Note that this filter does not guarantee that `n` columns will be
-        # present:
-        >>> pprint(columns(range(4), 3), width=10)
-        [[0, 2],
-         [1, 3]]
-    """
-    try:
-        n = int(n)
-        thelist = list(thelist)
-    except (ValueError, TypeError):
-        return [thelist]
-    list_len = len(thelist)
-    split = list_len // n
-    if list_len % n != 0:
-        split += 1
-    return [thelist[i::split] for i in range(split)]
-
-register.filter(rows)
-register.filter(rows_distributed)
-register.filter(columns)
-
-def _test():
-    import doctest
-    doctest.testmod()
-
-if __name__ == "__main__":
-    _test()
--- a/sat_website/templatetags/utils.py	Wed Jan 21 20:08:26 2015 +0100
+++ b/sat_website/templatetags/utils.py	Wed Jan 21 20:13:19 2015 +0100
@@ -12,3 +12,27 @@
     # rely on a strange comparison - isinstance(value, arg) doesn't work here
     return type(value) == tuple
 
+@register.filter
+def buffer(value, n):
+    """Split values in sub-lists of n elements.
+
+    @param value (list): a list object
+    @return: a list containing sub-lists
+
+    For example:
+
+    >>> buffer(range(10), 2)
+    [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]]
+
+    >>> buffer(range(10), 3)
+    [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
+
+    >>> buffer(range(10), 4)
+    [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9]]
+    """
+    result = []
+    index = 0
+    while index < len(value):
+        result.append(value[index:index + n])
+        index += n
+    return result
--- a/sat_website/views.py	Wed Jan 21 20:08:26 2015 +0100
+++ b/sat_website/views.py	Wed Jan 21 20:13:19 2015 +0100
@@ -71,10 +71,8 @@
         return render_to_response('sat_website/overview.html', context)
     elif category == "screenshots":
         context["screenshots"] = screenshots.screenshots
-        context["screencasts"] = screenshots.screencasts
     elif category == "screenshots_tech":
-        context["screenshots"] = screenshots.screencasts
-        context["screencasts"] = screenshots.screencasts
+        context["screenshots"] = screenshots.screenshots_tech
     elif category == "social_contract":
         context["SOCIAL_CONTRACT"] = social_contract.get_social_contract()
 
--- a/static/css/sat_website.css	Wed Jan 21 20:08:26 2015 +0100
+++ b/static/css/sat_website.css	Wed Jan 21 20:13:19 2015 +0100
@@ -36,13 +36,54 @@
     font-size: 13px;
 }
 
+.carousel-inner {
+    padding: 10px 30px 10px 30px;
+    border-radius: 5px;
+}
+
+.carousel-inner > .item {
+    text-align: center;
+}
+
+.carousel-caption {
+    padding: 10px;
+    bottom: -55px;
+    color: black;
+    font-style: italic;
+    font-size: 14px;
+    text-shadow: none;
+}
+
+.carousel-indicators {
+    bottom: -5px;
+    color: black;
+}
+
+.carousel-indicators li {
+    border: 1px solid black;
+}
+
+.carousel-indicators .active {
+    background-color: black;
+}
+
+.carousel-control.left, .carousel-control.right{
+    width: 30px;
+    border-radius: 5px;
+}
+
+.modal-header .close {
+    margin-top: -10px;
+    padding-right: 5px;
+}
+
 
 /* sat_website own styles */
 
 
 #language {
-    /* position: absolute; */
     text-align: center;
+    padding-top: 10px;
 }
 
 #sat_logo {
@@ -96,25 +137,28 @@
 }
 
 div:last-child>div.feature {
- /* we don't wine line on the last feature */
+    /* we don't want the line on the last feature */
 	border-bottom-style: none;
 }
 
-.screenshot_desc {
-	text-align: center;
-	font-style: italic;
-	font-size: 14px;
+#carousel-screenshots img, #carousel-screenshots video {
+    cursor: pointer;
+    height: 200px;
+    width: auto;
+    display: table-cell;
 }
 
-.screencast {
-	text-align: center;
+#modal-screenshots .modal-dialog {
+    max-width: 90%;
+    width: auto;
 }
 
-.screencast_dest {
-	text-align: center;
-	font-style: italic;
-	font-size: 14px;
-	margin-bottom: 30px;
+#carousel-screenshots-modal .carousel-inner {
+    padding-bottom: 80px;
+}
+
+#carousel-screenshots-modal img, #carousel-screenshots-modal video {
+    margin: auto;
 }
 
 #language_form {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/static/js/sat_website.js	Wed Jan 21 20:13:19 2015 +0100
@@ -0,0 +1,24 @@
+$(document).ready(function() {
+
+    // language selector
+    $('#language_select').change(function() {
+        $('#language_form').submit();
+    });
+    
+    // open the big gallery when a thumbnail is clicked
+    $('#carousel-screenshots .thumbnail').click(function() {
+        $('#modal-screenshots').modal('show');
+        index = parseInt($(this).attr('index'));
+        $('#carousel-screenshots-modal').carousel(index);
+        // start playing the big video
+        if ($(this)[0].tagName == 'VIDEO') {
+           $('#carousel-screenshots-modal img, #carousel-screenshots-modal video')[index].play();
+        }
+    });
+    
+    // thumbail's video starts playing when clicked, pause it
+    $('#carousel-screenshots video.thumbnail').each(function(index) {
+        $(this).on('play', function(e) { $(this)[0].pause(); });
+    });
+
+});
--- a/templates/sat_website/base.html	Wed Jan 21 20:08:26 2015 +0100
+++ b/templates/sat_website/base.html	Wed Jan 21 20:13:19 2015 +0100
@@ -58,12 +58,6 @@
     {% block content %}{% endblock %}
     <script src="{% static "js/jquery.min.js" %}"></script>
     <script src="{% static "bootstrap/js/bootstrap.min.js" %}"></script>
-    <script type="text/javascript">
-		$(document).ready(function() {
-		    $('#language_select').change(function() {
-			    $('#language_form').submit();
-			});
-		});
-    </script>
+    <script src="{% static "js/sat_website.js" %}"></script>
 </body>
 </html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/sat_website/gallery.html	Wed Jan 21 20:13:19 2015 +0100
@@ -0,0 +1,105 @@
+{% comment %}
+SàT website: Salut à Toi's presentation website
+Copyright (C) 2012  Jérôme Poisson (goffi@goffi.org)
+
+This file is part of SàT website.
+
+SàT website is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Foobar is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+{% endcomment %}
+
+{% load i18n %}
+{% load staticfiles %}
+{% load utils %}
+
+<div id="carousel-screenshots" class="carousel slide" data-ride="carousel" data-interval="0">
+    {% if screenshots|length > 9 %}
+        <ol class="carousel-indicators">
+            {% for row in screenshots|buffer:9 %}
+                <li data-target="#carousel-screenshots" data-slide-to="{{ forloop.counter0 }}"{% if not forloop.counter0 %} class="active"{% endif %}></li>
+            {% endfor %}
+        </ol>
+    {% endif %}
+    <div class="carousel-inner" role="listbox">
+        {% for row in screenshots|buffer:9 %}
+            <div class="item{% if not forloop.counter0 %} active{% endif %}">
+            {% for screenshot in row %}
+                <div class="col-md-4">
+                    {% if screenshot.poster %}
+	                    <video index="{{ forloop.counter0 }}" class="thumbnail img-responsive" width="640" height="400" poster="{% static screenshot.poster %}" controls="controls" src="{% static screenshot.path %}" preload="none">
+	                    {% blocktrans with firefox="<a href=\"http://www.mozilla-europe.org/fr/\">Firefox</a>" %}Your browser doesn't manage the « video » tag, you should update, e.g. with the last {{ firefox }}{% endblocktrans %}
+	                    </video>
+                    {% else %}
+                        <img index="{{ forloop.counter0 }}" class="thumbnail img-responsive" src="{% static screenshot.path %}">
+                    {% endif %}
+                </div>
+            {% endfor %}
+            </div>
+        {% endfor %}
+        {% if screenshots|length > 9 %}
+	        <a class="left carousel-control" href="#carousel-screenshots" role="button" data-slide="prev">
+	          <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
+	          <span class="sr-only">Previous</span>
+	        </a>
+	        <a class="right carousel-control" href="#carousel-screenshots" role="button" data-slide="next">
+	          <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
+	          <span class="sr-only">Next</span>
+	        </a>
+        {% endif %}
+    </div>
+</div>
+
+<div class="modal" id="modal-screenshots" role="dialog">
+    <div class="modal-dialog">
+        <div class="modal-content">
+            <div class="modal-header">
+                <button class="close" type="button" data-dismiss="modal">×</button>
+            </div>
+            <div class="modal-body">
+                <div id="carousel-screenshots-modal" class="carousel slide" data-ride="carousel" data-interval="0">
+                    {% if screenshots|length > 1 %}
+                        <ol class="carousel-indicators">
+                            {% for screenshot in screenshots %}
+                                <li data-target="#carousel-screenshots-modal" data-slide-to="{{ forloop.counter0 }}"{% if not forloop.counter0 %} class="active"{% endif %}></li>
+                            {% endfor %}
+                        </ol>
+                    {% endif %}
+                    <div class="carousel-inner" role="listbox">
+                        {% for screenshot in screenshots %}
+                            <div class="item{% if not forloop.counter0 %} active{% endif %}">
+                                {% if screenshot.poster %}
+	                                <video class="img-responsive" width="640" height="400" poster="{% static screenshot.poster %}" controls="controls" src="{% static screenshot.path %}" preload="none">
+	                                {% blocktrans with firefox="<a href=\"http://www.mozilla-europe.org/fr/\">Firefox</a>" %}Your browser doesn't manage the « video » tag, you should update, e.g. with the last {{ firefox }}{% endblocktrans %}
+	                                </video>
+                                {% else %}
+                                    <img class="img-responsive" src="{% static screenshot.path %}">                            
+                                {% endif %}
+                                <div class="carousel-caption">{{ screenshot.description }}</div>
+                            </div>
+                        {% endfor %}
+                    </div>
+                    {% if screenshots|length > 1 %}
+	                    <a class="left carousel-control" href="#carousel-screenshots-modal" data-slide="prev">
+	                        <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
+	                        <span class="sr-only">Previous</span>
+	                    </a>
+	                    <a class="right carousel-control" href="#carousel-screenshots-modal" data-slide="next">
+	                        <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
+	                        <span class="sr-only">Next</span>
+	                    </a>
+                    {% endif %}
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
--- a/templates/sat_website/screenshots.html	Wed Jan 21 20:08:26 2015 +0100
+++ b/templates/sat_website/screenshots.html	Wed Jan 21 20:13:19 2015 +0100
@@ -21,47 +21,8 @@
 {% endcomment %}
 
 {% load i18n %}
-{% load staticfiles %}
-{% load partition %}
-
-{% block headline %}{% trans "Some screenshots..." %}{% endblock %}
-{% block extra %}
-    <div class="btn-group">
-        <a class="btn btn-default btn-info" href="#screenshots">{% trans "screenshots" %}</a>
-        <a class="btn btn-default btn-primary" href="#screencasts">{% trans "screencasts" %}</a>
-    </div>
-{% endblock %}
+{% block headline %}{% trans "Screenshots and videos..." %}{% endblock %}
 {% block main_container %}
-<a name="screenshots"><h3>{% trans "Screenshots" %}</h3></a>
-    <div class="row">
-    {% for row in screenshots|columns:3 %}
-            {% for screenshot in row %}
-                <div class="col-md-4">
-                    <a href="{% static screenshot.path %}" class="thumbnail">
-                        <img src="{% static screenshot.path %}" alt="{{ screenshot.description }}">
-                    </a>
-                     <div class="screenshot_desc">
-                         {{ screenshot.description }}
-                     </div>
-                </div>
-                
-            {% endfor %}
-        </ul>
-    {% endfor %}
-    </div>
-<a name="screencasts"><h3>{% trans "Screencasts" %}</h3></a>
-    {% for screencast in screencasts %}
-        <div class="row">
-            <div class="col-md-10 col-md-offset-1">
-                <div class="screencast">
-                    <video width="640" height="400" poster="{% static screencast.poster %}" controls="controls" src="{% static screencast.path %}" preload="none">
-                        {% blocktrans with firefox="<a href=\"http://www.mozilla-europe.org/fr/\">Firefox</a>" %}Your browser doesn't manage the « video » tag, you should update, e.g. with the last {{ firefox }}{% endblocktrans %}
-                    </video>
-                </div>
-                 <div class="screencast_dest">
-                     {{ screencast.description  }}
-                 </div>
-            </div>
-        </div>
-    {% endfor %}
+
+{% include "sat_website/gallery.html" %}
 {% endblock %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/sat_website/screenshots_tech.html	Wed Jan 21 20:13:19 2015 +0100
@@ -0,0 +1,28 @@
+{% extends "sat_website/category.html" %}
+
+{% comment %}
+SàT website: Salut à Toi's presentation website
+Copyright (C) 2012  Jérôme Poisson (goffi@goffi.org)
+
+This file is part of SàT website.
+
+SàT website is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Foobar is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+{% endcomment %}
+
+{% load i18n %}
+{% block headline %}{% trans "Technical screenshots and videos..." %}{% endblock %}
+{% block main_container %}
+
+{% include "sat_website/gallery.html" %}
+{% endblock %}