From f978d2f0d0b2a729cd08a0bb47f61b2882be97ba Mon Sep 17 00:00:00 2001 From: John Lines Date: Sun, 20 Dec 2020 18:05:23 +0000 Subject: [PATCH] zoph: Add new app to organize photos - Identify Freedbombox admin who installed the app to be zoph admin. - Implement backup and restore. - Photos directory should remain at /var/lib/zoph, for proper operation of backups. - There remains an issue that the App is enabled before it is configured but will not work correctly until configuration. - OpenStreetMap mapping is enabled. These should be configurable before installation. - Add initial forms.py and views.py to Zoph app, however these are currently unused as urls.py does not invoke the view. When the view is not invoked the Zoph App installs, with single signon, currently as the first LDAP user, rather than the plinth user. - The first user's preferences are not set, and need to be manually set to avoid other errors. * Sunil's changes - Squash commits and re-split them. - Drop documentation as it will be auto populated from wiki later. - Remove outdated validation code from manifest. - Drop some dead code. - Don't send MySQL password over command line for improved security. Instead rely on Unix authentication of root user similar to backup/restore process. - Use JSON for exchanging configuration dump to avoid encoding errors. - Add username validation to avoid a potential SQL injection. - Update description for neural tone and brevity. Add information about how user accounts work in FreedomBox - Fix functional tests. - Drop all code related to changing photos path until it is ready. - Update URL from /zoph to /zoph/ to avoid another redirect. - Fix disabling the app. - Use icon that Zoph uses for favicon as logo. Update copyright file. - Fix spelling unzip. - Minor refactors. Run yapf and isort. - Use subprocess.run() instead of os.popen() everywhere for better security with argument parsing. - Enable OpenStreetMap by default. User have a choice to disable it before using the app. Add label to explain privacy concerns. - Fix dropping database by using --force argument. - Cleanup enabling the app to not enable the app when updating configuration. - Use AppView's default template instead of overriding. - Update functional tests to just check if the app is enabled/disabled as expected. Checking that Zoph site is available will require reliable handling of admin user. Signed-off-by: John Lines Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- actions/zoph | 147 +++++ debian/copyright | 2 + plinth/modules/zoph/__init__.py | 130 +++++ .../zoph/data/etc/plinth/modules-enabled/zoph | 1 + plinth/modules/zoph/forms.py | 16 + plinth/modules/zoph/manifest.py | 21 + .../zoph/templates/zoph-pre-setup.html | 31 ++ plinth/modules/zoph/tests/__init__.py | 0 plinth/modules/zoph/tests/test_functional.py | 19 + plinth/modules/zoph/tests/zoph.feature | 27 + plinth/modules/zoph/urls.py | 13 + plinth/modules/zoph/views.py | 74 +++ static/themes/default/icons/zoph.png | Bin 0 -> 29654 bytes static/themes/default/icons/zoph.svg | 522 ++++++++++++++++++ 14 files changed, 1003 insertions(+) create mode 100755 actions/zoph create mode 100644 plinth/modules/zoph/__init__.py create mode 100644 plinth/modules/zoph/data/etc/plinth/modules-enabled/zoph create mode 100644 plinth/modules/zoph/forms.py create mode 100644 plinth/modules/zoph/manifest.py create mode 100644 plinth/modules/zoph/templates/zoph-pre-setup.html create mode 100644 plinth/modules/zoph/tests/__init__.py create mode 100644 plinth/modules/zoph/tests/test_functional.py create mode 100644 plinth/modules/zoph/tests/zoph.feature create mode 100644 plinth/modules/zoph/urls.py create mode 100644 plinth/modules/zoph/views.py create mode 100644 static/themes/default/icons/zoph.png create mode 100644 static/themes/default/icons/zoph.svg diff --git a/actions/zoph b/actions/zoph new file mode 100755 index 000000000..3b6b8a831 --- /dev/null +++ b/actions/zoph @@ -0,0 +1,147 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: AGPL-3.0-or-later +""" +Configuration helper for Zoph server. +""" + +import argparse +import configparser +import json +import os +import re +import subprocess + +from plinth import action_utils + +APACHE_CONF = '/etc/apache2/conf-available/zoph.conf' +DB_BACKUP_FILE = '/var/lib/plinth/backups-data/zoph-database.sql' + + +def parse_arguments(): + """Return parsed command line arguments as dictionary.""" + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') + + subparsers.add_parser('pre-install', + help='Perform Zoph pre-install configuration') + subparser = subparsers.add_parser('setup', + help='Perform Zoph configuration setup') + subparsers.add_parser('get-configuration', + help='Return the current configuration') + subparser = subparsers.add_parser('set-configuration', + help='Configure zoph') + subparser.add_argument('--admin-user', help='Name of the admin user') + subparser.add_argument('--enable-osm', help='Enable OpenSteetMap maps') + subparsers.add_parser('is-configured', help='return true if configured') + subparsers.add_parser('dump-database', help='Dump database to file') + subparsers.add_parser('restore-database', + help='Restore database from file') + + subparsers.required = True + return parser.parse_args() + + +def subcommand_pre_install(_): + """Preseed debconf values before packages are installed.""" + action_utils.debconf_set_selections([ + 'zoph zoph/dbconfig-install boolean true', + 'zoph zoph/mysql/admin-user string root' + ]) + + +def subcommand_get_configuration(_): + """Return the current configuration.""" + configuration = {} + process = subprocess.run(['zoph', '--dump-config'], stdout=subprocess.PIPE, + check=True) + for line in process.stdout.decode().splitlines(): + name, value = line.partition(':')[::2] + configuration[name.strip()] = value[1:] + + print(json.dumps(configuration)) + + +def _zoph_configure(key, value): + """Set a configure value in Zoph.""" + subprocess.run(['zoph', '--config', key, value], check=True) + + +def subcommand_setup(_): + """Setup Zoph configuration.""" + _zoph_configure('import.enable', 'true') + _zoph_configure('import.upload', 'true') + _zoph_configure('import.rotate', 'true') + _zoph_configure('path.unzip', 'unzip') + _zoph_configure('path.untar', 'tar xvf') + _zoph_configure('path.ungz', 'gunzip') + + # Maps using OpenStreetMap is enabled by default. + _zoph_configure('maps.provider', 'osm') + + +def _get_db_name(): + """Return the name of the database configured by dbconfig.""" + config = configparser.ConfigParser() + config.read_file(open('/etc/zoph.ini', 'r')) + return config['zoph']['db_name'].strip('"') + + +def subcommand_set_configuration(arguments): + """Setup Zoph Apache configuration.""" + _zoph_configure('interface.user.remote', 'true') + + # Note that using OpenSteetmap as a mapping provider is a very nice + # feature, but some people may regard its use as a privacy issue + if arguments.enable_osm: + value = 'osm' if arguments.enable_osm == 'True' else '' + _zoph_configure('maps.provider', value) + + if arguments.admin_user: + # Edit the database to rename the admin user to FreedomBox admin user. + admin_user = arguments.admin_user + if not re.match(r'^[\w.@][\w.@-]+\Z', admin_user, flags=re.ASCII): + # Check to avoid SQL injection + raise ValueError('Invalid username') + + query = f"UPDATE zoph_users SET user_name='{admin_user}' \ + WHERE user_name='admin';" + + subprocess.run(['mysql', _get_db_name()], input=query.encode(), + check=True) + + +def subcommand_is_configured(_): + """Print whether zoph app is configured.""" + subprocess.run(['zoph', '--get-config', 'interface.user.remote'], + check=True) + + +def subcommand_dump_database(_): + """Dump database to file.""" + db_name = _get_db_name() + os.makedirs(os.path.dirname(DB_BACKUP_FILE), exist_ok=True) + with open(DB_BACKUP_FILE, 'w') as db_backup_file: + subprocess.run(['mysqldump', db_name], stdout=db_backup_file, + check=True) + + +def subcommand_restore_database(_): + """Restore database from file.""" + db_name = _get_db_name() + subprocess.run(['mysqladmin', '--force', 'drop', db_name]) + subprocess.run(['mysqladmin', 'create', db_name], check=True) + with open(DB_BACKUP_FILE, 'r') as db_restore_file: + subprocess.run(['mysql', db_name], stdin=db_restore_file, check=True) + + +def main(): + """Parse arguments and perform all duties.""" + arguments = parse_arguments() + + subcommand = arguments.subcommand.replace('-', '_') + subcommand_method = globals()['subcommand_' + subcommand] + subcommand_method(arguments) + + +if __name__ == '__main__': + main() diff --git a/debian/copyright b/debian/copyright index 3491ea7f6..c0cdc3f70 100644 --- a/debian/copyright +++ b/debian/copyright @@ -69,6 +69,8 @@ Files: static/themes/default/icons/diaspora.png static/themes/default/icons/privoxy.png static/themes/default/icons/privoxy.svg static/themes/default/icons/radicale.svg + static/themes/default/icons/zoph.png + static/themes/default/icons/zoph.svg static/themes/default/img/network-connection.svg static/themes/default/img/network-connection-vertical.svg static/themes/default/img/network-ethernet.svg diff --git a/plinth/modules/zoph/__init__.py b/plinth/modules/zoph/__init__.py new file mode 100644 index 000000000..94cc8dd45 --- /dev/null +++ b/plinth/modules/zoph/__init__.py @@ -0,0 +1,130 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +""" +FreedomBox app to configure Zoph web application +""" + +import json +import logging + +from django.utils.translation import ugettext_lazy as _ + +from plinth import actions +from plinth import app as app_module +from plinth import cfg, frontpage, menu +from plinth.modules.apache.components import Webserver +from plinth.modules.backups.components import BackupRestore +from plinth.modules.firewall.components import Firewall +from plinth.utils import format_lazy + +from . import manifest + +logger = logging.getLogger(__name__) + +version = 1 + +managed_packages = ['zoph'] + +_description = [ + format_lazy( + _('Zoph manages your photo collection. Photos are stored on your ' + '{box_name}, under your control. Instead of focusing on galleries ' + 'for public display, Zoph focuses on managing them for your own ' + 'use, organizing them by who took them, where they were taken, ' + 'and who is in them. Photos can be linked to multiple hierarchical ' + 'albums and categories. It is easy to find all photos containing a ' + 'person, or photos taken on a date, or photos taken at a location ' + 'using search, map and calendar views. Individual photos can be ' + 'shared with others by sending a direct link.'), + box_name=_(cfg.box_name)), + format_lazy( + _('The {box_name} user who setup Zoph will also become the ' + 'administrator in Zoph. For additional users, accounts must be ' + 'created both in {box_name} and in Zoph with the same user name.'), + box_name=_(cfg.box_name)) +] + +app = None + + +class ZophApp(app_module.App): + """FreedomBox app for Zoph.""" + + app_id = 'zoph' + + def __init__(self): + """Create components for the app.""" + super().__init__() + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Zoph'), icon_filename='zoph', + short_description=_('Photo Organizer'), + description=_description, manual_page='Zoph', + clients=manifest.clients) + self.add(info) + + menu_item = menu.Menu('menu-zoph', info.name, info.short_description, + info.icon_filename, 'zoph:index', + parent_url_name='apps') + self.add(menu_item) + + shortcut = frontpage.Shortcut('shortcut-zoph', info.name, + short_description=info.short_description, + icon=info.icon_filename, url='/zoph/', + clients=info.clients, + login_required=True) + self.add(shortcut) + + firewall = Firewall('firewall-zoph', info.name, + ports=['http', 'https'], is_external=True) + self.add(firewall) + + webserver = Webserver('webserver-zoph', 'zoph', + urls=['https://{host}/zoph/']) + self.add(webserver) + + backup_restore = ZophBackupRestore('backup-restore-zoph', + **manifest.backup) + self.add(backup_restore) + + +def setup(helper, old_version=None): + """Install and configure the module.""" + helper.call('pre', actions.superuser_run, 'zoph', ['pre-install']) + helper.install(managed_packages) + helper.call('post', actions.superuser_run, 'zoph', ['setup']) + helper.call('post', app.enable) + + +def set_configuration(admin_user=None, enable_osm=None): + """Configure Zoph.""" + args = [] + if admin_user: + args += ['--admin-user', admin_user] + + if enable_osm is not None: + args += ['--enable-osm', str(enable_osm)] + + actions.superuser_run('zoph', ['set-configuration'] + args) + + +def is_configured(): + """Return whether the Zoph app is configured.""" + output = actions.superuser_run('zoph', ['is-configured']) + return output.strip() == 'true' + + +def get_configuration(): + """Return full configuration of Zoph.""" + configuration = actions.superuser_run('zoph', ['get-configuration']) + return json.loads(configuration) + + +class ZophBackupRestore(BackupRestore): + """Component to backup/restore Zoph database""" + + def backup_pre(self, packet): + """Save database contents.""" + actions.superuser_run('zoph', ['dump-database']) + + def restore_post(self, packet): + """Restore database contents.""" + actions.superuser_run('zoph', ['restore-database']) diff --git a/plinth/modules/zoph/data/etc/plinth/modules-enabled/zoph b/plinth/modules/zoph/data/etc/plinth/modules-enabled/zoph new file mode 100644 index 000000000..a33721589 --- /dev/null +++ b/plinth/modules/zoph/data/etc/plinth/modules-enabled/zoph @@ -0,0 +1 @@ +plinth.modules.zoph diff --git a/plinth/modules/zoph/forms.py b/plinth/modules/zoph/forms.py new file mode 100644 index 000000000..11a149e88 --- /dev/null +++ b/plinth/modules/zoph/forms.py @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +""" +FreedomBox app for configuring Zoph. +""" + +from django import forms +from django.utils.translation import ugettext_lazy as _ + + +class ZophForm(forms.Form): + """Zoph application configuration form.""" + + enable_osm = forms.BooleanField( + label=_('Enable OpenStreetMap for maps'), required=False, + help_text=_('When enabled, requests will be made to OpenStreetMap ' + 'servers from user\'s browser. This impacts privacy.')) diff --git a/plinth/modules/zoph/manifest.py b/plinth/modules/zoph/manifest.py new file mode 100644 index 000000000..bf6ea5268 --- /dev/null +++ b/plinth/modules/zoph/manifest.py @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later + +from django.utils.translation import ugettext_lazy as _ + +clients = [{ + 'name': _('Zoph'), + 'platforms': [{ + 'type': 'web', + 'url': '/zoph/', + }] +}] + +backup = { + 'data': { + 'files': ['/var/lib/plinth/backups-data/zoph-database.sql'], + 'directories': ['/var/lib/zoph/'] + }, + 'secrets': { + 'files': ['/etc/zoph.ini'], + } +} diff --git a/plinth/modules/zoph/templates/zoph-pre-setup.html b/plinth/modules/zoph/templates/zoph-pre-setup.html new file mode 100644 index 000000000..0eb12544e --- /dev/null +++ b/plinth/modules/zoph/templates/zoph-pre-setup.html @@ -0,0 +1,31 @@ +{% extends "app.html" %} +{% comment %} +# SPDX-License-Identifier: AGPL-3.0-or-later +{% endcomment %} + +{% load bootstrap %} +{% load i18n %} + +{% block pagetitle %} +

{{ name }}

+{% endblock %} + + +{% block configuration %} +

{% trans "Setup" %}

+ +

+ {% blocktrans trimmed with username=request.user.username %} + User account {{ username }} will become the administrator + account for Zoph. + {% endblocktrans %} +

+ +
+ {% csrf_token %} + + +
+ +{% endblock %} diff --git a/plinth/modules/zoph/tests/__init__.py b/plinth/modules/zoph/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plinth/modules/zoph/tests/test_functional.py b/plinth/modules/zoph/tests/test_functional.py new file mode 100644 index 000000000..f0ebab73a --- /dev/null +++ b/plinth/modules/zoph/tests/test_functional.py @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +""" +Functional, browser based tests for zoph app. +""" + +from pytest_bdd import given, parsers, scenarios + +from plinth.tests import functional + +scenarios('zoph.feature') + + +@given(parsers.parse('the zoph application is setup')) +def _zoph_is_setup(session_browser): + """Click setup button on the setup page.""" + functional.nav_to_module(session_browser, 'zoph') + button = session_browser.find_by_css('input[name="zoph_setup"]') + if button: + functional.submit(session_browser, element=button) diff --git a/plinth/modules/zoph/tests/zoph.feature b/plinth/modules/zoph/tests/zoph.feature new file mode 100644 index 000000000..e7c655794 --- /dev/null +++ b/plinth/modules/zoph/tests/zoph.feature @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later + +@apps @zoph +Feature: Zoph Organises PHotos + Run photo organiser + +Background: + Given I'm a logged in user + Given the zoph application is installed + Given the zoph application is setup + +Scenario: Enable zoph application + Given the zoph application is disabled + When I enable the zoph application + Then the zoph application is enabled + +@backups +Scenario: Backup and restore zoph + Given the zoph application is enabled + When I create a backup of the zoph app data with name test_zoph + And I restore the zoph app data backup with name test_zoph + Then the zoph application is enabled + +Scenario: Disable zoph application + Given the zoph application is enabled + When I disable the zoph application + Then the zoph application is disabled diff --git a/plinth/modules/zoph/urls.py b/plinth/modules/zoph/urls.py new file mode 100644 index 000000000..15e49d964 --- /dev/null +++ b/plinth/modules/zoph/urls.py @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +""" +URLs for the Zoph module. +""" + +from django.conf.urls import url + +from .views import SetupView, ZophAppView + +urlpatterns = [ + url(r'^apps/zoph/setup/$', SetupView.as_view(), name='setup'), + url(r'^apps/zoph/$', ZophAppView.as_view(app_id='zoph'), name='index') +] diff --git a/plinth/modules/zoph/views.py b/plinth/modules/zoph/views.py new file mode 100644 index 000000000..067179853 --- /dev/null +++ b/plinth/modules/zoph/views.py @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +""" +FreedomBox app for configuring Zoph photo organiser. +""" + +import logging + +from django.contrib import messages +from django.http import HttpResponseRedirect +from django.shortcuts import redirect +from django.urls import reverse_lazy +from django.utils.translation import ugettext as _ +from django.views.generic import TemplateView + +from plinth import views +from plinth.errors import ActionError +from plinth.modules import zoph + +from .forms import ZophForm + +logger = logging.getLogger(__name__) + + +class SetupView(TemplateView): + """Show zoph setup page.""" + template_name = 'zoph-pre-setup.html' + success_url = reverse_lazy('zoph:index') + + def get_context_data(self, *args, **kwargs): + """Provide context data to the template.""" + context = super().get_context_data(**kwargs) + context['title'] = zoph.app.info.name + context['app_info'] = zoph.app.info + return context + + def post(self, _request, *args, **kwargs): + """Handle form submission.""" + admin_user = self.request.user.get_username() + zoph.set_configuration(admin_user=admin_user) + return HttpResponseRedirect(reverse_lazy('zoph:index')) + + +class ZophAppView(views.AppView): + """App configuration page.""" + form_class = ZophForm + app_id = 'zoph' + + def dispatch(self, request, *args, **kwargs): + """Redirect to setup page if setup is not done yet.""" + if not zoph.is_configured(): + return redirect('zoph:setup') + + return super().dispatch(request, *args, **kwargs) + + def get_initial(self): + """Get the current settings from Zoph.""" + status = super().get_initial() + config = zoph.get_configuration() + status['enable_osm'] = (config['maps.provider'] == 'osm') + return status + + def form_valid(self, form): + """Apply the changes submitted in the form.""" + old_status = form.initial + new_status = form.cleaned_data + if old_status['enable_osm'] != new_status['enable_osm']: + try: + zoph.set_configuration(enable_osm=new_status['enable_osm']) + messages.success(self.request, _('Configuration updated.')) + except ActionError: + messages.error(self.request, + _('An error occurred during configuration.')) + + return super().form_valid(form) diff --git a/static/themes/default/icons/zoph.png b/static/themes/default/icons/zoph.png new file mode 100644 index 0000000000000000000000000000000000000000..cc8b9d0930fff65df2896378e883ee4d4a2b3b85 GIT binary patch literal 29654 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

HyHiW^^~=b~k~X@$*5wJBJ&H8%FsqH&6$v z!8QYLomac#9GMHAHemFjWb^e*)gzR1go7}c0R>Ete4V>fD{VZ}7P~~qkzXpGaP1ri$hD?r(lGvnUpxdwucF^j&z}ld%9N-?t#>zIh;b~2) zu(Y_;*x96^Nt>@zA|@GX(44T z^xdQtxurJLP|UV^{Fl=QLM3XULTg)Yp~qm+^73-~-}Rt<9CTE#@A9-%O6(Jd4o0f+X>$#@9rurLrr%&3v}zwoajio1TdP zzxH1msib^L9<%bN(Xe>#G|@I!?qoG6BshpnnT^Z{i&^grkC(G~AURQyFCNRdwL~P< za-Z(ioj?zyg!-gQpk+GOM7f>t!stER-w1fE?2U`Nsm0%DNzuqz^s$wpX{F!o504JS zSO|xHxHaYa5mxvpU`TDd}h2<~)WjWbs zP%~oCC>YhNzkw$hY~yVm+;oJ{*jY=9&m6H!JBnGA{CK;mt=RId5m#$m4S1i%_lAQ& zoS?D0H!ACjmAk{y_KHvYL7<9;GQrH*o3H{xFgCsN3voRD)c`3WRjGc89(R~j_cc;B z3{fV7GO(jc>{ao1U`EuFkvKe6DSY0xc-o$N6knE-gNKhB)Gv}#L^`8geM1_rKn^cO zG?=E|(VLB+m0C26Ax&<*z-Mh`_R8Ifsl)f>=>|7W>RKCLE!#>-rRwk+QVPeVsrlzh zx9E7ZfyL;@y9Pf4v3^UI+m_f|t(q&S#h3BAKi}vVjp&RyrU~o+Wc9$0%t(FL(}RH% zjE0G*)MqxPw%}dM+pej`I(f9j`Aw$7Uigm8n z^u{g}2fkJXp}<&EXjI}!wDlmPV1Ai5jz&pw<(;88M#v z>$c@l^n$UoyTc(YjL_G|GxTZ1T*kyiR0Z=gYx_DV=*ch51aQ6kXC*Zl@7AcJ!zge5 zJX9pXnKwLFt?NKu>+kzsxZUHydu~+ly}|UaX@u;p=94ZVxW3$%0bD}IkKr~QTWo4!p0q<`cDUS-A3l{Xu5YsbcOGA#hkKKS5_`}zja`_*OhMK zEzf$P?NFw?=6KtNkv|f7l1ymJT{dzA`tQ6vI}A*w5uJOLg7DMQMPe~hj3Kd7tK#XC z6-9Na$5-9J8;nrPMIv>D#@;W{btjZK68&!EGP0@`-p-KKtEPe&Y)rxgdi-2_bMUkBVCKYrGT#Q7i`LfGcATI2hnqLipmMVhuGv#N6i z?VaCud&AWflfCbXT?g(YX#JtvD8xE6QsIu=#WU85(V&}@#tnx`5J+8hyy`&IJfi*4 zp87@bGe;eHmn7E65oD-ckRu_P2mAepvi#4!^S{^KJJ;R2%(zLXmzUw|Z_f532%R#K zk;UFdb5H_I1*>WnyRK27Q$J_6~P~5swL<9?TBlW6T@k1(zdOWqyuVDU{x8s#)amG=@*>47qxzDRrufH9=QgYt^4P?#-C zvMYDA6epbg8*W(D1go%*-N9?xV&--y^@Cj(;V53;3ZwygqZMnQ9M5-0`+hOQf%NU_~D;I1mp3c?{Lr(-Yx2Zjj95OsBR&u0|bD-G0dF5WVg$w4>e z81PMr8D80>madiSe4e03yD5A5bc4G-GpkzK)rJhpDVZ9Nd&n=+SBsH2++LT1E5DH> zawmpV1pXse9jUu>6yJ ze9)O=$e9D7HWWI3h8+Iq{M6iIG)9UxulYOOXn!vA{--b6b(M;q=phP7B#T&`vx})e%lA-(hl~NL<`;U$= z0-v244zx#8kU+9}d|N_RR#!dSg!J&x_;F?JiPhZgs{Pm?DYPv0r|USLhlts3GkiMD z^D?twfeJ>FCG)e2vdZA;1(6$5^6s?T0e2#yp~9fz(_qIiwq>m9U5Py#kJZ&LdwySX zEWTqM;;Tj4a9uiEt7d`CHJ4@7ahU6Z6Ih7%_6MC|kyk%hecQfhM&@O~0|Ws?3Z_98 z$mddcLGNm%Nn$fJ@f#Lv!QgxE=wR?@;#ngY8;?FQw35AkMkrS!o_Fn$Ug9GT+q+J! zVK__(!p{%<==#}>UpnJkuig%qq*bBEQF8MY#tfZ^$;#Tu4jEAndp4H(u|w%wX*IlFQtRfl=9B@a?SLE!_NOZMj@&iGC%4 zB;j>Yw}%6WGt`m&&aGfEZD=D~t#zuY@!LR0bay1VT?%`BfRwpQzzm<-v7T#yhajrem7 z!E8_W!~$fIK}PKBSI8G0UL3R-YWbOs>^%lVFbm|zj~`%=U;DWP6gLp+d#*{L`+G}b z+6VRKVvW7{UKk~MP*2Zzq-q{rp3NIITM|k-6j6zUaqT6MGwbYR6F_Es zD)2tvaB5{K0X3_<7CGL&8IGyUeIrRRCP2d=xHv28RrkN|%)ZZey9u;A?gKCkFp8W? z|Gu1I#Xq++5u?W){V6q+IyTjTrAiG&Kbs~FPp>d0dma=FOFKF$5AfDOB2V;o0NeG+ zKWl{Y#ul-LH(Dl|3r_i?8V)}Go!PX129nbgovEIRx);}QA;T5Rd4}g-;xPrdNSqd) zfT^Vg_`+=snQn;^U7|y#|B^pyYU<3LoG1*J4F6o;UB8nm!yz99>81gDjv=$Qq3wnt z$11kzQFES+qi@vr0iCd}hRCuxZ8`Vf1^WB?a!|xXRaW!QeF*m->gGX&)v#~`l8gi2 zXYd9$BKGOkA`P10P-za{h=o1S3d?O^CXJZi?yH8xKip8Q>zdqZ^L@xlu;#=-Xj^)x zrfG`+ws{b`^1+CiPjd2J?Z@~e2 zEIIO~4KYLg`tBSv)wb0G$%IbJ6&ACy!K^^YVsuQ)0CrxMsMEA6eu%Ciy1(GkfJaTq zyrR3bn+OhuxxGIr7gw{Mo{X1Vc^hz-o?k2i{UL{B`DzK6Y7x}|jpY9T}z{!Z2^8CzR?f;`b+g35A-M>W=3 z3%vX1o<&EoIV;{{5?1oa%(Hj(H7r|8_1%eJE{0Ih{(kz??)NYp_?`hED2l48@Xx}+ zfNIFK=5mimMLEkGfMon!y`-Xocj)r>{Cs0~%ZI%5pQaLuH=%l0^yl7G-wDif6)>?4Tl&2!LlM*#Nt__%B~EE^q^qjpyM-|OyEnDOLIP5+L+ z{taq1Ws-r)G6-zR1tZCmDT4SJD2Uv2~v3w4_ZhQ3rJ*a3>~aT z*vrNfN+P>_PB*@1G3OR8m=r}1i7}FYjTmDR%KZ?&Vl8bGAlNCe$aRhw^EuPqKM)+oXPkzmRV%nUPgX3ooQMD2L`ZG~1pVN99vdvfsSOwVFAiMNl z5!MBIzKWA&cyzh<8)|?y zwJkC&%-OYaH5h;EhU5AVQu~VXpXHjmd~IPhCWc~e1i4WQA`G$IMV+47TGsBTYpd>y zpG&rOPU0pmq5^_~>zE|yjQ{r;lZ4-U{uPMQo_{*I^ zhkCW<4`Gb)x$pG)mp5q*gp`RX`H;G5`F7T9r?&PJh#K0v;{~_y4+?zUaX^?Y+!j9Y ztOARqPPJ#7S(=YIKCVST$JI$#VT$3z3%y4TzzqDDY3}5O=}_$eWEwsG{>{V98?f%) z0b*BE&T8GimD{8s$A0GDbLKza*wP)oT~?+<>byd=NCyq>83;XDr34g$UVLe>aNvDp zvAIbpILFxb>yVJ z)^9o`*_Q&(*bzIjg%U$|hD^{Xf`g`Q^ieTPBmOE0)0dZb`u9v?GaVg__#eVo76yD{ z-yJ-WUJvZ*!_}rOr#pBU38rh8W8My}&Ch}epOt_HH-@>daK(wZYEH!wfvfm&h;ry=p7Fb( zC=Z*B2_^TmK7Sm2pDPo5PfHJDb3Xs;ZtX$u8o|h)i?rnqm!idr7>KoP z`2yj&l3H-=A4C><=UoYh)KOU)6E$n|lR8aleT$5@-<5m&(&7xWlO8tAa^b<6;B`4=gQOONpX{c5K@|jQdR?p8jV&; zKAbT=)so&W(MqqOMuQ7X+}ZOIAie~P|DKn>t=9V@op7B}@eoyBfu$u_UrS)9u zktt&&@0uUc?qq?qk8FT---6{}v+BFy2da0nHBy1YgWm;y=05auVt`@e)at6bE<~-x za+hA~RVfRC8am>l3*Ce)T1+6j5(5{gu(@6hF@xEx4ZJ?ono|*fBQMK$WEi7HEQd38 zC&PCC2yNzo`)ux~Q@|{^wHgBgXD8>crXlQ}i;DqAsCjxpBN|lkQU|{+G^*zYtsDEz zng>CXV$P#o;8eBYhjD#XQo9iGkfC{|t#1bI*fHw!Ll-H+wy;sLsk7lBJwM7II{%rK zlBza+njw!{eYmves0rWx#pjC|cOFiqPsvnp50LqtX`^p)^pQ4ahu-fyRGSj01eF>s*@b=3<#2ZbmV;-_-)vEymi;X!k z7~<&;!x!&B6j@s2oiceS0o~fy--Lj)g8XZ^w;fdumu8%#=YK7B8^1J$#Z{6(s~qQe z+S(&sx#JbV)bt`WCLR(@-|M&p-<$8+Q0*!>C&#;WcRjNy2i%mA@7^7&`1UZws&WJb z1!^4aI0W0N5G<@|ly7cAU=D%X-49+GO*GA~+sFjbml|ret)xP3&owi1Og7ut$OyMF zExE{0tW_yYmgfq+eyW*NcV* z>s7Rxt#e!A8MAR-;^iM)!DQC4FX7fEeVQDu(E2i zN>C9(3TD!fSu*IC6!RLtgI{8 zwOI!;IyU#Cj8%V!V2C!d5Dmfu$JEK+zX_i=$krsNiEDgo>yo`Ou9>5&+~ttRC)io( z@M?RRPt<7$u(qzBkaOP`NV@307Fe1y94wn&U4(C5h$L$}{d{l{1^UGH$7f{UlNrs1 zvR`FivaJz^%#@Uf0`#)ydCXdW=|1XeHh-@)#!!fcuc(b#Aet(E5lS6OgIg3_Y-~J< zn%s@}Y5BwQ&T8{U!&x}Aq^su@We%6>(c0?b;Mz-tixxCk0_A5?vZ>{|mh|S@^6EgB zZr$eU@Lx&s$NwC1yC{FuO}%louWF4oS7Gn1?_6QD6m6W(>I;nh-t}`IHf3A&-XB{Q zxRCYrDn#v!7makdPgI{JxM|^F+YDLvPERK2 zo>#c(4aUpR~)$wxDxAvVm#ObLY|Q(v8IfP*!3Za;BZ;xyE{8N z)96d0Hh-dCFTk$1JmIH7Oi3w-npQK4^!5s>zK+usV~umMN)JnB{CPzqQC_(X2W~vI z@bJI~Ls^3i`nfLZg3bKaM+ZJin;3|Ov`7{*H8=={>>*@fXdCrl;skg*Wpz(I-HqM)Ix;Ww5Zsl+k$>tYm!Ay?%BKkjDAk0mr2vGvyU&v#``W)Q5DWod){ zb8GpVtMO-cpvJ|~Jh1b+X*Es?{xtX&H~6Z+BFgAKfZn#9Qf_zs8olRfA zeb`3f(4T@^K)5{!k%1l$6`bs3Pr@sEr~44#*4xKzci^^=#F(2UCmp~<1PElsqK2N()nC%?YSxuXTO@N{gr z32=TP)i1xDCKDY{sJfP5`ZV(FK76{eJh*ZmCyabNOpA-?^Mrzo;I};#wix%$sSg?- ztB5;XiVMR)Fs_c!sYkA;6xNH=h~MwTEhG-*;y$fjZK( zp`K{<2kQ}}=}U|lW4NynYC)Zf)296dgykF)y>k_8WxQLz8z7T`OJ>Xtz$dL{;wCg- zJpzSW98*hKtw?I|*mXt&Pv__Ek`XxJja;P-nM{M6ptl>#TL5`{bpqV8W9s(9m{hdS zmB@{{9?7{%VtDCjTvPi44qgggpGZ@^$$q1+{!m^fE9%cnGXup2bQ{&J+M9aIT=X&m zL&2^+Icek*dCiwS zzV{^Hd!IB=YN zwsgg`oCw`NQ{UcNH3gwM>TtbZsY?`u5o2w{xIBK^j2LcWN%X&(In2~p_JbLF5s(9N zC9&XR;e$=mj7?Iqg8I_KS>kX?a2tfMbxJk;q2!#5W$nR-JT?EDART@G(Aab$%~B4< zL@kLGgTZfoasngT|NCBm{3};3W`2oSqk}M}z?Ky1yJNkr23k1&S`N|Pe8$G-r*{;7 zW5O?MlOgKbIz>?VcK-`Pm8u5Ag71sJ!oDs8@W% z(mx3iS>G>TR{d;Zxz!4Q74CkVvv)AZ(MqQtDK&j-fRinF8KaMbX!LMPXzY7!=SrS1 z9R_FA7xen#?x%A@FGew1TzH0+{v0~>wiNtM@~j$M6JKJ0I5E%YZH!X9i$eywbtz40 z$x62e(fD}ltM?xY^GBVjV?9dg*8Nn#UAHxLMM0ctp*~h5({g;0pT1uz;L{Z2fT1hN zU_GQG%iEoVd+sEh?)#FHzpb-6;3!(PE4nCJZ=|RjENZj(z9h3=^^lTdQLd0 zueCV>yYAiS;|%ACvXYLM3L{Sc;H72sE%W|P6AK+-0;q%0{!dj`9Te63es`A@7DT$c zSvm!zyG1~{B?M`a5|EM(X^?Iakdl^`?v`$mE=kGX`}zL&+u7Nf-5Hp>ci;Oy=Q+UGtL^MYL!BK)dgDtcJ5(FMdizCg=>&#kh z8qWZix%u`aXB~>XOiYe~vuiz`@Ou66{+%;I7&2&AO{|RT^DaSTbj$Bw=H*3_A$4>& zw}xEh<0LCj-Q=ntLZ+HCFYw87&MXnZ(qLwQqcvf^7kpNm(9ZzIDLjILp~J(^!}9dT z{rmg_lL}>$?5y;Aq8F3{)e{C&iG6Aei^f)!gR)U&RHnSYe9xeE$^s6szV)0VI21bg zMpM&QnTrXYIh=*b2e_1;zqXrG%XeUQ$3CQHF;oBYmnJqch&o(0R*_-Xe2%j@OOr|b zM)~N4-bXo}7Gef0+IIE{Oe7nEY{pxDHX=)yhchj07g<6o* ziCglCK2+A9QWBg&8-#m)pQMI1DrM3KPKXm= z^~r^I5-%4<oZq0}gn+?v^B0oqn51B|{^n#_V){_I%u2t_?k!mIpSDxw5jrhF)Y zBm-IZ(#<9I*>%4xOr@GHc3^t~tgi1d*S?LiU*)eFRi&cQ6TJzu(p8zN8XCUxYjC`3 z0;2Ly7TD3~LnB-?c+q(+h0i7vpW@A#c1{E8mdy+L_cE6j_Iur5PA+7Vw+mZ&{Lfo! z*9Vcy!|AfGU&v%Nx(0qySn6}WSt5qhPTSXBkZXj8O(R+f-XX2<)h&pEaO~~v&D`9y zr(;v^%Tb?TAL1AV#WTF}u?17r95(G7*<|!$4*J3>rXt9np2sA->^igG(5BO(H+8Oet;dP1YhljZ6J{N4$6JI{PwoSo?)wCpbA z9|W3`BI*K>=3b8X?Oz3%lP^EeHmAx#pm8TN62H> z{XAJ8V3iLI8C#ymTsh=)_YoJ`uN&F|Q-``5K%#ei48KW5{ZXjSQJY+bNGuUduW!@9 zVIW3hhT()L-9-=p%hoTg`NN^b+ki*YT^2n1_Kt}J-5V#KO6wj%6rR^RMQcO--0WK} zY>Qj2!v6`PJB}_luFv<5|M@U#2mCzHRHwf`^FCA|kQ*8ver09`q39(Xep$3etBjX- z?)Wl>$9Jrm+Cd*9J~!lu$I(oj`WS zB0Ls(Ab_A6c&Uv7CT>;VjWqXPFyJwgn_@kAvi2_f#z=pitLCD#acv@MH6in%Kp}h3Li5+eBN869l1c;WCWTB>%C{^#!5N35uTn_clo%!gW19FXGojBD( zZgIR$N8WJdT3AJBcG=F^kbdHzRl!8YZ^jIdT(p@dHa}v?q6NZGgk-57G33cX@GyhZ zpZ(GO_3Or^!mBJqI_rQOt-V%U;^Fp`Y5mfx<#Tp+FA&85bt_9kOg1y&cFgp{*69B} zYs`PfMR0}ekl9Th+*CzXs(ry(gm2;zGvWTWvb8aKTo1ExQ5?i}a5-NtQ3|ezT4@@##&5EUmB4^g|#c?7T(k2VM)6R(J0{w{Kz=8Bi>np&ur_kCVkHD zZG5+NJj6F@(;I~2^EV;8i&^{xeRv%kf=wmbUf*`-P%!z%Fu(2X1Bd}YrVQ#C8dme8 z?!M|pX`i1G{S2C;4l^98nBJ)^U@M&VqEyZ{`WSABH)`yQ8rTVy)2M9z5p{4SuzX2= zBqKz@Q|p1i%8>AY3Ocq9e_GmcmKcC`+metINPm@uj}gL6;b7|7 zbzv`Ajv)zs6!8qpEC(bN7~B&p*J9_I^4()JVkU5FOFVnb)q0ipngcD3%0I^Mc4N^S zNS4<+sqZVp+x1mLwTtHAo~-`H^kzkUQXdvv8l&#+J}}`U&~EE=D8yOa@z7=Ia_ex7 z|I_Imn6Bsi%({7du|Ekh_~=G_xjy=c+wzfs+GDDm7@Cr8WgM(Wxk!w*eD>|5!Dn&45B!B_dm!If4#YiWV1* zX5dS24k31Dx({p-fm*EHmO61}l6>(OWZ&6kk{Q%PGGB4khH9m?Wav$OMV~9h|*V>yYre%wC4rh%zTBzgfn- z;{!*n-X7EJh&yINmpS}%(}lZ8m#c!)f(~q+vHyePqHc;v4q{1XaNQ z{^QtdhA#t29gwzO!k==zHst44zu%AhS1bl+vH}bpq7;E(pA}wZ-^vgJEJ~ z`mE!dJsPT#!kMd6x&|&6L~z>m$^E@J0H|@pub+37t_X%a?4mF0sUhANR?27)fJ+P1 zFFpt8ojIgmi)TFXH5cfydW9`K=A*8_?4C3^>w;|AF^bb43Le!3Ow=1fC5bxCY0m% zEzV6wU*D4ad8>{jS|gZKi3dNjk$7(+EJMxO?m4#iDi<~u1*cS5uRlr#0*P#P6m5_c6V=N1grJmu#+vTzt{bLTM$Uf zs*AolpWz2m#qn{CsOad_s4(Dl17QioZ?DgGKCQKh&SY!9&mWg;GX@W-qEH6{J8yu>Lo&2|p9Qi%mq^`H(_Wa%1dDF0TD3Jd( ztN6-C{M)y0+}++Lt1-gYTfmF!0QQnlN}{FY9@GmGWpov3C)TDe_qNM`9s=ewZJ`e7{~O3VGGX-4q`a8 zP!ql0X+=N@WA`M9O?N(Qg>$XcYTSl^GbNIRu-G=RbFKX&8aCuO+K;^|TQCL!xwuR( zCErU3h4=$A?@NO5ZJAh|Mop=%{;bEVE>+JyvMVihL;dq%HRh1vO%!Mj<((&WVqi_z zm`{KN+deXuv3O_wX~QbAa8cutKZ_k!`)kx5-n)~zYU`5#Y@hb{D}7u4)}b|L8UhUc zi1U{(UxEcxM|o>uou61&%yaEQe0<5wlwQq)XEqumcQme=9916&+^Ey zxmO{~u=-idZ*yE{I#$qkQ%)LLKzqHOauX}E- z>A-Ji$BW~{Dfy1&S|=$hA5%z@{Mw3v5TU*!LnUmh*JDU|@a^wyF?ACwjC{}29pdJF z*tpg3Lu^l6$Er}X-z8Dt(>5O;UmAg#?(LQXWS)p1p;pB|t`Db2zBko@ zZ|-%LWD_40TDsGoGpzSJfV>^=Zaox6`_=jN1|=bDg&R_*;llA2(#w_E!Bux(KlQKk>$8-93#iZXEOAkapSU^IVgp0PjrrC5; zH8l$jz$jBBCWr9K1TxX8mB$Llw1=RcMN6=Lz4U~|P8h89UcEBhBps_?7=NyGSa4eU zFq$b+#L@Q(EmO-Ms0R##DwxOO;&4@rvzechq{3*sYHB|Jc;7;0I6A|TnEXT%2QKPy z{yr#hKU{Zjuatvn`CiIB4W|nmo~LC8srF9;P`` zRr3tWer}!eDEOVXWqk(HdEtorZy$cWtu^TW^((pfL4NxHBFSgk0s5u%!-{#BuvAO3 z3Cyfmgwv#Jgol;|!-$hOtL+*luOOf5Fo0&oaOwX1t`4P~9B12ZpvmLOH6t#I`gJTc ztc4&s>8j#h_cA3pS@_fS^{GV_$!VI-%{L1Ouu<$F>&V^SNh5%KAMCKY*R2{b^_XZ( zsU_l@M4GO>leNOO6I#v_5XMpCx}x>krn^Oa231m0b*q?Ds%d>O!QU5MIwFt4JJkW7 zq@Uwk!84)&E-tQgQB+ZFU^;CdeH-Vdatna*<>8VAo>0uydHT$r1OCJNp^pkzyRBK! z?S*qT8FhZL={#pJFh5WQ^w zw`aQbAU?ye-m$iJEwczOv#R{%Lli)ii>)8Xqvbz}%p2m?&>)UIMJe5Uspz>|*@&v& z3CoS(Q)26UloYM{=oy&AI?_t*FG5N3ZP6U1Wd&3D8V_sn>x_5#M^Xi@q*>4L*X$=* zIDxgbPgP&Mu<-Jl*CbR=nKw6XnryunN>w#~^-Ahpw|+6@E?>#b?w1mX`*KVPQ5C|t z&ER`0S7!MKtz-GagG`QZ-^9&o>cs^DsS$9BFATg>*bM$MXM>2ZH3bzWfz~^J!Yf2s zAdqFRJ&qu0S^)5P7QRjdo*202)^Vi_Hu&BNS0CwYRYiRerL)2nk3m2FBYVd7yrF6| zBF}861LmG^tDm z(ej=AonwdF*bp={G|z+DnVPW`HM+JWKhx)jb4o*7JW68Kmc>splxllJ?pq4@Seoxf zxH!Vw44pS1$VUlRXa&0h)VNoo*?DIz!_6N%6~pt?>{jp863x2 zwUOjK03c%Wev|Me9WK!txy7A+7rPQ(g7X1h>yN5r+@n&sB;tD`=5pgdfy2d?;EyQx zm{O2^>(%y-bU@%Fn34=R$z{|po)y4LIBe@@PJS|>I8WFvvB$oV33w$S_ z(!mdQv<@zTFT4uEV=3f4?alcZMB^@b9g8Cy{OwHd5zT&#ab}^lqZ0@8>jU8R$_(o( ztE)c_rf^xfx@whj5nw1X;H7npVeGh)2|6yegHoQN7#(E2UsQ6l$9GFXK~Yl->-oSI zI3vMF`+O9&)VShfofg9Q%bIoZflgfkmXg#Q@$Q+7xYD+DmgR8s^W%R*r+?UzFn~^kO4Kt4tcbqo{2%xF z{ucBxCKdU5>~!hF zdFsf|VXb9t6y}??X!8!tiJHUmxzzunKOA;pP60#w{1C)T|^?>Aw>Jl$hN54w13n=4&q45Q6k}?m+ zS@+o&t`$@pD^xRj@ z>rpdR5^f0PwGDi~*PAJD?hz9L22?{^2_nYnoh0ig4GuCngc=6LaM9`9NrKmRzD3uP zG-E_)Im;EAWf$W1O^*bb{}mZi%)_4Lwi}^JB4!AM+G(#9A86n$*gT9__)<91_GL%b zdw(&1zTqdEot@pD&C+&MVBZS9cwMdU5WL~YKteTDvnr38HtZpRy@Ev%ICg%-g?OGG zFb-|EP>0R*wZElR7mkIT<$M^4p%M1lcVV=j(M1yM?s^;~hpnFo{S~7jV3YpE7XsQR zPtoO*3`d-B+1V(j8EVwkbGH-=$>?zIU=%}D_Ug}=Q->(qbfc>Y!M+g?h)s-f0F7)U zV#;qQ59!YMNN;m&tJc)WXur5=Xco;a!HN__dJf5n|2U|}&>(n<-hOp>%?R}rTe2d> zfJfOHB}9r_i27`;XF9EA)GTg?MM0$_DlCQ`_L)VZFo`#~qwOqF! z9(n`s{tGQhUH*bvM1vEipGXCo!u$J^Ln}G^T7xG5R`!5c-qOvn$rsB!CJG#a<%jtd zy1G{ypw|=-5O8*OHnUwRj?n?VBu_9DUs@fRq+YG}b5H2_=4#Sk8)xEd{i<0!OpRHO zuTRsPr4R2NO(PrcsQP2le~_tk@f|=;f6n*}$5o8f|GuzeN~rO``$YZPy~H|Tbu}lj zg^CYC>fcw10-^&f930A+=dwIybvRRivW>&2)rSN;CG{>`0-EUf<=#5C!{Y4c>^I;JDG`Aljofb%dg0O@#Rntr4y5bHu%{mVb6|8THEQ%^7! z!j~(S5d7!=Z1`bCFM|d*1Hp50Zc(5P!x zQ+Q?x)1l9~r?e{}*bU5yrN zun}kIe_dNl3_S&wox@qb;8|}(UXErlP4*}~IG=P6Q4;~mw?IS#aJoSm?=)OT!kCj- zEmOFg;Nhmqr8;7^k6YpUZ)}q53y}p8&%G3Jw40vxC zZT8I+H3!dvR^mzYDvplHLeB{Zf?#pZh*|SOUtwVaj6MQqj6aan)KpT->N)!8i>2h^ zpda=1p{h(MDTBcv!dj9BQZZ9%^;aYDb^Me;S%^!cecG~JbDhZ(95@soAD@Jb4E&DL zwFYv+ng1#NZEMpCgLs}k?H+Oy@xm1ak7CR8HPIK@@7Wj2we)CElf0UuaU2 z1A1wN4e%r<#aEq$KC75hqdip$RB`3A09aBXPOV-iZHK4hR6tGx#EHiCTgDD<^0(FT zxPB%)DGGEO2Hggz2}$&Zl1VI7K+$ zBp*#6qy(w%_u0!Bri}!9A|CrkoHsYttPEewlg%X|eN^`89x#SgdYF@wlO$AB@&f78B+4FeSb|qxefDV0YL=gKRb<6wHFY?Vkf;5GFTNt0y z_-EuoLjgfsgS)eX+jQL6}XmnW9IyNI}0>6|R^o4oIpF6Ig&YHb`jS9}EW4)mvU##3M zIPlKS&M!B>OlIab{L5umL9W*5XNPaaOi(*j;n{|Yic0CVv<5*p#futMhV=%|w^&#T zFV)8u;7rU#Kqd?(h}YL^5~`k}RI1O_I<`*Jn`vtp)AqXie5K zU##fvo!yiWn03qQeTeMcP$zg4EDcpF!k@M!OjSiSG&KAo4)$ewvS{8^ZQi7xYIz@? zw3^857!!Tfh5A^%dbL)kO|L;9H#hL3IYo8tqD;i|Vvg~6Q~uP#;&^kD96Bva4C+uxg=IE%^K>fSTc;0B79VUt)xGg z#M~eUL9&L5TD>stOvnkxgGMSOH_dGPxznq~kJqZ=y5Va%&jV zOz|fTo6*kgdl?XD%noj zKS)KEZcy7}RhgbXG%vq!vR)}sovs!sysL|cc-lrRPdHgZz|O8)Vd60Oufil9Cot*! zN%Mz|*Agc6$f_93I$ax^{x4j?#VDvfJ6ykNCdg5{tE{INP(6(ukq+$$0w`L zDSt&e3k9vMkpO-hG(fADXf(oD!IbmA2|~)Lc|W-dM;CUZ^;x}3zC-c1t-S{6u1kWi z?ltt%uTiSz$^3?hvh!TH2`(BI5C0u5hlouvqN`~HEm)aAWN_es<9(wfrvIZMDYt?g zB|9#*Y*&9G>}}B2bf`3~QKL(D&1&dC(BEK~@BN^u+wD@!<6@JW!;xQq?1UIxBi!k9 zG2LuK&nWql=Tab$)t|wSA4u9C_C4_cPbkkV=s|^_fbZP&M6`3=s;cB{NG8sTzW|l& z9CbHZ7`$5QhkWac-IN)0Mpzy`Rw$%s5p#eo1C`6N5tZ}fpCsB&gm zoCz?N)SrHCo!j*GfV}S~pwLh%c>huyT{sJ}?#<6Rs)$evQ5HMF26))l*JSY2+UJ*Y zZA6>r-EeE+?XPQ0t$N*G4?3IxyL65lkHQ%~rv_&69HYj5!t)O&G;MHRYlB`9y9;w7 zV01FCGCu5Lg35=B5z?6N%KX#%sVIZqJe}TB%%CPT2mj_A9^HBI9Hv{b^dl?a+5XX1 z709u%7qE^zHvBNKe|UI53Bp<7pO|+IlW_S91$ezrCXTLd14d_e7jY#K9cNd_MGiq^ zBm|fu4p-W+m3OKTE38u~-SDaV?T5WrjFey905!a*D3P-Rdl(e-3%rxAGOf^)Y0-FC zdED0e4^4={;I5A=Ka;jH?6wcH{kN7{(PTxJd6hmSMI=*|MXpe@+mSBZQ#8Py6QzT; zebcqJ>pCA-y!6w=#lE)}Y1rJdGx5gB8b^BJcXkUWuHeE#M*DA0c}C(wMwE>x$ii+_ zrqfZE#>F4yb_#^&WxHC)Bp0iv!gx0%v;|Gg8pb(V#dNEuKj#dv1{IOH>Il$K^jY$z zJr8AE_aN(t-nJZ?tY5=@=5$>F+(KY1dv?aJ`xBG^r`vtvzD(H$k;2Wck&CwS#6iZI zhh=#sNs*7IZ7(VDzkS6^Q!DZtQ^q`rk5+(;MTHG+T~yUJMgz^i<-6hW1R;cM5k3s} z2a}J(*$m~!+=_bR0lgw+h=UvlYAzCV_T-Lm)R_(H9 z*XbTI+daHuC>juTrIMMuj#x0l$UZ9RGzKZdnuQZR@F>|0_a+d;5DoY@^AqQqefgEG zrKXpZk?17RAFesbqSya!4m!+zh#r0E#DDzfJexlARnPDl#ktpAOx-V?XaV{b0!WS@ zX8Yfn)b#WlO6fj92*tBE%?k&C5;&6fmL&95xIy%UfL;DwOY6S~e&qLsvL~7cz(`DdQr=~gz z7X}*8otGO3v;`Ta3TW$(oG^w+% zpJ(5HJ8Jb6!y4#bF>LjF=;b!NDns0TwWY_e+pKPEY7=R{(Zkvfqe7K;UK1O=Zyh}F+94UWBB$@vP>uTJ9Im;9ESz0h>fYV8UD$12(!0|? zy)P4mumV>5_3PK%e0=%u1HXOlzy3qC?6uWXRnXy>+c%Kz;n;j9=PK}f&I-{itm0aj z({eVUV$Pd-8=5%b_fecLPyzF)QoL_NTiZjl7HjRJz42CZ>3TX}?)k5m@3sd(H-?v< zC3Z;(bGbclv8P!*T^pKWFyh?P&|VtYL>~#!DKR=x}Y`j0a0kbR63V_qo7$4)7>B|j^&QRe-!H3N<8D!*QcTEXH=$Rx;#63^(c+2 z+EAk{L2+eA!pXD(l_V9?=(#y30piH0t4?EEq`6mnQ;YirP;v>OyL(fv%ey+oTKthM zX{V!e$^je^bRfv*Kf_%B1VlD2I@WWT&p4gSqX;@K_3U2DujJq8#aiuf9W8rqW#@?e zlwif*4$H!`8#KEz1X;FuiTBSTGA+%%*)%Vek60uA8nl@BSy@zVI)IrmP`mYG%{S#m=4{HxLu&oH5KQ(DvNRXwC!WS&DnBpsln}QSIWd!!k_774mJ@@ z(jfeYOuYW^fspf+4O$?Wi^5dk5$6q56zWo4AAbSzbOLXEp?2x%dJD5YHuSAjwYKLH zoXd^p929sq5~=fW3`#Bli0Pl zO+4blfmLxNfo1%Ll71u|QiXw@=EUG&J{W$#LB-C}gAje9+x1Y{Z=vzM ztJ1(5yFihV>EN@rZev6I#-IY^?drKF4kmo{^8K27<0_7vkl~wVblTxJtMO4G47s#Q z$P{dK))Wo)B(GL=Y_T4GzDq4tP|qHJS|bUD?x;-6F>_S)ui z@hQU`So*+LXt_Q#xk3zg34ePJxr1p704t|N!rao*$JApH+%t{cm>>@;$cyZDuYcdJ z97?@RuDhZvoOO~!bga#m}s@$rR6m(o+CU4RlZ*}DqypsdJ zSTzRCN~-fRL^CMV&OBnHcqT8de_U76A*K@=lcTK|nu8#UtJ7CLP;^QFy^FEZlq zX=ZH_m*vZ1%z5-FPWsi_1M>e4_Sg}6^f4gU=$X)Lg-Y#i_M^_Yx6*ynp%V)pq?Y<# z?>Fz4xa#q|DT4c&2=BZ@peUX$ImK8>%5rlmU4EjX0$Bv`wN4TrG=*TTikXMgJJXe` zGzO(7u1x!mq_EQRB9d9i@}1e-BmSHA#U!>Px5)CNz*ziD$+Ovy6I#Vaw@cH(!EMqa zZ%cxf?BvIYF@+wQsK04^{JNQH>C(I5;=6Egn#p8|!)AsSk=LC1oJqW`v98Yl5YyY8 zRN7SGgBU7ad%`2OH~vEsmy5)dvbQgQe$i+{Nxsme!p~vt*RVv$x#B3BL`1}xE+&IN z^W)@vCDynGTrByAt~Hk|S~#&<$;%iN0=a+hX|wNs4*a~+edSamQO@8@2hQpX&W3mh z#ARo0>%J8k;fFB0UUNHH`>2@)IEaT=CnFUPn7`!}$w?s)=^=b;2K&XeGBbQm))jjO-3P*9&zsRD!xQ280F%z`6Y~@(Ca{Ohh0x zq`FBewt8Mn7)zh$tfXp|!F38(u46$HH8_IXniAifIO6t1N)I+~5AI z6v4Ts!wp(_f%lYmu0hzn`EDDCG*?WXjtB8m`WROgt10b{u@ZNkVu z%LayqZ2r$PbbrbIZuZ5|%-dGD=3lI)ic=`&byE z7&y{4xDccqdg5!A*e^kc0TUcb@xM;{f4lRh)>ISl#~h#c9vPThziNk|dVby(IMt|M zVTq+w-umB%8Dqnj*RptL5EM-A_W%C^dHg`Vw|6HmRVI@U0S_fPHQ91$v%voY DY;X4B literal 0 HcmV?d00001 diff --git a/static/themes/default/icons/zoph.svg b/static/themes/default/icons/zoph.svg new file mode 100644 index 000000000..f718e008a --- /dev/null +++ b/static/themes/default/icons/zoph.svg @@ -0,0 +1,522 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + Photo Camera + + + camera + photo + SLR + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +