apps: Remove Coquelicot

Bepasty is the replacement file-sharing app.

Signed-off-by: Joseph Nuthalapati <njoseph@riseup.net>
Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
Joseph Nuthalapati 2020-08-21 19:49:24 +05:30 committed by Sunil Mohan Adapa
parent 5971ace116
commit 4e9d22d376
No known key found for this signature in database
GPG Key ID: 43EA1CFF0AA7C5F2
19 changed files with 6 additions and 1145 deletions

View File

@ -1,109 +0,0 @@
#!/usr/bin/python3
# -*- mode: python -*-
# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Configuration helper for coquelicot.
"""
import argparse
import hashlib
import os
import sys
import yaml
from plinth import action_utils
SETTINGS_FILE = '/etc/coquelicot/settings.yml'
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('setup',
help='Post-installation operations for coquelicot')
subparsers.add_parser(
'set-upload-password',
help='Set a new global, pre-shared password for uploading files')
max_file_size = subparsers.add_parser(
'set-max-file-size',
help='Change the maximum size of the files that can be uploaded to '
'Coquelicot')
max_file_size.add_argument('size', type=int, help='upload file size in MB')
subparsers.add_parser(
'get-max-file-size',
help='Print the maximum size of the files that can be uploaded to '
'Coquelicot')
subparsers.required = True
return parser.parse_args()
def subcommand_setup(_):
"""Perform post-installation operations for coquelicot."""
settings = read_settings()
settings['path'] = "/coquelicot"
settings['max_file_size'] = mebibytes(1024)
write_settings(settings)
action_utils.service_restart('coquelicot')
def subcommand_set_upload_password(arguments):
"""Set a new upload password for Coquelicot."""
upload_password = ''.join(sys.stdin)
settings = read_settings()
hashed_pw = hashlib.sha1(upload_password.encode()).hexdigest()
settings['authentication_method']['upload_password'] = hashed_pw
write_settings(settings)
action_utils.service_try_restart('coquelicot')
def subcommand_set_max_file_size(arguments):
"""Set a new maximum file size for Coquelicot."""
size_in_bytes = mebibytes(arguments.size)
settings = read_settings()
settings['max_file_size'] = size_in_bytes
write_settings(settings)
action_utils.service_try_restart('coquelicot')
def subcommand_get_max_file_size(_):
"""Print the maximum file size to stdout."""
if os.path.exists(SETTINGS_FILE):
settings = read_settings()
print(int(settings['max_file_size'] / (1024 * 1024)))
else:
print(-1)
def read_settings():
with open(SETTINGS_FILE, 'rb') as settings_file:
return yaml.load(settings_file)
def write_settings(settings):
with open(SETTINGS_FILE, 'w') as settings_file:
yaml.dump(settings, settings_file)
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
def mebibytes(size):
"""Return the given size of mebibytes in bytes."""
return size * 1024 * 1024
if __name__ == '__main__':
main()

3
debian/copyright vendored
View File

@ -5,8 +5,7 @@ Files: *
Copyright: 2011-2020 FreedomBox Authors
License: AGPL-3+
Files: static/themes/default/icons/coquelicot.svg
static/themes/default/icons/jsxc.png
Files: static/themes/default/icons/jsxc.png
static/themes/default/icons/jsxc.svg
static/themes/default/icons/mldonkey.svg
Copyright: 2011-2019 FreedomBox Authors

View File

@ -12,3 +12,4 @@ rm_conffile /etc/plinth/modules-enabled/repro 20.1~
rm_conffile /etc/apt/preferences.d/50freedombox3.pref 20.5~
rm_conffile /etc/plinth/plinth.config 20.12~
rm_conffile /etc/plinth/custom-shortcuts.json 20.12~
rm_conffile /etc/plinth/modules-enabled/coquelicot 20.14~

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,86 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Plinth module to configure coquelicot.
"""
from django.utils.translation import ugettext_lazy as _
from plinth import actions
from plinth import app as app_module
from plinth import frontpage, menu
from plinth.daemon import Daemon
from plinth.modules.apache.components import Webserver
from plinth.modules.firewall.components import Firewall
from .manifest import backup, clients # noqa, pylint: disable=unused-import
version = 1
managed_services = ['coquelicot']
managed_packages = ['coquelicot']
_description = [
_('Coquelicot is a "one-click" file sharing web application with a focus '
'on protecting users\' privacy. It is best used for quickly sharing a '
'single file. '),
_('This Coquelicot instance is exposed to the public but requires an '
'upload password to prevent unauthorized access. You can set a new '
'upload password in the form that will appear below after installation. '
'The default upload password is "test".')
]
app = None
class CoquelicotApp(app_module.App):
"""FreedomBox app for Coquelicot."""
app_id = 'coquelicot'
def __init__(self):
"""Create components for the app."""
super().__init__()
info = app_module.Info(app_id=self.app_id, version=version,
name=_('Coquelicot'),
icon_filename='coquelicot',
short_description=_('File Sharing'),
description=_description,
manual_page='Coquelicot', clients=clients)
self.add(info)
menu_item = menu.Menu('menu-coquelicot', info.name,
info.short_description, info.icon_filename,
'coquelicot:index', parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-coquelicot', info.name,
short_description=info.short_description,
icon='coquelicot', url='/coquelicot',
clients=info.clients,
login_required=True)
self.add(shortcut)
firewall = Firewall('firewall-coquelicot', info.name,
ports=['http', 'https'], is_external=True)
self.add(firewall)
webserver = Webserver('webserver-coquelicot', 'coquelicot-freedombox',
urls=['https://{host}/coquelicot'])
self.add(webserver)
daemon = Daemon('daemon-coquelicot', managed_services[0])
self.add(daemon)
def setup(helper, old_version=None):
"""Install and configure the module."""
helper.install(managed_packages)
helper.call('post', actions.superuser_run, 'coquelicot', ['setup'])
helper.call('post', app.enable)
def get_current_max_file_size():
"""Get the current value of maximum file size."""
size = actions.superuser_run('coquelicot', ['get-max-file-size'])
return int(size.strip())

View File

@ -1,5 +0,0 @@
<Location /coquelicot>
ProxyPass http://127.0.0.1:51161/coquelicot
SetEnv proxy-sendchunks 1
RequestHeader set X-Forwarded-SSL "on"
</Location>

View File

@ -1 +0,0 @@
#plinth.modules.coquelicot

View File

@ -1,20 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Plinth form for configuring Coquelicot.
"""
from django import forms
from django.utils.translation import ugettext_lazy as _
class CoquelicotForm(forms.Form): # pylint: disable=W0232
"""Coquelicot configuration form."""
upload_password = forms.CharField(
label=_('Upload Password'),
help_text=_('Set a new upload password for Coquelicot. '
'Leave this field blank to keep the current password.'),
required=False, widget=forms.PasswordInput)
max_file_size = forms.IntegerField(
label=_("Maximum File Size (in MiB)"), help_text=_(
'Set the maximum size of the files that can be uploaded to '
'Coquelicot.'), required=False, min_value=0)

View File

@ -1,24 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
from django.utils.translation import ugettext_lazy as _
from plinth.clients import validate
from plinth.modules.backups.api import validate as validate_backups
clients = validate([{
'name': _('coquelicot'),
'platforms': [{
'type': 'web',
'url': '/coquelicot'
}]
}])
backup = validate_backups({
'data': {
'directories': ['/var/lib/coquelicot']
},
'secrets': {
'files': ['/etc/coquelicot/settings.yml']
},
'services': ['coquelicot']
})

View File

@ -1,57 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
@apps @coquelicot @backups @skip
Feature: Coquelicot File Sharing
Run Coquelicot File Sharing server.
Background:
Given I'm a logged in user
Given the coquelicot application is installed
Scenario: Enable coquelicot application
Given the coquelicot application is disabled
When I enable the coquelicot application
Then the coquelicot service should be running
Scenario: Modify maximum upload size
Given the coquelicot application is enabled
When I modify the maximum file size of coquelicot to 256
Then the maximum file size of coquelicot should be 256
Scenario: Modify upload password
Given the coquelicot application is enabled
When I modify the coquelicot upload password to whatever123
Then I should be able to login to coquelicot with password whatever123
Scenario: Modify maximum upload size in disabled case
Given the coquelicot application is disabled
When I modify the maximum file size of coquelicot to 123
Then the coquelicot service should not be running
Scenario: Upload a file to coquelicot
Given the coquelicot application is enabled
And a sample local file
When I modify the coquelicot upload password to whatever123
And I upload the sample local file to coquelicot with password whatever123
And I download the uploaded file from coquelicot
Then contents of downloaded sample file should be same as sample local file
Scenario: Backup and restore coquelicot
Given the coquelicot application is enabled
When I modify the coquelicot upload password to beforebackup123
And I modify the maximum file size of coquelicot to 128
And I upload the sample local file to coquelicot with password beforebackup123
And I create a backup of the coquelicot app data with name test_coquelicot
And I modify the coquelicot upload password to afterbackup123
And I modify the maximum file size of coquelicot to 64
And I restore the coquelicot app data backup with name test_coquelicot
And I download the uploaded file from coquelicot
Then the coquelicot service should be running
And I should be able to login to coquelicot with password beforebackup123
And the maximum file size of coquelicot should be 128
And contents of downloaded sample file should be same as sample local file
Scenario: Disable coquelicot application
Given the coquelicot application is enabled
When I disable the coquelicot application
Then the coquelicot service should not be running

View File

@ -1,126 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Functional, browser based tests for coquelicot app.
"""
import random
import tempfile
from pytest_bdd import given, parsers, scenarios, then, when
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from plinth.tests import functional
scenarios('coquelicot.feature')
@given('a sample local file')
def sample_local_file():
file_path, contents = _create_sample_local_file()
return dict(file_path=file_path, contents=contents)
@when(parsers.parse('I modify the maximum file size of coquelicot to {size:d}')
)
def modify_max_file_size(session_browser, size):
_modify_max_file_size(session_browser, size)
@then(parsers.parse('the maximum file size of coquelicot should be {size:d}'))
def assert_max_file_size(session_browser, size):
assert _get_max_file_size(session_browser) == size
@when(parsers.parse('I modify the coquelicot upload password to {password:w}'))
def modify_upload_password(session_browser, password):
_modify_upload_password(session_browser, password)
@then(
parsers.parse(
'I should be able to login to coquelicot with password {password:w}'))
def verify_upload_password(session_browser, password):
_verify_upload_password(session_browser, password)
@when(
parsers.parse('I upload the sample local file to coquelicot with password '
'{password:w}'))
def coquelicot_upload_file(session_browser, sample_local_file, password):
url = _upload_file(session_browser, sample_local_file['file_path'],
password)
sample_local_file['upload_url'] = url
@when('I download the uploaded file from coquelicot')
def coquelicot_download_file(sample_local_file):
file_path = functional.download_file_outside_browser(
sample_local_file['upload_url'])
sample_local_file['download_path'] = file_path
@then('contents of downloaded sample file should be same as sample local file')
def coquelicot_compare_upload_download_files(sample_local_file):
_compare_files(sample_local_file['file_path'],
sample_local_file['download_path'])
def _create_sample_local_file():
"""Create a sample file for upload using browser."""
contents = bytearray(random.getrandbits(8) for _ in range(64))
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
temp_file.write(contents)
return temp_file.name, contents
def _verify_upload_password(browser, password):
functional.visit(browser, '/coquelicot')
# ensure the password form is scrolled into view
browser.execute_script('window.scrollTo(100, 0)')
browser.find_by_id('upload_password').fill(password)
actions = ActionChains(browser.driver)
actions.send_keys(Keys.RETURN)
actions.perform()
assert functional.eventually(browser.is_element_present_by_css,
args=['div[style*="display: none;"]'])
def _upload_file(browser, file_path, password):
"""Upload a local file from disk to coquelicot."""
_verify_upload_password(browser, password)
browser.attach_file('file', file_path)
functional.submit(browser)
assert functional.eventually(browser.is_element_present_by_css,
args=['#content .url'])
url_textarea = browser.find_by_css('#content .url textarea').first
return url_textarea.value
def _modify_max_file_size(browser, size):
"""Change the maximum file size of coquelicot to the given value"""
functional.visit(browser, '/plinth/apps/coquelicot/')
browser.find_by_id('id_max_file_size').fill(size)
functional.submit(browser, form_class='form-configuration')
def _get_max_file_size(browser):
"""Get the maximum file size of coquelicot"""
functional.visit(browser, '/plinth/apps/coquelicot/')
return int(browser.find_by_id('id_max_file_size').value)
def _modify_upload_password(browser, password):
"""Change the upload password for coquelicot to the given value"""
functional.visit(browser, '/plinth/apps/coquelicot/')
browser.find_by_id('id_upload_password').fill(password)
functional.submit(browser, form_class='form-configuration')
def _compare_files(file1, file2):
"""Assert that the contents of two files are the same."""
file1_contents = open(file1, 'rb').read()
file2_contents = open(file2, 'rb').read()
assert file1_contents == file2_contents

View File

@ -1,12 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""
URLs for the coquelicot module.
"""
from django.conf.urls import url
from .views import CoquelicotAppView
urlpatterns = [
url(r'^apps/coquelicot/$', CoquelicotAppView.as_view(), name='index'),
]

View File

@ -1,52 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Plinth views for Coquelicot.
"""
from django.contrib import messages
from django.utils.translation import ugettext as _
from plinth import actions, views
from plinth.errors import ActionError
from plinth.modules.coquelicot import get_current_max_file_size
from .forms import CoquelicotForm
class CoquelicotAppView(views.AppView):
"""Serve configuration page."""
app_id = 'coquelicot'
form_class = CoquelicotForm
def get_initial(self):
"""Return the status of the service to fill in the form."""
initial = super().get_initial()
initial['max_file_size'] = get_current_max_file_size()
return initial
def form_valid(self, form):
"""Apply the changes submitted in the form."""
form_data = form.cleaned_data
if form_data['upload_password']:
try:
actions.superuser_run(
'coquelicot', ['set-upload-password'],
input=form_data['upload_password'].encode())
messages.success(self.request, _('Upload password updated'))
except ActionError:
messages.error(self.request,
_('Failed to update upload password'))
max_file_size = form_data['max_file_size']
if max_file_size and max_file_size != get_current_max_file_size():
try:
actions.superuser_run(
'coquelicot', ['set-max-file-size',
str(max_file_size)])
messages.success(self.request, _('Maximum file size updated'))
except ActionError:
messages.error(self.request,
_('Failed to update maximum file size'))
return super().form_valid(form)

View File

@ -5,7 +5,6 @@ markers = functional
backups
bind
configuration
coquelicot
date_and_time
deluge
dynamicdns

View File

@ -37,6 +37,7 @@ ENABLED_APPS_PATH = "/etc/plinth/modules-enabled/"
DISABLED_APPS_TO_REMOVE = [
'apps',
'coquelicot',
'diaspora',
'owncloud',
'system',
@ -110,6 +111,7 @@ class CustomBuild(build):
class CustomClean(clean):
"""Override clean command to clean doc, locales, and egg-info."""
def run(self):
"""Execute clean command"""
subprocess.check_call(['rm', '-rf', 'Plinth.egg-info/'])
@ -127,6 +129,7 @@ class CustomClean(clean):
class CustomInstall(install):
"""Override install command."""
def run(self):
for app in DISABLED_APPS_TO_REMOVE:
file_path = pathlib.Path(ENABLED_APPS_PATH) / app
@ -144,6 +147,7 @@ class CustomInstall(install):
class CustomInstallData(install_data):
"""Override install command to allow directory creation and copy"""
def _run_doc_install(self):
"""Install documentation"""
command = ['make', '-j', '8', '-C', 'doc', 'install']

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

View File

@ -1,648 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="512"
height="512"
viewBox="0 0 135.46666 135.46666"
version="1.1"
id="svg101941"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="coquelicot.svg">
<defs
id="defs101935">
<linearGradient
inkscape:collect="always"
id="linearGradient103632">
<stop
style="stop-color:#329909;stop-opacity:1"
offset="0"
id="stop103628" />
<stop
style="stop-color:#72cd7f;stop-opacity:1"
offset="1"
id="stop103630" />
</linearGradient>
<linearGradient
id="linearGradient103416"
inkscape:collect="always">
<stop
id="stop103408"
offset="0"
style="stop-color:#221319;stop-opacity:1" />
<stop
style="stop-color:#3a4b59;stop-opacity:1"
offset="0.53904575"
id="stop103410" />
<stop
id="stop103412"
offset="0.81551278"
style="stop-color:#615e67;stop-opacity:1" />
<stop
id="stop103414"
offset="1"
style="stop-color:#dfe7c9;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient103111">
<stop
style="stop-color:#810819;stop-opacity:1"
offset="0"
id="stop103107" />
<stop
style="stop-color:#330600;stop-opacity:1"
offset="1"
id="stop103109" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient103099">
<stop
style="stop-color:#beeba4;stop-opacity:1"
offset="0"
id="stop103095" />
<stop
id="stop103105"
offset="0.44017982"
style="stop-color:#f1f1e7;stop-opacity:1;" />
<stop
style="stop-color:#fdf3f7;stop-opacity:1"
offset="1"
id="stop103097" />
</linearGradient>
<linearGradient
id="linearGradient103075"
inkscape:collect="always">
<stop
id="stop103071"
offset="0"
style="stop-color:#730003;stop-opacity:1" />
<stop
style="stop-color:#981905;stop-opacity:1;"
offset="0.43894926"
id="stop103077" />
<stop
id="stop103079"
offset="0.68612754"
style="stop-color:#cb4e2a;stop-opacity:1" />
<stop
id="stop103073"
offset="1"
style="stop-color:#d87e64;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient103059">
<stop
style="stop-color:#bb0321;stop-opacity:1"
offset="0"
id="stop103055" />
<stop
id="stop103067"
offset="0.48986167"
style="stop-color:#cd2a1c;stop-opacity:1;" />
<stop
style="stop-color:#d84219;stop-opacity:1;"
offset="0.60998076"
id="stop103069" />
<stop
id="stop103089"
offset="0.77200484"
style="stop-color:#f4561b;stop-opacity:1" />
<stop
style="stop-color:#fb806e;stop-opacity:1"
offset="1"
id="stop103057" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient103051">
<stop
style="stop-color:#b20705;stop-opacity:1"
offset="0"
id="stop103047" />
<stop
style="stop-color:#d73c03;stop-opacity:1"
offset="1"
id="stop103049" />
</linearGradient>
<linearGradient
id="linearGradient103045"
inkscape:collect="always">
<stop
id="stop103041"
offset="0"
style="stop-color:#e95661;stop-opacity:1" />
<stop
id="stop103043"
offset="1"
style="stop-color:#f46227;stop-opacity:1" />
</linearGradient>
<linearGradient
id="linearGradient103029"
inkscape:collect="always">
<stop
id="stop103025"
offset="0"
style="stop-color:#ae0a23;stop-opacity:1" />
<stop
style="stop-color:#ba1d1a;stop-opacity:1;"
offset="0.45210764"
id="stop103031" />
<stop
id="stop103027"
offset="1"
style="stop-color:#cd3a0f;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient102969">
<stop
style="stop-color:#730003;stop-opacity:1"
offset="0"
id="stop102965" />
<stop
id="stop103085"
offset="0.34435743"
style="stop-color:#a00000;stop-opacity:1" />
<stop
id="stop103083"
offset="0.64401531"
style="stop-color:#d1513f;stop-opacity:1" />
<stop
style="stop-color:#d05e39;stop-opacity:1"
offset="0.83254153"
id="stop103087" />
<stop
style="stop-color:#e87b59;stop-opacity:1"
offset="1"
id="stop102967" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient102969"
id="radialGradient102971"
cx="97.851082"
cy="90.528397"
fx="97.851082"
fy="90.528397"
r="27.965883"
gradientTransform="matrix(1.834388,-0.1240804,0.10193965,1.5070624,-121.74193,-72.671465)"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient103075"
id="radialGradient102987"
cx="83.60437"
cy="95.778877"
fx="83.60437"
fy="95.778877"
r="32.91663"
gradientTransform="matrix(-1.7190724,-1.4017787,0.74845319,-0.91786612,126.83856,263.51971)"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient103029"
id="radialGradient103015"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.1682862,-0.392181,0.21632217,0.64441202,-62.884164,32.830256)"
cx="86.232368"
cy="89.784615"
fx="86.232368"
fy="89.784615"
r="34.334667" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient103045"
id="radialGradient103039"
cx="114.46259"
cy="79.308273"
fx="114.46259"
fy="79.308273"
r="15.434178"
gradientTransform="matrix(0.42238979,0.38953831,-2.2305671,2.4186806,215.53025,-186.99498)"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient103051"
id="radialGradient103053"
cx="89.391365"
cy="131.548"
fx="89.391365"
fy="131.548"
r="31.466179"
gradientTransform="matrix(1.3924804,-0.06486971,0.0170618,0.36624528,-66.130564,29.230868)"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient103059"
id="radialGradient103065"
cx="84.810532"
cy="99.482605"
fx="84.810532"
fy="99.482605"
r="37.518353"
gradientTransform="matrix(1.6251244,-0.48669823,0.4382379,1.4633117,-124.47312,-48.948427)"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient103099"
id="radialGradient103101"
cx="84.350105"
cy="94.211342"
fx="84.350105"
fy="94.211342"
r="5.2453465"
gradientTransform="matrix(4.5011062,1.1169997,-0.45215383,1.8220171,-281.80642,-209.66575)"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient103111"
id="radialGradient103113"
cx="82.498795"
cy="99.945396"
fx="82.498795"
fy="99.945396"
r="5.7683945"
gradientTransform="matrix(3.2353239,0.58806417,-0.3175636,1.747124,-181.47426,-162.12963)"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient103632"
id="linearGradient103634"
x1="86.143066"
y1="137.00896"
x2="107.91013"
y2="129.73291"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-28.801756,-38.290867)" />
<linearGradient
inkscape:collect="always"
id="linearGradient103121">
<stop
style="stop-color:#584f25;stop-opacity:1"
offset="0"
id="stop103117" />
<stop
id="stop103125"
offset="0.27339816"
style="stop-color:#29630b;stop-opacity:1" />
<stop
style="stop-color:#96aa52;stop-opacity:1"
offset="0.81551278"
id="stop103127" />
<stop
style="stop-color:#dfe7c9;stop-opacity:1"
offset="1"
id="stop103119" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient103416"
id="radialGradient1729"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.255259,-0.02074969,0.02669552,1.6149539,-205.42735,-53.963712)"
cx="271.53668"
cy="80.981316"
fx="271.53668"
fy="80.981316"
r="1.1244792" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient103121"
id="radialGradient1731"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2522061,-0.08992049,0.08908713,1.240601,-292.45187,-33.008306)"
cx="271.55136"
cy="80.775024"
fx="271.55136"
fy="80.775024"
r="1.1244792" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.25"
inkscape:cx="-681.1186"
inkscape:cy="-725.92702"
inkscape:document-units="px"
inkscape:current-layer="svg101941"
showgrid="false"
inkscape:window-width="2766"
inkscape:window-height="1520"
inkscape:window-x="242"
inkscape:window-y="158"
inkscape:window-maximized="0"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
units="px" />
<metadata
id="metadata101938">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="g1691"
transform="matrix(1.0804962,0,0,1.0804962,2.8659237,-1.8658115e-6)">
<path
sodipodi:nodetypes="zzzssz"
inkscape:connector-curvature="0"
id="path103626"
d="m 62.21491,75.882043 c 3.909821,7.7554 14.098824,26.453277 14.855414,39.680857 0.75658,13.22758 4.70444,13.44476 2.06852,-1.57307 -2.63592,-15.017827 -3.4929,-14.830957 -10.405649,-29.928397 -3.845517,-8.39862 -12.520417,-21.68616 -10.762825,-16.75356 1.75759,4.93259 0.334719,0.81877 4.24454,8.57417 z"
style="fill:url(#linearGradient103634);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="cssssccccssssscc"
inkscape:connector-curvature="0"
id="path102936"
d="m 60.589611,71.610913 c 0,0 5.338916,12.18974 4.960938,18.89881 -0.377976,6.70908 -7.361158,10.857607 -9.638393,12.142487 -2.091353,1.18 -3.610621,0.87993 -5.995569,1.3918 -2.384948,0.51188 -3.607037,1.00867 -6.167877,1.16765 -2.044431,0.12692 -3.522564,-1.09479 -5.743175,-1.66176 -2.220611,-0.56696 -4.393973,-0.75595 -7.039806,-1.93713 -2.645833,-1.18117 -1.653646,-1.842627 -3.638021,-3.212787 -1.984375,-1.37017 -5.24442,-2.36235 -9.260416,-6.80358 C 14.051295,87.155193 11.03411,80.136673 9.761311,78.580553 6.098094,74.101893 2.286785,67.642163 0.68038597,55.452436 -0.92601303,43.262703 0.58589597,38.538002 2.381279,32.017912 4.176667,25.497822 9.751816,17.087853 14.854493,13.497079 19.957171,9.906304 24.965358,7.921929 29.123094,6.976989 33.280833,6.032048 46.226517,6.126544 46.226517,6.126544 Z"
style="fill:url(#radialGradient102987);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path102938"
d="m 88.462744,14.357967 c 0,0 3.60814,-1.737252 4.67722,-1.870888 1.06908,-0.133633 1.93771,1.403165 3.94223,2.33861 2.00452,0.935442 4.810846,0.935442 6.548096,2.204971 1.73725,1.269532 10.02261,11.02487 11.35895,14.566191 1.33635,3.541321 3.47451,7.216278 4.34313,12.160763 0.86863,4.944486 1.33635,17.973879 -0.73499,24.521979 -2.07134,6.5481 -6.34765,12.42803 -9.95579,15.83572 -3.60813,3.40769 -10.223056,5.87993 -12.428026,6.34765 -2.20497,0.46772 -9.68852,1.67043 -14.76664,-0.13363 -5.07812,-1.80407 -15.434815,-11.96032 -16.971616,-18.90932 -1.5368,-6.94901 23.987436,-57.062046 23.987436,-57.062046 z"
style="fill:url(#radialGradient102971);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path102944"
d="m 104.62384,43.30995 c 0.14174,-1.653646 0.42522,-0.803198 -1.18118,-2.267858 -1.6064,-1.464656 -1.6064,0.236236 -2.26786,-0.661458 -0.66146,-0.897694 0.70871,-1.370163 -1.559146,-1.559152 -2.26786,-0.188987 -3.30729,0.519718 -4.15774,-0.14174 -0.85045,-0.661459 -0.8032,-0.755952 -2.03162,0.236235 -1.22842,0.992188 -2.78758,0.48192 -4.15774,0.340178 -1.37016,-0.14174 -10.93296,1.880431 -10.93296,1.880431 l -42.267188,13.843378 15.780507,11.717259 27.025301,-4.6302 z"
style="fill:url(#radialGradient103015);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="zscsccsscscczz"
inkscape:connector-curvature="0"
id="path102934"
d="M 34.887232,53.279071 C 28.403402,50.159708 22.28895,47.107282 19.673691,38.254519 c -0.590276,-1.998108 1.039434,-1.700892 1.039434,-1.700892 0,0 -5.858632,-4.913691 -1.417413,-16.536458 4.441222,-11.62277 21.544645,-18.048364 21.544645,-18.048364 0,0 14.765797,-2.90737002 18.00349,-1.65602102 1.700892,0.85044702 2.519897,1.44983602 4.882248,1.26084902 2.36235,-0.188989 2.355215,-0.93860802 5.55697,-0.549769 1.508956,0.183256 2.078869,0.850447 3.874259,2.267857 1.79538,1.41741 6.99255,1.889882 9.54389,4.252233 2.55134,2.36235 9.5439,11.71726 9.82739,17.103423 0.28348,5.38616 0.28348,9.071427 -1.41741,13.13467 -1.7009,4.063246 -21.63343,19.444066 -32.638249,21.402903 -11.004819,1.958837 -17.101883,-2.786516 -23.585713,-5.905879 z"
style="fill:url(#radialGradient103065);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path102942"
d="m 87.473174,59.988148 c 1.70089,0 4.53571,-0.04725 6.28385,-2.882067 1.74814,-2.834822 0.56696,-2.456846 2.07887,-3.307291 1.5119,-0.850448 2.3151,-0.850448 2.78757,-1.653646 0.47247,-0.803201 1.700896,-0.425225 2.551336,-1.27567 0.85045,-0.850448 1.22842,-2.598587 1.03943,-3.921503 -0.18898,-1.322917 1.74815,-2.504094 2.17337,-3.071056 0.42522,-0.566965 0,-0.755954 -1.13393,-0.566965 -1.13393,0.188989 -5.858626,0.09449 -5.858626,0.09449 0,0 -1.46466,0.141743 -2.50409,0.755954 -1.03944,0.614209 -6.61459,5.008184 -8.31548,6.992559 -1.70089,1.984375 -3.87426,4.063244 -4.53571,4.393973 -0.66146,0.330729 -8.032,6.000371 -8.032,6.000371 0,0 2.36235,2.598586 2.55134,2.598586 0.18899,0 10.91407,-4.157738 10.91407,-4.157738 z"
style="fill:url(#radialGradient103039);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="zzscsszz"
inkscape:connector-curvature="0"
id="path103093"
d="m 51.956092,52.548995 c -0.0847,0.408546 0.16853,4.8046 -0.16536,5.159375 -0.33389,0.354775 -1.60252,0.269285 -1.78594,0.859896 -0.31633,1.01858 0.0992,2.116667 2.61276,2.546615 2.51354,0.429948 5.49011,0.859892 7.34219,0.198437 1.85208,-0.661458 1.38906,-3.009635 1.38906,-4.43177 0,-1.422136 0.11681,-3.060788 0.0992,-4.564063 -0.0176,-1.503275 -9.4072,-0.177036 -9.49191,0.23151 z"
style="fill:url(#radialGradient103113);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path103091"
d="m 57.247762,46.562798 c 0,0 0.3638,-0.231511 0.42995,0.363802 0.0661,0.595312 0.89297,0.165364 1.75286,0.661458 0.8599,0.496094 -0.39687,1.058333 0.42995,1.35599 0.82682,0.297656 2.01745,0 2.24896,0.628385 0.23151,0.628385 0.0992,1.852083 -0.16536,2.513542 -0.26459,0.661458 -0.23151,1.389062 -1.09141,2.414323 -0.8599,1.02526 -4.29948,1.885156 -5.19245,1.521354 -0.89297,-0.363802 -3.40651,-2.050521 -3.63802,-3.770313 -0.23151,-1.719791 1.81901,-5.093229 2.4474,-5.225521 0.62838,-0.132291 1.32291,0.231511 1.5875,0.264584 0.26458,0.03307 1.19062,-0.727604 1.19062,-0.727604 z"
style="fill:url(#radialGradient103101);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path102940"
d="m 30.19799,57.655629 c 0,0 -2.338607,-3.407685 -1.670433,-3.80859 0.668173,-0.400904 2.338609,-0.668173 2.338609,-0.668173 0,0 0.668174,0.46772 2.004521,0.935442 1.336347,0.467722 3.34087,-1.135896 4.677217,-0.734991 1.336346,0.400904 3.006783,1.603618 4.009043,2.806329 1.00226,1.202714 1.937702,2.80633 3.340867,3.942226 1.403168,1.135896 3.87541,-1.202711 5.278575,-1.00226 1.403165,0.200453 2.939965,1.937705 5.011304,2.071338 2.071338,0.133636 8.953529,-1.00226 10.891231,-1.737252 1.937705,-0.734991 4.276312,0.200454 6.21402,1.202714 1.9377,1.00226 1.80407,2.271791 4.27631,1.00226 2.47224,-1.269529 4.20949,-3.073598 5.87993,-2.472243 1.67043,0.601356 0.66817,1.603616 2.00452,1.603616 1.33635,0 1.60362,-1.670433 2.87315,-1.536798 1.26953,0.133633 -2.6727,3.942226 -8.95353,7.015826 -6.28083,3.0736 -16.036172,5.54584 -19.711128,5.54584 -3.674957,0 -10.223058,-1.26953 -12.361214,-2.60588 -2.138157,-1.33634 -4.677217,-2.47224 -7.283096,-3.34087 -2.605878,-0.86862 -4.209494,-2.3386 -7.951269,-2.60587 -3.741774,-0.26727 -5.746294,-1.06908 -6.214017,-2.071343 -0.46772,-1.00226 -0.267269,-2.138156 1.33635,-3.140417 1.603615,-1.00226 4.00904,-0.400904 4.00904,-0.400904 z"
style="fill:url(#radialGradient103053);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<g
transform="translate(-89.032292,-24.209375)"
id="g31718">
<ellipse
style="opacity:1;fill:url(#radialGradient1729);fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1"
id="path103115-106-6-62"
cx="137.58333"
cy="70.341164"
rx="1.1244792"
ry="0.92604166" />
<use
transform="translate(-2.778125,3.4395833)"
height="100%"
width="100%"
id="use31639"
xlink:href="#path103115-106-6-62"
y="0"
x="0" />
<use
transform="translate(2.116667,6.0854167)"
height="100%"
width="100%"
id="use31641"
xlink:href="#path103115-106-6-62"
y="0"
x="0" />
<use
transform="translate(0.926042,8.9958333)"
height="100%"
width="100%"
id="use31643"
xlink:href="#path103115-106-6-62"
y="0"
x="0" />
<use
transform="translate(3.571875,12.038542)"
height="100%"
width="100%"
id="use31645"
xlink:href="#path103115-106-6-62"
y="0"
x="0" />
<use
transform="translate(8.202083,12.038542)"
height="100%"
width="100%"
id="use31647"
xlink:href="#path103115-106-6-62"
y="0"
x="0" />
<use
transform="translate(6.217708,0.396875)"
height="100%"
width="100%"
id="use31649"
xlink:href="#path103115-106-6-62"
y="0"
x="0" />
<use
transform="translate(15.08125,1.4552083)"
height="100%"
width="100%"
id="use31651"
xlink:href="#path103115-106-6-62"
y="0"
x="0" />
<use
transform="translate(20.505208,2.5135417)"
height="100%"
width="100%"
id="use31655"
xlink:href="#path103115-106-6-62"
y="0"
x="0" />
<use
transform="translate(15.478125,10.186458)"
height="100%"
width="100%"
id="use31657"
xlink:href="#path103115-106-6-62"
y="0"
x="0" />
<use
transform="translate(20.902083,7.8052083)"
height="100%"
width="100%"
id="use31661"
xlink:href="#path103115-106-6-62"
y="0"
x="0" />
</g>
<g
id="g31609">
<ellipse
style="opacity:1;fill:url(#radialGradient1731);fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1"
id="path103115-59"
cx="54.78236"
cy="42.198574"
rx="1.1244792"
ry="0.92604166" />
<use
x="0"
y="0"
xlink:href="#path103115-59"
id="use31558"
transform="translate(4.0224064,-1.403165)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#use31558"
id="use31562"
transform="translate(-8.4189901,4.4901281)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#use31562"
id="use31564"
transform="translate(0.37417734,3.5546847)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#use31564"
id="use31566"
transform="translate(1.8708867,1.964431)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#use31566"
id="use31568"
transform="translate(-0.46772167,2.1515197)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#use31568"
id="use31570"
transform="translate(4.1159507,0.46772167)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#use31570"
id="use31572"
transform="translate(3.5546847,0.74835468)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#use31572"
id="use31574"
transform="translate(3.1805074,0.46772167)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#use31574"
id="use31576"
transform="translate(2.245064,-1.683798)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#use31576"
id="use31578"
transform="translate(-14.686461,-11.599498)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#use31578"
id="use31580"
transform="translate(10.757598,1.964431)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#use31580"
id="use31582"
transform="translate(3.2740517,-1.5902537)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#use31582"
id="use31584"
transform="translate(1.7773424,2.3386084)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#use31584"
id="use31586"
transform="translate(1.7773424,3.1805074)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#use31586"
id="use31588"
transform="translate(2.80633,-0.46772167)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#use31588"
id="use31590"
transform="translate(-4.2094951,3.086963)"
width="100%"
height="100%" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 25 KiB