Signed-off-by: Julien Veyssier <eneiluj@posteo.net>tags/v20.0.0beta1
@@ -40,6 +40,7 @@ | |||
!/apps/theming | |||
!/apps/twofactor_backupcodes | |||
!/apps/user_status | |||
!/apps/weather_status | |||
!/apps/workflowengine | |||
/apps/files_external/3rdparty/irodsphp/PHPUnitTest | |||
/apps/files_external/3rdparty/irodsphp/web |
@@ -0,0 +1,21 @@ | |||
<?xml version="1.0"?> | |||
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd"> | |||
<id>weather_status</id> | |||
<name>Weather status</name> | |||
<summary>Weather status in your dashboard</summary> | |||
<description><![CDATA[Weather status integrated in the dashboard app. | |||
User's position can be automatically determined or manually defined. A 6 hours forecast is then displayed. | |||
This status can also be integrated in other places like the Calendar app.]]></description> | |||
<version>1.0.0</version> | |||
<licence>agpl</licence> | |||
<author mail="eneiluj@posteo.net">Julien Veyssier</author> | |||
<namespace>WeatherStatus</namespace> | |||
<default_enable/> | |||
<category>integration</category> | |||
<category>dashboard</category> | |||
<bugs>https://github.com/nextcloud/server</bugs> | |||
<dependencies> | |||
<nextcloud min-version="20" max-version="20"/> | |||
</dependencies> | |||
</info> |
@@ -0,0 +1,34 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright Copyright (c) 2020, Julien Veyssier | |||
* | |||
* @author Julien Veyssier <eneiluj@posteo.net> | |||
* | |||
* @license AGPL-3.0 | |||
* | |||
* This code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License, version 3, | |||
* as published by the Free Software Foundation. | |||
* | |||
* This program 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, version 3, | |||
* along with this program. If not, see <http://www.gnu.org/licenses/> | |||
* | |||
*/ | |||
return [ | |||
'ocs' => [ | |||
['name' => 'WeatherStatus#setMode', 'url' => '/api/v1/mode', 'verb' => 'PUT'], | |||
['name' => 'WeatherStatus#usePersonalAddress', 'url' => '/api/v1/use-personal', 'verb' => 'PUT'], | |||
['name' => 'WeatherStatus#getLocation', 'url' => '/api/v1/location', 'verb' => 'GET'], | |||
['name' => 'WeatherStatus#setLocation', 'url' => '/api/v1/location', 'verb' => 'PUT'], | |||
['name' => 'WeatherStatus#getForecast', 'url' => '/api/v1/forecast', 'verb' => 'GET'], | |||
], | |||
]; |
@@ -0,0 +1 @@ | |||
<svg width="280.5" height="280.5" xmlns="http://www.w3.org/2000/svg"><path d="M140.22 210.04c38.48 0 69.78-31.3 69.78-69.78s-31.3-69.78-69.78-69.78c-38.47 0-69.78 31.3-69.78 69.78s31.3 69.78 69.78 69.78M132.8 38.9a7.43 7.43 0 0014.85 0V7.44a7.43 7.43 0 00-14.85 0V38.9M132.8 241.63v31.46a7.43 7.43 0 0014.85 0v-31.46a7.43 7.43 0 00-14.85 0M89.54 59.91a7.43 7.43 0 006.43-11.14L80.24 21.53a7.43 7.43 0 00-12.86 7.43L83.1 56.2a7.42 7.42 0 006.43 3.71M187.2 221.62a7.43 7.43 0 00-2.72 10.14L200.2 259a7.42 7.42 0 1012.86-7.42l-15.73-27.25a7.43 7.43 0 00-10.15-2.71M18.76 70.14a7.43 7.43 0 002.72 10.15L48.72 96a7.42 7.42 0 107.43-12.86L28.9 67.42a7.43 7.43 0 00-10.14 2.72M258.97 200.24l-27.25-15.73a7.43 7.43 0 00-7.42 12.87l27.24 15.73a7.4 7.4 0 0010.14-2.72 7.43 7.43 0 00-2.71-10.15M46.28 140.27c0-4.1-3.33-7.42-7.43-7.42H7.4a7.43 7.43 0 000 14.85h31.46c4.1 0 7.43-3.33 7.43-7.43M273.05 132.85h-31.46a7.43 7.43 0 000 14.85h31.46a7.43 7.43 0 000-14.85M48.73 184.51L21.5 200.24a7.43 7.43 0 107.42 12.86l27.25-15.73a7.43 7.43 0 00-7.43-12.86M251.54 67.42L224.3 83.15A7.43 7.43 0 00231.72 96l27.24-15.73a7.43 7.43 0 00-7.42-12.86M83.1 224.34l-15.73 27.24a7.43 7.43 0 0012.87 7.43l15.73-27.25a7.43 7.43 0 00-12.87-7.42M187.2 58.91a7.4 7.4 0 0010.14-2.71l15.73-27.25a7.43 7.43 0 10-12.86-7.42l-15.73 27.24a7.43 7.43 0 002.71 10.14"/></svg> |
@@ -0,0 +1 @@ | |||
<svg width="280.5" height="280.5" xmlns="http://www.w3.org/2000/svg"><g fill="#fff"><path d="M140.22 210.04c38.48 0 69.78-31.3 69.78-69.78s-31.3-69.78-69.78-69.78c-38.47 0-69.78 31.3-69.78 69.78s31.3 69.78 69.78 69.78M132.8 38.9a7.43 7.43 0 0014.85 0V7.44a7.43 7.43 0 00-14.85 0V38.9M132.8 241.63v31.46a7.43 7.43 0 0014.85 0v-31.46a7.43 7.43 0 00-14.85 0M89.54 59.91a7.43 7.43 0 006.43-11.14L80.24 21.53a7.43 7.43 0 00-12.86 7.43L83.1 56.2a7.42 7.42 0 006.43 3.71M187.2 221.62a7.43 7.43 0 00-2.72 10.14L200.2 259a7.42 7.42 0 1012.86-7.42l-15.73-27.25a7.43 7.43 0 00-10.15-2.71M18.76 70.14a7.43 7.43 0 002.72 10.15L48.72 96a7.42 7.42 0 107.43-12.86L28.9 67.42a7.43 7.43 0 00-10.14 2.72M258.97 200.24l-27.25-15.73a7.43 7.43 0 00-7.42 12.87l27.24 15.73a7.4 7.4 0 0010.14-2.72 7.43 7.43 0 00-2.71-10.15M46.28 140.27c0-4.1-3.33-7.42-7.43-7.42H7.4a7.43 7.43 0 000 14.85h31.46c4.1 0 7.43-3.33 7.43-7.43M273.05 132.85h-31.46a7.43 7.43 0 000 14.85h31.46a7.43 7.43 0 000-14.85M48.73 184.51L21.5 200.24a7.43 7.43 0 107.42 12.86l27.25-15.73a7.43 7.43 0 00-7.43-12.86M251.54 67.42L224.3 83.15A7.43 7.43 0 00231.72 96l27.24-15.73a7.43 7.43 0 00-7.42-12.86M83.1 224.34l-15.73 27.24a7.43 7.43 0 0012.87 7.43l15.73-27.25a7.43 7.43 0 00-12.87-7.42M187.2 58.91a7.4 7.4 0 0010.14-2.71l15.73-27.25a7.43 7.43 0 10-12.86-7.42l-15.73 27.24a7.43 7.43 0 002.71 10.14"/></g></svg> |
@@ -0,0 +1 @@ | |||
<svg width="294.71" height="189.16" xmlns="http://www.w3.org/2000/svg"><path d="M109.03 119.2c0-29.19 26.4-52.94 58.86-52.94 7.02 0 13.81 1.08 20.29 3.22a54.36 54.36 0 0125.04-14.36A51.95 51.95 0 00166.9 26.3c-4.21 0-8.41.52-12.48 1.52-3 .74-6.15-.44-7.9-2.99a57.13 57.13 0 00-85.58-9.84 56.95 56.95 0 00-18.33 36.65 7.42 7.42 0 01-4.2 6c-.69.34-1.38.68-2.05 1.06-.62.34-1.29.6-1.98.76A44.3 44.3 0 009.8 74.66a43.2 43.2 0 00-9.8 27.58c0 24.19 19.78 43.86 44.1 43.86h52.96a48.04 48.04 0 0112.03-24.63c-.03-.76-.05-1.51-.05-2.27" fill="#61c9e7"/><path d="M269.25 120.4a7.43 7.43 0 01-4.46-9.5 28.85 28.85 0 001.72-9.82c0-14.1-10.27-26.16-24.7-30.9a43.63 43.63 0 00-21.08-1.55c-9.9 1.68-18.74 6.7-24.5 14.12a7.43 7.43 0 01-8.8 2.26 49.12 49.12 0 00-19.53-3.96c-24.27 0-44.02 17.1-44.02 38.1 0 1.34.09 2.73.26 4.1a7.39 7.39 0 01-2.23 6.26 34.3 34.3 0 00-9.71 16.53 31.03 31.03 0 00.02 14.85c4 16.13 20.62 28.27 40.46 28.27h100.63c22.82 0 41.38-16.04 41.38-35.76 0-14.45-9.99-27.41-25.44-33" fill="#4492a8"/></svg> |
@@ -0,0 +1 @@ | |||
<svg width="251.88" height="220.4" xmlns="http://www.w3.org/2000/svg"><g fill="#61c9e7"><path d="M47.2 155.9h157.35c26.07 0 47.27-21.1 47.27-47a46.59 46.59 0 00-18.56-37.34 7.5 7.5 0 01-2.64-3.85 55.5 55.5 0 00-53.08-39.86c-4.5 0-9 .54-13.35 1.62-3 .74-6.14-.45-7.9-3a61.1 61.1 0 00-91.5-10.5 60.87 60.87 0 00-19.6 39.19 7.43 7.43 0 01-4.2 6c-.74.35-1.48.73-2.2 1.13-.62.34-1.29.6-1.98.75a47.48 47.48 0 00-26.35 16.3A46.3 46.3 0 00-.05 108.9c0 25.92 21.2 47 47.26 47M47.94 186.84a16.77 16.77 0 100 33.54 16.77 16.77 0 000-33.54M141.7 203.6a16.78 16.78 0 10-33.56.02 16.78 16.78 0 0033.56-.01M214.36 203.6a16.77 16.77 0 10-33.54 0 16.77 16.77 0 0033.54 0"/></g></svg> |
@@ -0,0 +1 @@ | |||
<svg width="24" height="24" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"/><path d="M22 12h-4M6 12H2M12 6V2M12 22v-4"/></svg> |
@@ -0,0 +1 @@ | |||
<svg width="128.67" height="188.52" xmlns="http://www.w3.org/2000/svg"><path d="M-.02 126.27c0 34.33 28.86 62.26 64.33 62.26 35.48 0 64.34-27.93 64.34-62.26 0-26.94-42-93.7-64.34-126.26C41.97 32.57-.02 99.33-.02 126.27" fill="#61c9e7"/></svg> |
@@ -0,0 +1 @@ | |||
<svg width="254.44" height="256.38" xmlns="http://www.w3.org/2000/svg"><g fill="#61c9e7"><path d="M233.3 71.58a7.42 7.42 0 01-2.63-3.84 55.5 55.5 0 00-53.08-39.87c-4.5 0-9 .55-13.35 1.63-3 .74-6.15-.45-7.9-3A61.1 61.1 0 00106 .01 60.89 60.89 0 0064.83 16a60.87 60.87 0 00-19.6 39.2 7.42 7.42 0 01-4.2 6c-.73.34-1.47.72-2.2 1.12-.61.35-1.28.6-1.97.75A47.48 47.48 0 0010.5 79.37 46.3 46.3 0 000 108.92c0 25.91 21.2 47 47.26 47H204.6c26.06 0 47.27-21.09 47.27-47a46.6 46.6 0 00-18.56-37.34M231.32 187.91c0-4.1-3.33-7.43-7.43-7.43H28.87a7.42 7.42 0 100 14.86h195.02c4.1 0 7.43-3.34 7.43-7.43M213.14 241.54H18.12a7.43 7.43 0 000 14.85h195.02a7.42 7.42 0 100-14.85M62.28 211.43a7.42 7.42 0 100 14.85h71.47a7.43 7.43 0 000-14.85H62.28M247 211.43h-71.48a7.42 7.42 0 100 14.85H247a7.43 7.43 0 000-14.85"/></g></svg> |
@@ -0,0 +1 @@ | |||
<svg width="318.87" height="166.86" xmlns="http://www.w3.org/2000/svg"><g fill="#dec60f"><path d="M159.33 78.52c-42.13 0-76.84 32.35-80.6 73.5h161.19c-3.76-41.15-38.47-73.5-80.59-73.5M152 7.44v36a7.42 7.42 0 1014.85 0v-36a7.43 7.43 0 00-14.85 0M79.67 21.37a7.43 7.43 0 00-2.72 10.15l18 31.18a7.42 7.42 0 1012.86-7.43l-18-31.18a7.43 7.43 0 00-10.14-2.72M62.63 95.01l-31.18-18a7.43 7.43 0 00-7.42 12.86l31.17 18a7.4 7.4 0 0010.15-2.71A7.43 7.43 0 0062.63 95M50.8 159.45c0-4.1-3.32-7.43-7.42-7.43h-36a7.43 7.43 0 000 14.86h36c4.1 0 7.42-3.33 7.42-7.43M311.33 152.02h-36a7.43 7.43 0 000 14.86h36a7.42 7.42 0 100-14.86M263.6 107.88l31.17-18A7.42 7.42 0 10287.34 77l-31.17 18a7.43 7.43 0 007.43 12.87M213.73 65.43a7.43 7.43 0 0010.15-2.72l18-31.18a7.42 7.42 0 10-12.86-7.43l-18 31.18a7.42 7.42 0 002.71 10.15"/></g></svg> |
@@ -0,0 +1 @@ | |||
<svg width="251.88" height="258.64" xmlns="http://www.w3.org/2000/svg"><path d="M251.85 108.9a46.6 46.6 0 00-18.56-37.34 7.43 7.43 0 01-2.63-3.85 55.5 55.5 0 00-53.08-39.86c-4.5 0-9 .54-13.35 1.62-3 .74-6.15-.45-7.9-3A61.1 61.1 0 00106 0a60.89 60.89 0 00-41.18 15.97 60.87 60.87 0 00-19.6 39.2 7.43 7.43 0 01-4.2 6c-.73.35-1.47.73-2.2 1.13-.61.34-1.28.6-1.97.75a47.48 47.48 0 00-26.35 16.3A46.3 46.3 0 00-.01 108.9c0 25.92 21.2 47 47.26 47H204.6c26.07 0 47.27-21.08 47.27-47" fill="#4492a8"/><g fill="#61c9e7"><path d="M66.66 223.44a7.42 7.42 0 007.1-9.6l-8.94-29.18a7.42 7.42 0 10-14.2 4.35l8.94 29.17a7.43 7.43 0 007.1 5.26M107.4 258.62a7.43 7.43 0 007.1-9.6l-8.93-29.18a7.42 7.42 0 10-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25M159.22 213.06a7.43 7.43 0 00-4.93 9.28l8.94 29.17a7.43 7.43 0 1014.2-4.35L168.5 218a7.43 7.43 0 00-9.27-4.93M128.84 223.44a7.41 7.41 0 007.1-9.6l-8.93-29.18a7.43 7.43 0 10-14.2 4.35l8.94 29.17a7.43 7.43 0 007.1 5.26M191.03 223.44a7.41 7.41 0 007.1-9.6l-8.94-29.18A7.42 7.42 0 10175 189l8.94 29.17a7.43 7.43 0 007.1 5.26"/></g></svg> |
@@ -0,0 +1 @@ | |||
<svg width="294.71" height="258.57" xmlns="http://www.w3.org/2000/svg"><g fill="#61c9e7"><path d="M109.04 119.18c0-29.2 26.4-52.95 58.87-52.95 7 0 13.8 1.08 20.28 3.22a54.32 54.32 0 0125.04-14.35 51.93 51.93 0 00-46.32-28.83c-4.21 0-8.41.51-12.47 1.52-3 .75-6.16-.45-7.91-3A57.15 57.15 0 0099.46.02c-14.3 0-27.97 5.31-38.51 14.95a56.93 56.93 0 00-18.33 36.65 7.43 7.43 0 01-4.2 6c-.68.32-1.37.68-2.05 1.05-.62.35-1.29.6-1.98.75A44.3 44.3 0 009.8 74.64 43.2 43.2 0 000 102.2c0 24.18 19.78 43.86 44.1 43.86h52.96a48.04 48.04 0 0112.03-24.63c-.03-.76-.05-1.51-.05-2.26M64.99 258.59a7.44 7.44 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.17a7.43 7.43 0 007.1 5.26M127.17 258.59a7.43 7.43 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.17a7.43 7.43 0 007.1 5.26M189.36 258.59a7.43 7.43 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.17a7.43 7.43 0 007.1 5.26"/></g><path d="M269.26 120.42a7.43 7.43 0 01-4.46-9.5 28.86 28.86 0 001.73-9.82c0-14.1-10.28-26.15-24.7-30.91a43.57 43.57 0 00-21.09-1.55c-9.9 1.69-18.74 6.7-24.49 14.12a7.43 7.43 0 01-8.8 2.28 49.12 49.12 0 00-19.54-3.96c-24.27 0-44.02 17.09-44.02 38.09 0 1.35.09 2.73.26 4.1a7.43 7.43 0 01-2.23 6.26 34.21 34.21 0 00-9.71 16.53 31.03 31.03 0 00.03 14.85c4 16.13 20.61 28.27 40.45 28.27h100.63c22.82 0 41.38-16.05 41.38-35.76 0-14.46-9.98-27.41-25.44-33" fill="#4492a8"/></svg> |
@@ -0,0 +1 @@ | |||
<svg width="318.94" height="289.62" xmlns="http://www.w3.org/2000/svg"><g fill="#61c9e7"><path d="M285.04 127.6a7.42 7.42 0 01-2.64-3.85 55.5 55.5 0 00-53.08-39.86c-4.5 0-9 .54-13.34 1.62-3 .75-6.15-.45-7.9-3a61.1 61.1 0 00-50.33-26.48 60.89 60.89 0 00-41.18 15.98 60.88 60.88 0 00-19.6 39.19 7.42 7.42 0 01-4.2 6c-.73.35-1.47.73-2.2 1.13-.62.34-1.29.6-1.98.75a47.48 47.48 0 00-26.35 16.3 46.29 46.29 0 00-10.51 29.55c0 25.92 21.2 47 47.27 47h157.34c26.06 0 47.26-21.08 47.26-47a46.6 46.6 0 00-18.56-37.33" paint-order="stroke fill markers"/><path d="M91.98 287.1a7.41 7.41 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25M154.16 287.1a7.43 7.43 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25M216.35 287.1a7.43 7.43 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 10-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25M116.58 259.97a7.43 7.43 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.17a7.43 7.43 0 007.1 5.26M178.76 259.97a7.43 7.43 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 10-14.2 4.35l8.94 29.17a7.43 7.43 0 007.1 5.26M278.33 287.09a7.43 7.43 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 10-14.2 4.35l8.94 29.17a7.43 7.43 0 007.1 5.26M240.73 259.95a7.43 7.43 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 10-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25"/></g><path d="M79.82-.05c-3.37 1.02-6.69 2.2-9.96 3.54-57.55 23.57-85.2 89.56-61.63 147.12 8.19 20 21.61 36.84 38.8 49.07a61.6 61.6 0 01-10.8-34.83c0-14.3 4.93-28.13 13.96-39.22a62.66 62.66 0 015.99-6.36 125.82 125.82 0 01-1.69-70.09A128.43 128.43 0 0179.82-.05z" fill="#e1c014"/></svg> |
@@ -0,0 +1 @@ | |||
<svg width="318.94" height="289.62" xmlns="http://www.w3.org/2000/svg"><g fill="#61c9e7"><path d="M285.04 127.6a7.42 7.42 0 01-2.64-3.85 55.5 55.5 0 00-53.08-39.86c-4.5 0-9 .54-13.34 1.62-3 .75-6.15-.45-7.9-3a61.1 61.1 0 00-50.33-26.48 60.89 60.89 0 00-41.18 15.98 60.88 60.88 0 00-19.6 39.19 7.42 7.42 0 01-4.2 6c-.73.35-1.47.73-2.2 1.13-.62.34-1.29.6-1.98.75a47.48 47.48 0 00-26.35 16.3 46.29 46.29 0 00-10.51 29.55c0 25.92 21.2 47 47.27 47h157.34c26.06 0 47.26-21.08 47.26-47a46.6 46.6 0 00-18.56-37.33" paint-order="stroke fill markers"/><path d="M119.95 289.63a7.41 7.41 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25M182.13 289.63a7.43 7.43 0 007.1-9.6l-8.93-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25M244.32 289.63a7.43 7.43 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 10-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25"/></g><path d="M80.66-.9C77.29.14 73.97 1.32 70.7 2.66 13.15 26.22-14.5 92.21 9.07 149.77c8.19 20 21.61 36.84 38.8 49.07A61.6 61.6 0 0137.07 164c0-14.3 4.93-28.13 13.96-39.22a62.66 62.66 0 015.99-6.36 125.82 125.82 0 01-1.69-70.09A128.43 128.43 0 0180.66-.89z" fill="#e1c014"/></svg> |
@@ -0,0 +1 @@ | |||
<svg width="318.94" height="289.62" xmlns="http://www.w3.org/2000/svg"><g fill="#61c9e7"><path d="M285.04 127.6a7.42 7.42 0 01-2.64-3.85 55.5 55.5 0 00-53.08-39.86c-4.5 0-9 .54-13.34 1.62-3 .75-6.15-.45-7.9-3a61.1 61.1 0 00-50.33-26.48 60.89 60.89 0 00-41.18 15.98 60.88 60.88 0 00-19.6 39.19 7.42 7.42 0 01-4.2 6c-.73.35-1.47.73-2.2 1.13-.62.34-1.29.6-1.98.75a47.48 47.48 0 00-26.35 16.3 46.29 46.29 0 00-10.51 29.55c0 25.92 21.2 47 47.27 47h157.34c26.06 0 47.26-21.08 47.26-47a46.6 46.6 0 00-18.56-37.33" paint-order="stroke fill markers"/><path d="M125.03 287.94a7.41 7.41 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25M187.21 287.94a7.43 7.43 0 007.1-9.6l-8.93-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25M249.4 287.94a7.43 7.43 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 10-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25M149.63 260.8a7.43 7.43 0 007.1-9.6l-8.94-29.17a7.43 7.43 0 00-14.2 4.35l8.94 29.17a7.43 7.43 0 007.1 5.26M211.81 260.8a7.43 7.43 0 007.1-9.6l-8.94-29.17a7.43 7.43 0 10-14.2 4.35l8.95 29.17a7.43 7.43 0 007.1 5.26"/></g><path d="M79.82-.05c-3.37 1.02-6.69 2.2-9.96 3.54-57.55 23.57-85.2 89.56-61.63 147.12 8.19 20 21.61 36.84 38.8 49.07a61.6 61.6 0 01-10.8-34.83c0-14.3 4.93-28.13 13.96-39.22a62.66 62.66 0 015.99-6.36 125.82 125.82 0 01-1.69-70.09A128.43 128.43 0 0179.82-.05z" fill="#e1c014"/></svg> |
@@ -0,0 +1 @@ | |||
<svg width="238.3" height="228.57" xmlns="http://www.w3.org/2000/svg"><g fill="#61c9e7"><path d="M91.64 66.54L86.46 85.9l18.52-7.66 16.8 10.91-1.55-19.97 15.57-12.62-19.48-4.69-7.19-18.7-10.48 17.07-20.01 1.04 13 15.26M176.79 45.46l-7.67-10.3-3.51 12.35-12.17 4.1 10.66 7.16.14 12.84 10.1-7.92 12.27 3.83-4.42-12.06 7.43-10.48-12.83.48"/><path d="M212.17 155.64a5.25 5.25 0 01-1.87-2.73 39.52 39.52 0 00-37.79-28.38c-3.2 0-6.4.39-9.5 1.16a5.29 5.29 0 01-5.63-2.14 43.5 43.5 0 00-65.14-7.48 43.35 43.35 0 00-13.95 27.9 5.3 5.3 0 01-3 4.28c-.52.24-1.04.51-1.56.8-.44.24-.91.42-1.4.53a33.84 33.84 0 00-18.77 11.6 32.99 32.99 0 00-7.48 21.05c0 18.44 15.1 33.45 33.65 33.45h112c18.56 0 33.66-15 33.66-33.45a33.2 33.2 0 00-13.22-26.59" paint-order="stroke fill markers"/></g><path d="M82.9-.33a117.62 117.62 0 00-10.3 3.67C13.06 27.73-15.55 96 8.84 155.54a116.43 116.43 0 0026.36 39.24 45.62 45.62 0 01-1.87-12.9A46.3 46.3 0 0143.7 152.7l.01-.01a46.59 46.59 0 0120.52-14.47A130.4 130.4 0 0156.7 50.6 132.88 132.88 0 0182.9-.32z" fill="#e1c014"/></svg> |
@@ -0,0 +1 @@ | |||
<svg width="238.3" height="228.57" xmlns="http://www.w3.org/2000/svg"><g fill="#61c9e7"><path d="M98.43 102.12l-5.19 19.36 18.53-7.65 16.8 10.9-1.56-19.97 15.58-12.61-19.49-4.7-7.18-18.7-10.48 17.08-20.02 1.04 13.01 15.25M176.79 45.46l-7.67-10.3-3.51 12.35-12.17 4.1 10.66 7.16.14 12.84 10.1-7.92 12.27 3.83-4.42-12.06 7.43-10.48-12.83.48"/><path d="M220.76 179.59a3.53 3.53 0 01-1.26-1.84 26.54 26.54 0 00-31.76-18.28 3.55 3.55 0 01-3.78-1.44 29.21 29.21 0 00-53.12 13.72 3.56 3.56 0 01-2 2.87c-.36.17-.71.35-1.06.54-.3.16-.61.28-.95.35a22.73 22.73 0 00-12.6 7.8 22.15 22.15 0 00-5.02 14.13 22.56 22.56 0 0022.6 22.47h75.23a22.56 22.56 0 0022.6-22.47 22.3 22.3 0 00-8.88-17.85" paint-order="stroke fill markers"/></g><path d="M82.47-.02a117.62 117.62 0 00-10.3 3.67C12.63 28.03-15.97 96.3 8.41 155.85a115.91 115.91 0 0062.9 63.4 116.18 116.18 0 0054.39 8.73c-14.4-2.74-25.45-15.44-25.45-30.57 0-3.46.59-6.86 1.7-10.09a130.42 130.42 0 01-40.12-53.35 130.4 130.4 0 01-5.56-83.06A132.88 132.88 0 0182.47-.02z" fill="#e1c014"/></svg> |
@@ -0,0 +1 @@ | |||
<svg width="189.63" height="228.4" xmlns="http://www.w3.org/2000/svg"><path d="M62.11 134a130.4 130.4 0 01-5.55-83.05A132.89 132.89 0 0182.76 0a117.64 117.64 0 00-10.3 3.67C12.92 28.07-15.7 96.34 8.7 155.88a115.91 115.91 0 0062.89 63.4 115.93 115.93 0 0089.3.36c3.39-1.39 6.7-2.92 9.92-4.62a132.66 132.66 0 01-54.4-17.92 130.45 130.45 0 01-54.3-63.1" fill="#e1c014"/><path d="M122.15 123.3l-5.19 19.37 18.52-7.65 16.81 10.9-1.56-19.97 15.58-12.61-19.49-4.7-7.18-18.7-10.48 17.08-20.02 1.04 13.01 15.25M176.79 45.46l-7.67-10.3-3.51 12.35-12.17 4.1 10.66 7.16.14 12.84 10.1-7.92 12.27 3.83-4.42-12.06 7.43-10.48-12.83.48" fill="#61c9e7"/></svg> |
@@ -0,0 +1 @@ | |||
<svg width="251.87" height="223.45" xmlns="http://www.w3.org/2000/svg"><path d="M47.26 155.9H204.6c26.06 0 47.27-21.1 47.27-47a46.6 46.6 0 00-18.56-37.34 7.43 7.43 0 01-2.64-3.85 55.5 55.5 0 00-53.08-39.86c-4.5 0-9 .54-13.34 1.62-3 .74-6.15-.45-7.9-3A61.1 61.1 0 00106.01 0a60.89 60.89 0 00-41.18 15.98 60.88 60.88 0 00-19.6 39.19 7.42 7.42 0 01-4.2 6c-.74.35-1.48.73-2.2 1.13-.62.34-1.29.6-1.98.75a47.47 47.47 0 00-26.35 16.3A46.3 46.3 0 000 108.9c0 25.92 21.2 47 47.26 47" fill="#4492a8"/><g fill="#61c9e7"><path d="M55.55 179.74a7.42 7.42 0 00-4.92 9.28l8.94 29.17a7.43 7.43 0 1014.2-4.35l-8.94-29.17a7.42 7.42 0 00-9.28-4.93M117.74 179.74a7.43 7.43 0 00-4.93 9.28l8.94 29.17a7.43 7.43 0 0014.2-4.35l-8.94-29.17a7.43 7.43 0 00-9.27-4.93M179.92 179.74a7.43 7.43 0 00-4.92 9.28l8.94 29.17a7.43 7.43 0 0014.2-4.35l-8.94-29.17a7.43 7.43 0 00-9.28-4.93"/></g></svg> |
@@ -0,0 +1 @@ | |||
<svg width="251.87" height="236.67" xmlns="http://www.w3.org/2000/svg"><g fill="#61c9e7"><path d="M233.28 71.56a7.43 7.43 0 01-2.63-3.85 55.5 55.5 0 00-53.08-39.86c-4.5 0-9 .55-13.35 1.63-3 .74-6.15-.45-7.9-3A61.1 61.1 0 00105.99-.01 60.88 60.88 0 0064.8 15.97a60.87 60.87 0 00-19.6 39.2 7.43 7.43 0 01-4.2 6c-.73.34-1.47.72-2.2 1.12-.62.34-1.28.6-1.97.75a47.48 47.48 0 00-26.36 16.3A46.3 46.3 0 00-.02 108.9c0 25.91 21.2 47 47.26 47h157.34c26.06 0 47.27-21.09 47.27-47a46.6 46.6 0 00-18.56-37.34M42.25 186.84a6.63 6.63 0 00-6.63 6.63v10.21l-10.86-2.94a6.63 6.63 0 10-3.47 12.8l10.45 2.83-6.32 9.73a6.63 6.63 0 1011.12 7.21l5.8-8.93 6.28 9.35a6.62 6.62 0 1011-7.4l-6.71-10 10.3-2.8a6.63 6.63 0 10-3.48-12.8l-10.85 2.95v-10.21a6.63 6.63 0 00-6.63-6.63M125.91 186.84a6.63 6.63 0 00-6.63 6.63v10.21l-10.86-2.94a6.63 6.63 0 10-3.47 12.8l10.45 2.83-6.31 9.73a6.63 6.63 0 1011.12 7.21l5.8-8.93 6.28 9.35a6.62 6.62 0 1011-7.4l-6.72-10 10.3-2.8a6.63 6.63 0 00-3.47-12.8l-10.86 2.95v-10.21a6.63 6.63 0 00-6.63-6.63M209.58 186.84a6.63 6.63 0 00-6.63 6.63v10.21l-10.86-2.94a6.63 6.63 0 10-3.47 12.8l10.45 2.83-6.32 9.73a6.63 6.63 0 1011.12 7.21l5.8-8.93 6.28 9.35a6.62 6.62 0 1011-7.4l-6.71-10 10.3-2.8a6.63 6.63 0 10-3.48-12.8l-10.85 2.95v-10.21a6.63 6.63 0 00-6.63-6.63"/></g></svg> |
@@ -0,0 +1 @@ | |||
<svg width="307.19" height="291.33" xmlns="http://www.w3.org/2000/svg"><path d="M55.57 92.54c0 9.27 3.43 18.02 9.54 24.76a62.36 62.36 0 0121.22-10.38 75.72 75.72 0 0125.61-45.8 36.97 36.97 0 00-56.37 31.4" fill="#dec60f"/><g fill="#61c9e7"><path d="M288.6 129.3a7.42 7.42 0 01-2.63-3.85A55.5 55.5 0 00232.9 85.6c-4.5 0-9 .54-13.34 1.62-3 .75-6.15-.45-7.9-3a61.1 61.1 0 00-50.33-26.48 60.89 60.89 0 00-41.18 15.98 60.88 60.88 0 00-19.6 39.19 7.42 7.42 0 01-4.2 6c-.73.35-1.47.73-2.2 1.13-.62.34-1.29.6-1.98.75a47.48 47.48 0 00-26.35 16.3 46.29 46.29 0 00-10.51 29.55c0 25.92 21.2 47 47.27 47H259.9c26.06 0 47.26-21.08 47.26-47a46.6 46.6 0 00-18.56-37.33M95.35 263.77a7.41 7.41 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25M157.53 263.77a7.43 7.43 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25M219.72 263.77a7.43 7.43 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 10-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25"/></g><g fill="#dec60f"><path d="M85.08 7.45v20.89a7.43 7.43 0 0014.85 0V7.44a7.43 7.43 0 10-14.85 0M60.41 44.36a7.43 7.43 0 006.42-11.14L56.4 15.12a7.43 7.43 0 00-12.86 7.44l10.45 18.09a7.42 7.42 0 006.43 3.71M40.61 54L22.52 43.56a7.43 7.43 0 10-7.43 12.86l18.1 10.45A7.43 7.43 0 0040.62 54M35.73 92.54c0-4.1-3.32-7.43-7.42-7.43H7.4a7.43 7.43 0 000 14.85h20.9c4.1 0 7.42-3.32 7.42-7.42M40.61 131.07a7.43 7.43 0 10-7.42-12.86l-18.1 10.44a7.43 7.43 0 107.43 12.87l18.1-10.45M120.9 43.36a7.42 7.42 0 0010.15-2.72l10.43-18.09a7.43 7.43 0 00-12.86-7.42l-10.45 18.1a7.43 7.43 0 002.72 10.13"/></g><g fill="#61c9e7"><path d="M194.41 290.59a7.41 7.41 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.17a7.43 7.43 0 007.1 5.26M132.7 289.39a7.41 7.41 0 007.1-9.6l-8.95-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.17a7.43 7.43 0 007.1 5.26M280.17 261.6a7.43 7.43 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 10-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25M254.86 288.41a7.41 7.41 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25"/></g></svg> |
@@ -0,0 +1 @@ | |||
<svg width="307.19" height="291.33" xmlns="http://www.w3.org/2000/svg"><path d="M55.57 92.54c0 9.27 3.43 18.02 9.54 24.76a62.36 62.36 0 0121.22-10.38 75.72 75.72 0 0125.61-45.8 36.97 36.97 0 00-56.37 31.4" fill="#dec60f"/><g fill="#61c9e7"><path d="M288.6 129.3a7.42 7.42 0 01-2.63-3.85A55.5 55.5 0 00232.9 85.6c-4.5 0-9 .54-13.34 1.62-3 .75-6.15-.45-7.9-3a61.1 61.1 0 00-50.33-26.48 60.89 60.89 0 00-41.18 15.98 60.88 60.88 0 00-19.6 39.19 7.42 7.42 0 01-4.2 6c-.73.35-1.47.73-2.2 1.13-.62.34-1.29.6-1.98.75a47.48 47.48 0 00-26.35 16.3 46.29 46.29 0 00-10.51 29.55c0 25.92 21.2 47 47.27 47H259.9c26.06 0 47.26-21.08 47.26-47a46.6 46.6 0 00-18.56-37.33M123.52 291.33a7.41 7.41 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25M185.7 291.33a7.43 7.43 0 007.1-9.6l-8.93-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25M247.89 291.33a7.43 7.43 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 10-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25"/></g><g fill="#dec60f"><path d="M85.08 7.45v20.89a7.43 7.43 0 0014.85 0V7.44a7.43 7.43 0 10-14.85 0M60.41 44.36a7.43 7.43 0 006.42-11.14L56.4 15.12a7.43 7.43 0 00-12.86 7.44l10.45 18.09a7.42 7.42 0 006.43 3.71M40.61 54L22.52 43.56a7.43 7.43 0 10-7.43 12.86l18.1 10.45A7.43 7.43 0 0040.62 54M35.73 92.54c0-4.1-3.32-7.43-7.42-7.43H7.4a7.43 7.43 0 000 14.85h20.9c4.1 0 7.42-3.32 7.42-7.42M40.61 131.07a7.43 7.43 0 10-7.42-12.86l-18.1 10.44a7.43 7.43 0 107.43 12.87l18.1-10.45M120.9 43.36a7.42 7.42 0 0010.15-2.72l10.43-18.09a7.43 7.43 0 00-12.86-7.42l-10.45 18.1a7.43 7.43 0 002.72 10.13"/></g></svg> |
@@ -0,0 +1 @@ | |||
<svg width="307.19" height="291.33" xmlns="http://www.w3.org/2000/svg"><path d="M55.57 92.54c0 9.27 3.43 18.02 9.54 24.76a62.36 62.36 0 0121.22-10.38 75.72 75.72 0 0125.61-45.8 36.97 36.97 0 00-56.37 31.4" fill="#dec60f"/><g fill="#61c9e7"><path d="M288.6 129.3a7.42 7.42 0 01-2.63-3.85A55.5 55.5 0 00232.9 85.6c-4.5 0-9 .54-13.34 1.62-3 .75-6.15-.45-7.9-3a61.1 61.1 0 00-50.33-26.48 60.89 60.89 0 00-41.18 15.98 60.88 60.88 0 00-19.6 39.19 7.42 7.42 0 01-4.2 6c-.73.35-1.47.73-2.2 1.13-.62.34-1.29.6-1.98.75a47.48 47.48 0 00-26.35 16.3 46.29 46.29 0 00-10.51 29.55c0 25.92 21.2 47 47.27 47H259.9c26.06 0 47.26-21.08 47.26-47a46.6 46.6 0 00-18.56-37.33M116.92 264.97a7.41 7.41 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25M179.1 264.97a7.43 7.43 0 007.1-9.6l-8.93-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25M241.29 264.97a7.43 7.43 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 10-14.2 4.35l8.94 29.18a7.43 7.43 0 007.1 5.25"/></g><g fill="#dec60f"><path d="M85.08 7.45v20.89a7.43 7.43 0 0014.85 0V7.44a7.43 7.43 0 10-14.85 0M60.41 44.36a7.43 7.43 0 006.42-11.14L56.4 15.12a7.43 7.43 0 00-12.86 7.44l10.45 18.09a7.42 7.42 0 006.43 3.71M40.61 54L22.52 43.56a7.43 7.43 0 10-7.43 12.86l18.1 10.45A7.43 7.43 0 0040.62 54M35.73 92.54c0-4.1-3.32-7.43-7.42-7.43H7.4a7.43 7.43 0 000 14.85h20.9c4.1 0 7.42-3.32 7.42-7.42M40.61 131.07a7.43 7.43 0 10-7.42-12.86l-18.1 10.44a7.43 7.43 0 107.43 12.87l18.1-10.45M120.9 43.36a7.42 7.42 0 0010.15-2.72l10.43-18.09a7.43 7.43 0 00-12.86-7.42l-10.45 18.1a7.43 7.43 0 002.72 10.13"/></g><path d="M215.98 291.79a7.41 7.41 0 007.1-9.6L214.15 253a7.43 7.43 0 00-14.2 4.35l8.95 29.17a7.43 7.43 0 007.1 5.26M154.27 290.59a7.41 7.41 0 007.1-9.6l-8.94-29.18a7.43 7.43 0 00-14.2 4.35l8.94 29.17a7.43 7.43 0 007.1 5.26" fill="#61c9e7"/></svg> |
@@ -0,0 +1 @@ | |||
<svg width="307.19" height="213.61" xmlns="http://www.w3.org/2000/svg"><path d="M288.6 129.37a7.38 7.38 0 01-2.63-3.84 55.51 55.51 0 00-53.08-39.87c-4.5 0-9 .55-13.34 1.63-3 .75-6.15-.45-7.9-3a61.1 61.1 0 00-91.51-10.5 60.89 60.89 0 00-19.6 39.2 7.44 7.44 0 01-4.2 6c-.73.34-1.47.71-2.2 1.11-.62.35-1.29.6-1.98.75a47.54 47.54 0 00-26.35 16.3 46.33 46.33 0 00-10.51 29.56c0 25.92 21.2 47 47.27 47H259.9c26.06 0 47.26-21.08 47.26-47a46.63 46.63 0 00-18.56-37.34" fill="#61c9e7"/><g fill="#dec60f"><path d="M55.57 92.57a36.7 36.7 0 009.54 24.76 62.36 62.36 0 0121.22-10.39 75.68 75.68 0 0125.61-45.78 36.97 36.97 0 00-56.37 31.41M92.5 35.77c4.1 0 7.43-3.33 7.43-7.43V7.45a7.42 7.42 0 10-14.85 0v20.9c0 4.09 3.33 7.42 7.43 7.42M53.97 40.7a7.42 7.42 0 1012.87-7.42l-10.45-18.1a7.43 7.43 0 00-12.86 7.43l10.44 18.1M15.1 56.44l18.09 10.45a7.47 7.47 0 0010.14-2.72 7.43 7.43 0 00-2.71-10.15l-18.1-10.45a7.43 7.43 0 00-7.43 12.87M7.42 100.04H28.3a7.43 7.43 0 000-14.86H7.41a7.42 7.42 0 100 14.86M43.33 120.97a7.42 7.42 0 00-10.14-2.72l-18.1 10.44a7.43 7.43 0 007.43 12.86l18.1-10.44a7.43 7.43 0 002.71-10.14M120.9 43.37a7.42 7.42 0 0010.15-2.72l10.43-18.1a7.43 7.43 0 00-12.86-7.42l-10.45 18.1a7.43 7.43 0 002.72 10.14"/></g></svg> |
@@ -0,0 +1 @@ | |||
<svg width="280.5" height="280.5" xmlns="http://www.w3.org/2000/svg"><path d="M257 219.74a4.3 4.3 0 01-1.53-2.24 32.38 32.38 0 00-30.96-23.26c-2.63 0-5.25.32-7.78.95a4.33 4.33 0 01-4.61-1.75 35.63 35.63 0 00-53.38-6.13 35.52 35.52 0 00-11.43 22.87 4.34 4.34 0 01-2.45 3.5c-.42.2-.86.42-1.28.65-.36.2-.75.35-1.15.44a27.73 27.73 0 00-15.37 9.5 27.03 27.03 0 00-6.13 17.25 27.52 27.52 0 0027.56 27.4h91.77c15.2 0 27.57-12.29 27.57-27.4a27.2 27.2 0 00-10.82-21.78" fill="#61c9e7" paint-order="stroke fill markers"/><g fill="#dec60f"><path d="M140.2 70.73A69.86 69.86 0 0070.4 140.5c0 33.33 23.5 61.27 54.8 68.14a40.6 40.6 0 0110.03-5.1 48.61 48.61 0 0114.7-25.58h.01a48.59 48.59 0 0153.5-8.05 69.35 69.35 0 006.52-29.41 69.86 69.86 0 00-69.78-69.78zm-.9 131.44l-.1.04-.04.03.13-.07zM132.8 38.9a7.43 7.43 0 0014.85 0V7.44a7.43 7.43 0 00-14.85 0V38.9M89.54 59.91a7.43 7.43 0 006.43-11.14L80.24 21.53a7.43 7.43 0 00-12.86 7.43L83.1 56.2a7.42 7.42 0 006.43 3.71M18.76 70.14a7.43 7.43 0 002.72 10.15L48.72 96a7.42 7.42 0 107.43-12.86L28.9 67.42a7.43 7.43 0 00-10.14 2.72M46.28 140.27c0-4.1-3.33-7.42-7.43-7.42H7.4a7.43 7.43 0 000 14.85h31.46c4.1 0 7.43-3.33 7.43-7.43M273.05 132.85h-31.46a7.43 7.43 0 000 14.85h31.46a7.43 7.43 0 000-14.85M48.73 184.51L21.5 200.24a7.43 7.43 0 107.42 12.86l27.25-15.73a7.43 7.43 0 00-7.43-12.86M251.54 67.42L224.3 83.15A7.43 7.43 0 00231.72 96l27.24-15.73a7.43 7.43 0 00-7.42-12.86M83.1 224.34l-15.73 27.24a7.43 7.43 0 0012.87 7.43l15.73-27.25a7.43 7.43 0 00-12.87-7.42M187.2 58.91a7.4 7.4 0 0010.14-2.71l15.73-27.25a7.43 7.43 0 10-12.86-7.42l-15.73 27.24a7.43 7.43 0 002.71 10.14"/></g></svg> |
@@ -0,0 +1 @@ | |||
<svg width="280.5" height="280.5" version="1.1" xmlns="http://www.w3.org/2000/svg"><g fill="#dec60f"><path d="M140.22 210.04c38.48 0 69.78-31.3 69.78-69.78s-31.3-69.78-69.78-69.78c-38.47 0-69.78 31.3-69.78 69.78s31.3 69.78 69.78 69.78M132.8 38.9a7.43 7.43 0 0014.85 0V7.44a7.43 7.43 0 00-14.85 0V38.9M132.8 241.63v31.46a7.43 7.43 0 0014.85 0v-31.46a7.43 7.43 0 00-14.85 0M89.54 59.91a7.43 7.43 0 006.43-11.14L80.24 21.53a7.43 7.43 0 00-12.86 7.43L83.1 56.2a7.42 7.42 0 006.43 3.71M187.2 221.62a7.43 7.43 0 00-2.72 10.14L200.2 259a7.42 7.42 0 1012.86-7.42l-15.73-27.25a7.43 7.43 0 00-10.15-2.71M18.76 70.14a7.43 7.43 0 002.72 10.15L48.72 96a7.42 7.42 0 107.43-12.86L28.9 67.42a7.43 7.43 0 00-10.14 2.72M258.97 200.24l-27.25-15.73a7.43 7.43 0 00-7.42 12.87l27.24 15.73a7.4 7.4 0 0010.14-2.72 7.43 7.43 0 00-2.71-10.15M46.28 140.27c0-4.1-3.33-7.42-7.43-7.42H7.4a7.43 7.43 0 000 14.85h31.46c4.1 0 7.43-3.33 7.43-7.43M273.05 132.85h-31.46a7.43 7.43 0 000 14.85h31.46a7.43 7.43 0 000-14.85M48.73 184.51L21.5 200.24a7.43 7.43 0 107.42 12.86l27.25-15.73a7.43 7.43 0 00-7.43-12.86M251.54 67.42L224.3 83.15A7.43 7.43 0 00231.72 96l27.24-15.73a7.43 7.43 0 00-7.42-12.86M83.1 224.34l-15.73 27.24a7.43 7.43 0 0012.87 7.43l15.73-27.25a7.43 7.43 0 00-12.87-7.42M187.2 58.91a7.4 7.4 0 0010.14-2.71l15.73-27.25a7.43 7.43 0 10-12.86-7.42l-15.73 27.24a7.43 7.43 0 002.71 10.14" fill="#dec60f"/></g></svg> |
@@ -0,0 +1 @@ | |||
<svg width="251.88" height="229.04" xmlns="http://www.w3.org/2000/svg"><path d="M75.45 140.62l33.04-97.27h41.74l-24.34 65.69h50.42l-30.73 46.87h58.97c26.07 0 47.27-21.08 47.27-47a46.61 46.61 0 00-18.56-37.34 7.46 7.46 0 01-2.64-3.84 55.5 55.5 0 00-53.08-39.86c-4.5 0-9 .54-13.35 1.62-3 .75-6.14-.45-7.9-3A61.08 61.08 0 0064.79 16a60.88 60.88 0 00-19.6 39.2 7.43 7.43 0 01-4.2 6c-.73.34-1.47.72-2.2 1.12-.62.34-1.29.6-1.98.76a47.44 47.44 0 00-26.35 16.29A46.33 46.33 0 00-.05 108.92c0 25.9 21.2 47 47.26 47h68.74l3.84-15.3H75.45" fill="#61c9e7"/><path d="M125.83 108.97l24.35-65.7h-41.75L75.4 140.56h44.35l-3.84 15.3-3.72 14.84-14.61 58.29 38.2-58.29 9.76-14.85 30.73-46.87h-50.43" fill="#dec60f"/></svg> |
@@ -0,0 +1 @@ | |||
<svg width="245.8" height="298.37" xmlns="http://www.w3.org/2000/svg"><g fill="#4492a8"><path d="M-.06 132.86c8.9-5.53 21.39-11.12 35.4-11.12 23.22 0 34.73 7.95 44.28 16.87 9.1-8.16 21.08-16.87 43.97-16.87 22.8 0 36.28 9.08 44.27 16.46 9.12-8.71 21.61-16.46 43.96-16.46 13.6 0 25.4 5.26 33.91 10.63-8.93-53.35-60.62-94.4-122.85-94.4-62.43 0-114.24 41.3-122.94 94.9"/><path d="M115.5 137.06v113.96c0 12.32 3.83 23.97 10.79 32.83 7.36 9.36 17.32 14.52 28.04 14.52 10.7 0 20.66-5.16 28.03-14.52 6.96-8.86 10.8-20.51 10.8-32.83a7.42 7.42 0 10-14.86 0c0 17.92-10.76 32.5-23.97 32.5-13.23 0-23.98-14.58-23.98-32.5V136.93a66.87 66.87 0 00-6.72-.33c-2.97 0-5.65.16-8.13.46M130.3 23.3V7.44a7.43 7.43 0 00-14.85 0v15.88a155.99 155.99 0 0114.85 0"/></g></svg> |
@@ -0,0 +1,72 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright Copyright (c) 2020, Julien Veyssier | |||
* | |||
* @author Julien Veyssier <eneiluj@posteo.net> | |||
* | |||
* @license AGPL-3.0 | |||
* | |||
* This code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License, version 3, | |||
* as published by the Free Software Foundation. | |||
* | |||
* This program 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, version 3, | |||
* along with this program. If not, see <http://www.gnu.org/licenses/> | |||
* | |||
*/ | |||
namespace OCA\WeatherStatus\AppInfo; | |||
use OCA\WeatherStatus\Capabilities; | |||
use OCP\AppFramework\App; | |||
use OCP\AppFramework\Bootstrap\IBootContext; | |||
use OCP\AppFramework\Bootstrap\IBootstrap; | |||
use OCP\AppFramework\Bootstrap\IRegistrationContext; | |||
use OCP\Dashboard\RegisterWidgetEvent; | |||
use OCP\EventDispatcher\IEventDispatcher; | |||
use OCP\EventDispatcher\Event; | |||
use OCP\Util; | |||
/** | |||
* Class Application | |||
* | |||
* @package OCA\WeatherStatus\AppInfo | |||
*/ | |||
class Application extends App implements IBootstrap { | |||
/** @var string */ | |||
public const APP_ID = 'weather_status'; | |||
/** | |||
* Application constructor. | |||
* | |||
* @param array $urlParams | |||
*/ | |||
public function __construct(array $urlParams = []) { | |||
parent::__construct(self::APP_ID, $urlParams); | |||
$dispatcher = $this->getContainer()->query(IEventDispatcher::class); | |||
$dispatcher->addListener(RegisterWidgetEvent::class, function (Event $e) { | |||
Util::addScript(self::APP_ID, 'weather-status'); | |||
}); | |||
} | |||
/** | |||
* @inheritDoc | |||
*/ | |||
public function register(IRegistrationContext $context): void { | |||
// Register OCS Capabilities | |||
$context->registerCapability(Capabilities::class); | |||
} | |||
public function boot(IBootContext $context): void { | |||
} | |||
} |
@@ -0,0 +1,55 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright Copyright (c) 2020, Julien Veyssier | |||
* | |||
* @author Julien Veyssier <eneiluj@posteo.net> | |||
* | |||
* @license AGPL-3.0 | |||
* | |||
* This code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License, version 3, | |||
* as published by the Free Software Foundation. | |||
* | |||
* This program 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, version 3, | |||
* along with this program. If not, see <http://www.gnu.org/licenses/> | |||
* | |||
*/ | |||
namespace OCA\WeatherStatus; | |||
use OCP\Capabilities\ICapability; | |||
use OCA\WeatherStatus\AppInfo\Application; | |||
/** | |||
* Class Capabilities | |||
* | |||
* @package OCA\UserStatus | |||
*/ | |||
class Capabilities implements ICapability { | |||
/** | |||
* Capabilities constructor. | |||
* | |||
*/ | |||
public function __construct() { | |||
} | |||
/** | |||
* @inheritDoc | |||
*/ | |||
public function getCapabilities() { | |||
return [ | |||
Application::APP_ID => [ | |||
'enabled' => true, | |||
], | |||
]; | |||
} | |||
} |
@@ -0,0 +1,124 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright Copyright (c) 2020, Julien Veyssier | |||
* | |||
* @author Julien Veyssier <eneiluj@posteo.net> | |||
* | |||
* @license AGPL-3.0 | |||
* | |||
* This code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License, version 3, | |||
* as published by the Free Software Foundation. | |||
* | |||
* This program 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, version 3, | |||
* along with this program. If not, see <http://www.gnu.org/licenses/> | |||
* | |||
*/ | |||
namespace OCA\WeatherStatus\Controller; | |||
use OCA\WeatherStatus\Service\WeatherStatusService; | |||
use OCP\AppFramework\Http; | |||
use OCP\AppFramework\Http\DataResponse; | |||
use OCP\AppFramework\OCSController; | |||
use OCP\ILogger; | |||
use OCP\IRequest; | |||
class WeatherStatusController extends OCSController { | |||
/** @var string */ | |||
private $userId; | |||
/** @var ILogger */ | |||
private $logger; | |||
/** @var WeatherStatusService */ | |||
private $service; | |||
public function __construct(string $appName, | |||
IRequest $request, | |||
ILogger $logger, | |||
WeatherStatusService $service, | |||
string $userId) { | |||
parent::__construct($appName, $request); | |||
$this->userId = $userId; | |||
$this->logger = $logger; | |||
$this->service = $service; | |||
} | |||
/** | |||
* @NoAdminRequired | |||
* | |||
* Try to use the address set in user personal settings as weather location | |||
* | |||
* @return DataResponse with success state and address information | |||
*/ | |||
public function usePersonalAddress(): DataResponse { | |||
return new DataResponse($this->service->usePersonalAddress()); | |||
} | |||
/** | |||
* @NoAdminRequired | |||
* | |||
* Change the weather status mode. There are currently 2 modes: | |||
* - ask the browser | |||
* - use the user defined address | |||
* | |||
* @param int $mode New mode | |||
* @return DataResponse success state | |||
*/ | |||
public function setMode(int $mode): DataResponse { | |||
return new DataResponse($this->service->setMode($mode)); | |||
} | |||
/** | |||
* @NoAdminRequired | |||
* | |||
* Set address and resolve it to get coordinates | |||
* or directly set coordinates and get address with reverse geocoding | |||
* | |||
* @param string|null $address Any approximative or exact address | |||
* @param float|null $lat Latitude in decimal degree format | |||
* @param float|null $lon Longitude in decimal degree format | |||
* @return DataResponse with success state and address information | |||
*/ | |||
public function setLocation(?string $address, ?float $lat, ?float $lon): DataResponse { | |||
$currentWeather = $this->service->setLocation($address, $lat, $lon); | |||
return new DataResponse($currentWeather); | |||
} | |||
/** | |||
* @NoAdminRequired | |||
* | |||
* Get stored user location | |||
* | |||
* @return DataResponse which contains coordinates, formatted address and current weather status mode | |||
*/ | |||
public function getLocation(): DataResponse { | |||
$location = $this->service->getLocation(); | |||
return new DataResponse($location); | |||
} | |||
/** | |||
* @NoAdminRequired | |||
* | |||
* Get forecast for current location | |||
* | |||
* @return DataResponse which contains success state and filtered forecast data | |||
*/ | |||
public function getForecast(): DataResponse { | |||
$forecast = $this->service->getForecast(); | |||
if (isset($forecast['success']) && $forecast['success'] === false) { | |||
return new DataResponse($forecast, Http::STATUS_NOT_FOUND); | |||
} else { | |||
return new DataResponse($forecast); | |||
} | |||
} | |||
} |
@@ -0,0 +1,429 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright Copyright (c) 2020, Julien Veyssier | |||
* | |||
* @author Julien Veyssier <eneiluj@posteo.net> | |||
* | |||
* @license AGPL-3.0 | |||
* | |||
* This code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License, version 3, | |||
* as published by the Free Software Foundation. | |||
* | |||
* This program 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, version 3, | |||
* along with this program. If not, see <http://www.gnu.org/licenses/> | |||
* | |||
*/ | |||
namespace OCA\WeatherStatus\Service; | |||
use OCP\IConfig; | |||
use OCP\IL10N; | |||
use OCP\App\IAppManager; | |||
use OCP\Accounts\IAccountManager; | |||
use OCP\Accounts\PropertyDoesNotExistException; | |||
use OCP\IUserManager; | |||
use OCP\Http\Client\IClientService; | |||
use OCP\Http\Client\IClient; | |||
use OCP\ICacheFactory; | |||
use OCP\ICache; | |||
use OCP\ILogger; | |||
use OCA\WeatherStatus\AppInfo\Application; | |||
/** | |||
* Class WeatherStatusService | |||
* | |||
* @package OCA\WeatherStatus\Service | |||
*/ | |||
class WeatherStatusService { | |||
public const MODE_BROWSER_LOCATION = 1; | |||
public const MODE_MANUAL_LOCATION = 2; | |||
/** @var IClientService */ | |||
private $clientService; | |||
/** @var IClient */ | |||
private $client; | |||
/** @var IConfig */ | |||
private $config; | |||
/** @var IL10N */ | |||
private $l10n; | |||
/** @var ILogger */ | |||
private $logger; | |||
/** @var IAccountManager */ | |||
private $accountManager; | |||
/** @var IUserManager */ | |||
private $userManager; | |||
/** @var IAppManager */ | |||
private $appManager; | |||
/** @var ICacheFactory */ | |||
private $cacheFactory; | |||
/** @var ICache */ | |||
private $cache; | |||
/** @var string */ | |||
private $userId; | |||
/** @var string */ | |||
private $version; | |||
/** | |||
* WeatherStatusService constructor | |||
* | |||
* @param IClientService $clientService | |||
* @param IConfig $config | |||
* @param IL10N $l10n | |||
* @param ILogger $logger | |||
* @param IAccountManager $accountManager | |||
* @param IUserManager $userManager | |||
* @param IAppManager $appManager | |||
* @param ICacheFactory $cacheFactory | |||
* @param string $userId | |||
*/ | |||
public function __construct(IClientService $clientService, | |||
IConfig $config, | |||
IL10N $l10n, | |||
ILogger $logger, | |||
IAccountManager $accountManager, | |||
IUserManager $userManager, | |||
IAppManager $appManager, | |||
ICacheFactory $cacheFactory, | |||
string $userId) { | |||
$this->config = $config; | |||
$this->userId = $userId; | |||
$this->l10n = $l10n; | |||
$this->logger = $logger; | |||
$this->accountManager = $accountManager; | |||
$this->userManager = $userManager; | |||
$this->appManager = $appManager; | |||
$this->version = $appManager->getAppVersion(Application::APP_ID); | |||
$this->clientService = $clientService; | |||
$this->client = $clientService->newClient(); | |||
if ($cacheFactory->isAvailable()) { | |||
$this->cache = $cacheFactory->createDistributed(); | |||
} | |||
} | |||
/** | |||
* Change the weather status mode. There are currently 2 modes: | |||
* - ask the browser | |||
* - use the user defined address | |||
* @param int $mode New mode | |||
* @return array success state | |||
*/ | |||
public function setMode(int $mode): array { | |||
$this->config->setUserValue($this->userId, Application::APP_ID, 'mode', strval($mode)); | |||
return ['success' => true]; | |||
} | |||
/** | |||
* Try to use the address set in user personal settings as weather location | |||
* | |||
* @return array with success state and address information | |||
*/ | |||
public function usePersonalAddress(): array { | |||
$account = $this->accountManager->getAccount($this->userManager->get($this->userId)); | |||
try { | |||
$address = $account->getProperty('address')->getValue(); | |||
} catch (PropertyDoesNotExistException $e) { | |||
return ['success' => false]; | |||
} | |||
if ($address === '') { | |||
return ['success' => false]; | |||
} | |||
return $this->setAddress($address); | |||
} | |||
/** | |||
* Set address and resolve it to get coordinates | |||
* or directly set coordinates and get address with reverse geocoding | |||
* | |||
* @param string|null $address Any approximative or exact address | |||
* @param float|null $lat Latitude in decimal degree format | |||
* @param float|null $lon Longitude in decimal degree format | |||
* @return array with success state and address information | |||
*/ | |||
public function setLocation(?string $address, ?float $lat, ?float $lon): array { | |||
if (!is_null($lat) && !is_null($lon)) { | |||
// store coordinates | |||
$this->config->setUserValue($this->userId, Application::APP_ID, 'lat', strval($lat)); | |||
$this->config->setUserValue($this->userId, Application::APP_ID, 'lon', strval($lon)); | |||
// resolve and store formatted address | |||
$address = $this->resolveLocation($lat, $lon); | |||
$address = $address ? $address : $this->l10n->t('Unknown address'); | |||
$this->config->setUserValue($this->userId, Application::APP_ID, 'address', $address); | |||
// get and store altitude | |||
$altitude = $this->getAltitude($lat, $lon); | |||
$this->config->setUserValue($this->userId, Application::APP_ID, 'altitude', strval($altitude)); | |||
return [ | |||
'address' => $address, | |||
'success' => true, | |||
]; | |||
} elseif ($address) { | |||
return $this->setAddress($address); | |||
} else { | |||
return ['success' => false]; | |||
} | |||
} | |||
/** | |||
* Provide address information from coordinates | |||
* | |||
* @param float $lat Latitude in decimal degree format | |||
* @param float $lon Longitude in decimal degree format | |||
*/ | |||
private function resolveLocation(float $lat, float $lon): ?string { | |||
$params = [ | |||
'lat' => number_format($lat, 2), | |||
'lon' => number_format($lon, 2), | |||
'addressdetails' => 1, | |||
'format' => 'json', | |||
]; | |||
$url = 'https://nominatim.openstreetmap.org/reverse'; | |||
$result = $this->requestJSON($url, $params); | |||
return $this->formatOsmAddress($result); | |||
} | |||
/** | |||
* Get altitude from coordinates | |||
* | |||
* @param float $lat Latitude in decimal degree format | |||
* @param float $lon Longitude in decimal degree format | |||
* @return float altitude in meter | |||
*/ | |||
private function getAltitude(float $lat, float $lon): float { | |||
$params = [ | |||
'locations' => $lat . ',' . $lon, | |||
]; | |||
$url = 'https://api.opentopodata.org/v1/srtm30m'; | |||
$result = $this->requestJSON($url, $params); | |||
$altitude = 0; | |||
if (isset($result['results']) && is_array($result['results']) && count($result['results']) > 0 | |||
&& is_array($result['results'][0]) && isset($result['results'][0]['elevation'])) { | |||
$altitude = floatval($result['results'][0]['elevation']); | |||
} | |||
return $altitude; | |||
} | |||
/** | |||
* @return string Formatted address from JSON nominatim result | |||
*/ | |||
private function formatOsmAddress(array $json): ?string { | |||
if (isset($json['address']) && isset($json['display_name'])) { | |||
$jsonAddr = $json['address']; | |||
$cityAddress = ''; | |||
// priority : city, town, village, municipality | |||
if (isset($jsonAddr['city'])) { | |||
$cityAddress .= $jsonAddr['city']; | |||
} elseif (isset($jsonAddr['town'])) { | |||
$cityAddress .= $jsonAddr['town']; | |||
} elseif (isset($jsonAddr['village'])) { | |||
$cityAddress .= $jsonAddr['village']; | |||
} elseif (isset($jsonAddr['municipality'])) { | |||
$cityAddress .= $jsonAddr['municipality']; | |||
} else { | |||
return $json['display_name']; | |||
} | |||
// post code | |||
if (isset($jsonAddr['postcode'])) { | |||
$cityAddress .= ', ' . $jsonAddr['postcode']; | |||
} | |||
// country | |||
if (isset($jsonAddr['country'])) { | |||
$cityAddress .= ', ' . $jsonAddr['country']; | |||
return $cityAddress; | |||
} else { | |||
return $json['display_name']; | |||
} | |||
} elseif (isset($json['display_name'])) { | |||
return $json['display_name']; | |||
} | |||
return null; | |||
} | |||
/** | |||
* Set address and resolve it to get coordinates | |||
* | |||
* @param string $address Any approximative or exact address | |||
* @return array with success state and address information (coordinates and formatted address) | |||
*/ | |||
public function setAddress(string $address): array { | |||
$addressInfo = $this->searchForAddress($address); | |||
if (isset($addressInfo['display_name']) && isset($addressInfo['lat']) && isset($addressInfo['lon'])) { | |||
$formattedAddress = $this->formatOsmAddress($addressInfo); | |||
$this->config->setUserValue($this->userId, Application::APP_ID, 'address', $formattedAddress); | |||
$this->config->setUserValue($this->userId, Application::APP_ID, 'lat', strval($addressInfo['lat'])); | |||
$this->config->setUserValue($this->userId, Application::APP_ID, 'lon', strval($addressInfo['lon'])); | |||
$this->config->setUserValue($this->userId, Application::APP_ID, 'mode', strval(self::MODE_MANUAL_LOCATION)); | |||
// get and store altitude | |||
$altitude = $this->getAltitude(floatval($addressInfo['lat']), floatval($addressInfo['lon'])); | |||
$this->config->setUserValue($this->userId, Application::APP_ID, 'altitude', strval($altitude)); | |||
return [ | |||
'lat' => $addressInfo['lat'], | |||
'lon' => $addressInfo['lon'], | |||
'address' => $formattedAddress, | |||
'success' => true, | |||
]; | |||
} else { | |||
return ['success' => false]; | |||
} | |||
} | |||
/** | |||
* Ask nominatim information about an unformatted address | |||
* | |||
* @param string Unformatted address | |||
* @return array Full Nominatim result for the given address | |||
*/ | |||
private function searchForAddress(string $address): array { | |||
$params = [ | |||
'format' => 'json', | |||
'addressdetails' => '1', | |||
'extratags' => '1', | |||
'namedetails' => '1', | |||
'limit' => '1', | |||
]; | |||
$url = 'https://nominatim.openstreetmap.org/search/' . $address; | |||
$results = $this->requestJSON($url, $params); | |||
if (count($results) > 0) { | |||
return $results[0]; | |||
} | |||
return ['error' => $this->l10n->t('No result.')]; | |||
} | |||
/** | |||
* Get stored user location | |||
* | |||
* @return array which contains coordinates, formatted address and current weather status mode | |||
*/ | |||
public function getLocation(): array { | |||
$lat = $this->config->getUserValue($this->userId, Application::APP_ID, 'lat', ''); | |||
$lon = $this->config->getUserValue($this->userId, Application::APP_ID, 'lon', ''); | |||
$address = $this->config->getUserValue($this->userId, Application::APP_ID, 'address', ''); | |||
$mode = $this->config->getUserValue($this->userId, Application::APP_ID, 'mode', self::MODE_MANUAL_LOCATION); | |||
return [ | |||
'lat' => $lat, | |||
'lon' => $lon, | |||
'address' => $address, | |||
'mode' => intval($mode), | |||
]; | |||
} | |||
/** | |||
* Get forecast for current location | |||
* | |||
* @return array which contains success state and filtered forecast data | |||
*/ | |||
public function getForecast(): array { | |||
$lat = $this->config->getUserValue($this->userId, Application::APP_ID, 'lat', ''); | |||
$lon = $this->config->getUserValue($this->userId, Application::APP_ID, 'lon', ''); | |||
$alt = $this->config->getUserValue($this->userId, Application::APP_ID, 'altitude', ''); | |||
if (!is_numeric($alt)) { | |||
$alt = 0; | |||
} | |||
if (is_numeric($lat) && is_numeric($lon)) { | |||
return $this->forecastRequest(floatval($lat), floatval($lon), floatval($alt)); | |||
} else { | |||
return ['success' => false]; | |||
} | |||
} | |||
/** | |||
* Actually make the request to the forecast service | |||
* | |||
* @param float $lat Latitude of requested forecast, in decimal degree format | |||
* @param float $lon Longitude of requested forecast, in decimal degree format | |||
* @param float $altitude Altitude of requested forecast, in meter | |||
* @param int $nbValues Number of forecast values (hours) | |||
* @return array Filtered forecast data | |||
*/ | |||
private function forecastRequest(float $lat, float $lon, float $altitude, int $nbValues = 10): array { | |||
$params = [ | |||
'lat' => number_format($lat, 2), | |||
'lon' => number_format($lon, 2), | |||
'altitude' => $altitude, | |||
]; | |||
$url = 'https://api.met.no/weatherapi/locationforecast/2.0/compact'; | |||
$weather = $this->requestJSON($url, $params); | |||
if (isset($weather['properties']) && isset($weather['properties']['timeseries']) && is_array($weather['properties']['timeseries'])) { | |||
return array_slice($weather['properties']['timeseries'], 0, $nbValues); | |||
} | |||
return ['error' => $this->l10n->t('Malformed JSON data.')]; | |||
} | |||
/** | |||
* Make a HTTP GET request and parse JSON result. | |||
* Request results are cached until the 'Expires' response header says so | |||
* | |||
* @param string $url Base URL to query | |||
* @param array $params GET parameters | |||
* @return array which contains the error message or the parsed JSON result | |||
*/ | |||
private function requestJSON(string $url, array $params = []): array { | |||
if (isset($this->cache)) { | |||
$cacheKey = $url . '|' . implode(',', $params) . '|' . implode(',', array_keys($params)); | |||
if ($this->cache->hasKey($cacheKey)) { | |||
return $this->cache->get($cacheKey); | |||
} | |||
} | |||
try { | |||
$options = [ | |||
'headers' => [ | |||
'User-Agent' => 'NextcloudWeatherStatus/' . $this->version . ' nextcloud.com' | |||
], | |||
]; | |||
$reqUrl = $url; | |||
if (count($params) > 0) { | |||
$paramsContent = http_build_query($params); | |||
$reqUrl = $url . '?' . $paramsContent; | |||
} | |||
$response = $this->client->get($reqUrl, $options); | |||
$body = $response->getBody(); | |||
$headers = $response->getHeaders(); | |||
$respCode = $response->getStatusCode(); | |||
if ($respCode >= 400) { | |||
return ['error' => $this->l10n->t('Error')]; | |||
} else { | |||
$json = json_decode($body, true); | |||
if (isset($this->cache)) { | |||
// default cache duration is one hour | |||
$cacheDuration = 60 * 60; | |||
if (isset($headers['Expires']) && count($headers['Expires']) > 0) { | |||
// if the Expires response header is set, use it to define cache duration | |||
$expireTs = (new \Datetime($headers['Expires'][0]))->getTimestamp(); | |||
$nowTs = (new \Datetime())->getTimestamp(); | |||
$duration = $expireTs - $nowTs; | |||
if ($duration > $cacheDuration) { | |||
$cacheDuration = $duration; | |||
} | |||
} | |||
$this->cache->set($cacheKey, $json, $cacheDuration); | |||
} | |||
return $json; | |||
} | |||
} catch (\Exception $e) { | |||
$this->logger->warning($url . 'API error : ' . $e, ['app' => Application::APP_ID]); | |||
return ['error' => $e->getMessage()]; | |||
} | |||
} | |||
} |
@@ -0,0 +1,503 @@ | |||
<!-- | |||
- @copyright Copyright (c) 2020 Julien Veyssier <eneiluj@posteo.net> | |||
- @author Julien Veyssier <eneiluj@posteo.net> | |||
- | |||
- @license GNU AGPL version 3 or any later version | |||
- | |||
- This program 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. | |||
- | |||
- This program 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 this program. If not, see <http://www.gnu.org/licenses/>. | |||
- | |||
--> | |||
<template> | |||
<li :class="{ inline }"> | |||
<div id="weather-status-menu-item"> | |||
<Actions | |||
class="weather-status-menu-item__subheader" | |||
:default-icon="weatherIcon" | |||
:menu-title="visibleMessage"> | |||
<ActionLink v-if="address && !errorMessage" | |||
icon="icon-address" | |||
target="_blank" | |||
:href="weatherLinkTarget" | |||
:close-after-click="true"> | |||
{{ locationText }} | |||
</ActionLink> | |||
<ActionSeparator v-if="address && !errorMessage" /> | |||
<ActionButton | |||
icon="icon-crosshair" | |||
:close-after-click="true" | |||
@click="onBrowserLocationClick"> | |||
{{ t('weather_status', 'Detect location') }} | |||
</ActionButton> | |||
<ActionInput | |||
ref="addressInput" | |||
:disabled="false" | |||
icon="icon-rename" | |||
type="text" | |||
value="" | |||
@submit="onAddressSubmit"> | |||
{{ t('weather_status', 'Set custom address') }} | |||
</ActionInput> | |||
</Actions> | |||
</div> | |||
</li> | |||
</template> | |||
<script> | |||
import { showError } from '@nextcloud/dialogs' | |||
import moment from '@nextcloud/moment' | |||
import { getLocale } from '@nextcloud/l10n' | |||
import Actions from '@nextcloud/vue/dist/Components/Actions' | |||
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton' | |||
import ActionInput from '@nextcloud/vue/dist/Components/ActionInput' | |||
import ActionLink from '@nextcloud/vue/dist/Components/ActionLink' | |||
import ActionSeparator from '@nextcloud/vue/dist/Components/ActionSeparator' | |||
import * as network from './services/weatherStatusService' | |||
const MODE_BROWSER_LOCATION = 1 | |||
const MODE_MANUAL_LOCATION = 2 | |||
const weatherOptions = { | |||
clearsky_day: { | |||
icon: 'icon-clearsky-day', | |||
text: t('weather_status', 'Clear sky'), | |||
}, | |||
clearsky_night: { | |||
icon: 'icon-clearsky-night', | |||
text: t('weather_status', 'Clear sky'), | |||
}, | |||
cloudy: { | |||
icon: 'icon-cloudy', | |||
text: t('weather_status', 'Cloudy'), | |||
}, | |||
fair_day: { | |||
icon: 'icon-fair-day', | |||
text: t('weather_status', 'Fair day'), | |||
}, | |||
fair_night: { | |||
icon: 'icon-fair-night', | |||
text: t('weather_status', 'Fair night'), | |||
}, | |||
partlycloudy_day: { | |||
icon: 'icon-partlycloudy-day', | |||
text: t('weather_status', 'Partly cloudy'), | |||
}, | |||
partlycloudy_night: { | |||
icon: 'icon-partlycloudy-night', | |||
text: t('weather_status', 'Partly cloudy'), | |||
}, | |||
fog: { | |||
icon: 'icon-fog', | |||
text: t('weather_status', 'Foggy'), | |||
}, | |||
lightrain: { | |||
icon: 'icon-lightrain', | |||
text: t('weather_status', 'Light rain'), | |||
}, | |||
rain: { | |||
icon: 'icon-rain', | |||
text: t('weather_status', 'Rain'), | |||
}, | |||
heavyrain: { | |||
icon: 'icon-heavyrain', | |||
text: t('weather_status', 'Heavy rain'), | |||
}, | |||
rainshowers_day: { | |||
icon: 'icon-rainshowers-day', | |||
text: t('weather_status', 'Rain showers'), | |||
}, | |||
rainshowers_night: { | |||
icon: 'icon-rainshowers-night', | |||
text: t('weather_status', 'Rain showers'), | |||
}, | |||
lightrainshowers_day: { | |||
icon: 'icon-light-rainshowers-day', | |||
text: t('weather_status', 'Light rain showers'), | |||
}, | |||
lightrainshowers_night: { | |||
icon: 'icon-light-rainshowers-night', | |||
text: t('weather_status', 'Light rain showers'), | |||
}, | |||
heavyrainshowers_day: { | |||
icon: 'icon-heavy-rainshowers-day', | |||
text: t('weather_status', 'Heavy rain showers'), | |||
}, | |||
heavyrainshowers_night: { | |||
icon: 'icon-heavy-rainshowers-night', | |||
text: t('weather_status', 'Heavy rain showers'), | |||
}, | |||
} | |||
export default { | |||
name: 'App', | |||
components: { | |||
Actions, ActionButton, ActionInput, ActionLink, ActionSeparator, | |||
}, | |||
props: { | |||
inline: { | |||
type: Boolean, | |||
default: false, | |||
}, | |||
}, | |||
data() { | |||
return { | |||
locale: getLocale(), | |||
loading: true, | |||
errorMessage: '', | |||
mode: MODE_BROWSER_LOCATION, | |||
address: null, | |||
lat: null, | |||
lon: null, | |||
forecasts: [], | |||
loop: null, | |||
} | |||
}, | |||
computed: { | |||
useFahrenheitLocale() { | |||
return ['en_US', 'en_MH', 'en_FM', 'en_PW', 'en_KY', 'en_LR'].includes(this.locale) | |||
}, | |||
strUnit() { | |||
return this.useFahrenheitLocale ? '°F' : '°C' | |||
}, | |||
locationText() { | |||
return t('weather_status', 'More weather for {adr}', { adr: this.address }) | |||
}, | |||
sixHoursTempForecast() { | |||
return this.forecasts.length > 5 ? this.forecasts[5].data.instant.details.air_temperature : '' | |||
}, | |||
sixHoursWeatherForecast() { | |||
return this.forecasts.length > 5 ? this.forecasts[5].data.next_1_hours.summary.symbol_code : '' | |||
}, | |||
sixHoursFormattedTime() { | |||
if (this.forecasts.length > 5) { | |||
const date = moment(this.forecasts[5].time) | |||
return t('weather_status', 'at {time}', { time: date.format('LT') }) | |||
} | |||
return '' | |||
}, | |||
weatherIcon() { | |||
if (this.loading) { | |||
return 'icon-loading-small' | |||
} else { | |||
return this.sixHoursWeatherForecast && this.sixHoursWeatherForecast in weatherOptions | |||
? weatherOptions[this.sixHoursWeatherForecast].icon | |||
: 'icon-fair-day' | |||
} | |||
}, | |||
weatherText() { | |||
return this.sixHoursWeatherForecast && this.sixHoursWeatherForecast in weatherOptions | |||
? weatherOptions[this.sixHoursWeatherForecast].text + ' ' + this.sixHoursFormattedTime | |||
: '???' | |||
}, | |||
/** | |||
* The message displayed in the top right corner | |||
* | |||
* @returns {String} | |||
*/ | |||
visibleMessage() { | |||
if (this.loading) { | |||
return t('weather_status', 'Loading weather') | |||
} else if (this.errorMessage) { | |||
return this.errorMessage | |||
} else { | |||
return this.sixHoursWeatherForecast | |||
? t('weather_status', '{temperature} {unit} {weatherDescription}', { | |||
temperature: this.getLocalizedTemperature(this.sixHoursTempForecast), | |||
unit: this.strUnit, | |||
weatherDescription: this.weatherText, | |||
}) | |||
: t('weather_status', 'Set location for weather') | |||
} | |||
}, | |||
weatherLinkTarget() { | |||
return 'https://www.windy.com/-Rain-thunder-rain?rain,' + this.lat + ',' + this.lon + ',11' | |||
}, | |||
}, | |||
mounted() { | |||
this.initWeatherStatus() | |||
}, | |||
methods: { | |||
async initWeatherStatus() { | |||
try { | |||
const loc = await network.getLocation() | |||
this.lat = loc.lat | |||
this.lon = loc.lon | |||
this.address = loc.address | |||
this.mode = loc.mode | |||
if (this.mode === MODE_BROWSER_LOCATION) { | |||
this.askBrowserLocation() | |||
} else if (this.mode === MODE_MANUAL_LOCATION) { | |||
this.startLoop() | |||
} | |||
} catch (err) { | |||
showError(t('weather_status', 'There was an error getting the weather status information.')) | |||
console.debug(err) | |||
} | |||
}, | |||
startLoop() { | |||
clearInterval(this.loop) | |||
if (this.lat && this.lon) { | |||
this.loop = setInterval(() => this.getForecast(), 60 * 1000 * 60) | |||
this.getForecast() | |||
} else { | |||
this.loading = false | |||
} | |||
}, | |||
askBrowserLocation() { | |||
this.loading = true | |||
this.errorMessage = '' | |||
if (navigator.geolocation && window.isSecureContext) { | |||
navigator.geolocation.getCurrentPosition((position) => { | |||
console.debug('browser location success') | |||
this.lat = position.coords.latitude | |||
this.lon = position.coords.longitude | |||
this.saveMode(MODE_BROWSER_LOCATION) | |||
this.mode = MODE_BROWSER_LOCATION | |||
this.saveLocation(this.lat, this.lon) | |||
}, | |||
(error) => { | |||
console.debug('location permission refused') | |||
console.debug(error) | |||
this.saveMode(MODE_MANUAL_LOCATION) | |||
this.mode = MODE_MANUAL_LOCATION | |||
// fallback on what we have if possible | |||
if (this.lat && this.lon) { | |||
this.startLoop() | |||
} else { | |||
this.usePersonalAddress() | |||
} | |||
}) | |||
} else { | |||
console.debug('no secure context!') | |||
this.saveMode(MODE_MANUAL_LOCATION) | |||
this.mode = MODE_MANUAL_LOCATION | |||
this.startLoop() | |||
} | |||
}, | |||
async getForecast() { | |||
try { | |||
this.forecasts = await network.fetchForecast() | |||
} catch (err) { | |||
this.errorMessage = t('weather_status', 'No weather information found') | |||
console.debug(err) | |||
} | |||
this.loading = false | |||
}, | |||
async setAddress(address) { | |||
this.loading = true | |||
this.errorMessage = '' | |||
try { | |||
const loc = await network.setAddress(address) | |||
if (loc.success) { | |||
this.lat = loc.lat | |||
this.lon = loc.lon | |||
this.address = loc.address | |||
this.mode = MODE_MANUAL_LOCATION | |||
this.startLoop() | |||
} else { | |||
this.errorMessage = t('weather_status', 'Location not found') | |||
this.loading = false | |||
} | |||
} catch (err) { | |||
showError(t('weather_status', 'There was an error setting the location address.')) | |||
console.debug(err) | |||
this.loading = false | |||
} | |||
}, | |||
async saveLocation(lat, lon) { | |||
try { | |||
const loc = await network.setLocation(lat, lon) | |||
this.address = loc.address | |||
this.startLoop() | |||
} catch (err) { | |||
showError(t('weather_status', 'There was an error setting the location.')) | |||
console.debug(err) | |||
} | |||
}, | |||
async saveMode(mode) { | |||
try { | |||
await network.setMode(mode) | |||
} catch (err) { | |||
showError(t('weather_status', 'There was an error saving the mode.')) | |||
console.debug(err) | |||
} | |||
}, | |||
onBrowserLocationClick() { | |||
this.askBrowserLocation() | |||
}, | |||
async usePersonalAddress() { | |||
this.loading = true | |||
try { | |||
const loc = await network.usePersonalAddress() | |||
this.lat = loc.lat | |||
this.lon = loc.lon | |||
this.address = loc.address | |||
this.mode = MODE_MANUAL_LOCATION | |||
this.startLoop() | |||
} catch (err) { | |||
showError(t('weather_status', 'There was an error using personal address.')) | |||
console.debug(err) | |||
this.loading = false | |||
} | |||
}, | |||
onAddressSubmit() { | |||
const newAddress = this.$refs.addressInput.$el.querySelector('input[type="text"]').value | |||
this.setAddress(newAddress) | |||
}, | |||
getLocalizedTemperature(celcius) { | |||
return this.useFahrenheitLocale | |||
? ((celcius * (9 / 5)) + 32).toFixed(1) | |||
: celcius | |||
}, | |||
}, | |||
} | |||
</script> | |||
<style lang="scss"> | |||
.icon-clearsky-day { | |||
background-image: url('./../img/sun.svg'); | |||
} | |||
.icon-clearsky-night { | |||
background-image: url('./../img/moon.svg'); | |||
} | |||
.icon-cloudy { | |||
background-image: url('./../img/cloud-cloud.svg'); | |||
} | |||
.icon-fair-day { | |||
background-image: url('./../img/sun-small-cloud.svg'); | |||
} | |||
.icon-fair-night { | |||
background-image: url('./../img/moon-small-cloud.svg'); | |||
} | |||
.icon-partlycloudy-day { | |||
background-image: url('./../img/sun-cloud.svg'); | |||
} | |||
.icon-partlycloudy-night { | |||
background-image: url('./../img/moon-cloud.svg'); | |||
} | |||
.icon-fog { | |||
background-image: url('./../img/fog.svg'); | |||
} | |||
.icon-lightrain { | |||
background-image: url('./../img/light-rain.svg'); | |||
} | |||
.icon-rain { | |||
background-image: url('./../img/rain.svg'); | |||
} | |||
.icon-heavyrain { | |||
background-image: url('./../img/heavy-rain.svg'); | |||
} | |||
.icon-light-rainshowers-day { | |||
background-image: url('./../img/sun-cloud-light-rain.svg'); | |||
} | |||
.icon-light-rainshowers-night { | |||
background-image: url('./../img/moon-cloud-light-rain.svg'); | |||
} | |||
.icon-rainshowers-day { | |||
background-image: url('./../img/sun-cloud-rain.svg'); | |||
} | |||
.icon-rainshowers-night { | |||
background-image: url('./../img/moon-cloud-rain.svg'); | |||
} | |||
.icon-heavy-rainshowers-day { | |||
background-image: url('./../img/sun-cloud-heavy-rain.svg'); | |||
} | |||
.icon-heavy-rainshowers-night { | |||
background-image: url('./../img/moon-cloud-heavy-rain.svg'); | |||
} | |||
.icon-crosshair { | |||
background-color: var(--color-main-text); | |||
padding: 0 !important; | |||
mask: url(./../img/cross.svg) no-repeat; | |||
mask-size: 18px 18px; | |||
mask-position: center; | |||
-webkit-mask: url(./../img/cross.svg) no-repeat; | |||
-webkit-mask-size: 18px 18px; | |||
-webkit-mask-position: center; | |||
min-width: 44px !important; | |||
min-height: 44px !important; | |||
} | |||
li:not(.inline) .weather-status-menu-item { | |||
&__header { | |||
display: block; | |||
align-items: center; | |||
color: var(--color-main-text); | |||
padding: 10px 12px 5px 12px; | |||
box-sizing: border-box; | |||
opacity: 1; | |||
white-space: nowrap; | |||
width: 100%; | |||
text-align: center; | |||
max-width: 250px; | |||
text-overflow: ellipsis; | |||
min-width: 175px; | |||
} | |||
&__subheader { | |||
width: 100%; | |||
> button { | |||
background-color: var(--color-main-background); | |||
background-size: 16px; | |||
border: 0; | |||
border-radius: 0; | |||
font-weight: normal; | |||
font-size: 0.875em; | |||
padding-left: 40px; | |||
&:hover, | |||
&:focus { | |||
box-shadow: inset 4px 0 var(--color-primary-element); | |||
} | |||
} | |||
} | |||
} | |||
body .inline .weather-status-menu-item__subheader > button { | |||
background-color: rgba(255, 255, 255, 0.8); | |||
} | |||
body.theme--dark .inline .weather-status-menu-item__subheader > button { | |||
background-color: rgba(24, 24, 24, 0.8) !important; | |||
} | |||
.inline .weather-status-menu-item__subheader { | |||
width: 100%; | |||
> button { | |||
background-size: 16px; | |||
border: 0; | |||
border-radius: var(--border-radius-pill); | |||
font-weight: normal; | |||
font-size: 0.875em; | |||
padding-left: 40px; | |||
&:hover, | |||
&:focus { | |||
background-color: var(--color-background-hover); | |||
} | |||
&.icon-loading-small { | |||
&::after { | |||
left: 21px; | |||
} | |||
} | |||
} | |||
} | |||
li { | |||
list-style-type: none; | |||
} | |||
</style> |
@@ -0,0 +1,116 @@ | |||
/** | |||
* @copyright Copyright (c) 2020, Julien Veyssier | |||
* | |||
* @author Julien Veyssier <eneiluj@posteo.net> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program 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. | |||
* | |||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
import HttpClient from '@nextcloud/axios' | |||
import { generateOcsUrl } from '@nextcloud/router' | |||
/** | |||
* | |||
* | |||
* @param {String} lat the latitude | |||
* @param {String} lon the longitude | |||
* @returns {Promise<Object>} | |||
*/ | |||
const setLocation = async(lat, lon) => { | |||
const url = generateOcsUrl('apps/weather_status/api/v1', 2) + 'location' | |||
const response = await HttpClient.put(url, { | |||
address: '', | |||
lat, | |||
lon, | |||
}) | |||
return response.data.ocs.data | |||
} | |||
/** | |||
* | |||
* @param {String} address The location | |||
* @returns {Promise<Object>} | |||
*/ | |||
const setAddress = async(address) => { | |||
const url = generateOcsUrl('apps/weather_status/api/v1', 2) + 'location' | |||
const response = await HttpClient.put(url, { | |||
address, | |||
lat: null, | |||
lon: null, | |||
}) | |||
return response.data.ocs.data | |||
} | |||
/** | |||
* | |||
* @param {String} mode can be 1 browser or 2 custom | |||
* @returns {Promise<Object>} | |||
*/ | |||
const setMode = async(mode) => { | |||
const url = generateOcsUrl('apps/weather_status/api/v1', 2) + 'mode' | |||
const response = await HttpClient.put(url, { | |||
mode, | |||
}) | |||
return response.data.ocs.data | |||
} | |||
/** | |||
* | |||
* @returns {Promise<Object>} | |||
*/ | |||
const usePersonalAddress = async() => { | |||
const url = generateOcsUrl('apps/weather_status/api/v1', 2) + 'use-personal' | |||
const response = await HttpClient.put(url) | |||
return response.data.ocs.data | |||
} | |||
/** | |||
* Fetches the location information for current user | |||
* | |||
* @returns {Promise<Object>} | |||
*/ | |||
const getLocation = async() => { | |||
const url = generateOcsUrl('apps/weather_status/api/v1', 2) + 'location' | |||
const response = await HttpClient.get(url) | |||
return response.data.ocs.data | |||
} | |||
/** | |||
* Fetches the weather forecast | |||
* | |||
* @param {String} address The location | |||
* @returns {Promise<Object>} | |||
*/ | |||
const fetchForecast = async() => { | |||
const url = generateOcsUrl('apps/weather_status/api/v1', 2) + 'forecast' | |||
const response = await HttpClient.get(url) | |||
return response.data.ocs.data | |||
} | |||
export { | |||
usePersonalAddress, | |||
setMode, | |||
getLocation, | |||
setLocation, | |||
setAddress, | |||
fetchForecast, | |||
} |
@@ -0,0 +1,28 @@ | |||
import Vue from 'vue' | |||
import { getRequestToken } from '@nextcloud/auth' | |||
import { generateUrl } from '@nextcloud/router' | |||
import App from './App' | |||
// eslint-disable-next-line camelcase | |||
__webpack_nonce__ = btoa(getRequestToken()) | |||
// Correct the root of the app for chunk loading | |||
// eslint-disable-next-line | |||
__webpack_public_path__ = generateUrl('/apps/weather_status/js/') | |||
Vue.prototype.t = t | |||
document.addEventListener('DOMContentLoaded', function() { | |||
if (!OCA.Dashboard) { | |||
return | |||
} | |||
OCA.Dashboard.registerStatus('weather', (el) => { | |||
const Dashboard = Vue.extend(App) | |||
return new Dashboard({ | |||
propsData: { | |||
inline: true, | |||
}, | |||
}).$mount(el) | |||
}) | |||
}) |
@@ -0,0 +1,26 @@ | |||
const path = require('path') | |||
module.exports = { | |||
entry: { | |||
'weather-status': path.join(__dirname, 'src', 'weather-status') | |||
}, | |||
output: { | |||
path: path.resolve(__dirname, './js'), | |||
publicPath: '/js/', | |||
filename: '[name].js?v=[chunkhash]', | |||
jsonpFunction: 'webpackJsonpWeatherStatus' | |||
}, | |||
optimization: { | |||
splitChunks: { | |||
automaticNameDelimiter: '-', | |||
} | |||
}, | |||
module: { | |||
rules: [ | |||
{ | |||
test: /\.(png|jpg|gif|svg|woff|woff2|eot|ttf)$/, | |||
loader: 'url-loader', | |||
}, | |||
], | |||
}, | |||
} |
@@ -349,6 +349,7 @@ Feature: provisioning | |||
| user_status | | |||
| viewer | | |||
| workflowengine | | |||
| weather_status | | |||
| files_external | | |||
| oauth2 | | |||
@@ -43,6 +43,7 @@ | |||
"user_ldap", | |||
"user_status", | |||
"viewer", | |||
"weather_status", | |||
"workflowengine" | |||
], | |||
"alwaysEnabled": [ |
@@ -16,6 +16,7 @@ const oauth2 = require('./apps/oauth2/webpack') | |||
const settings = require('./apps/settings/webpack') | |||
const systemtags = require('./apps/systemtags/webpack') | |||
const user_status = require('./apps/user_status/webpack') | |||
const weather_status = require('./apps/weather_status/webpack') | |||
const twofactor_backupscodes = require('./apps/twofactor_backupcodes/webpack') | |||
const updatenotification = require('./apps/updatenotification/webpack') | |||
const workflowengine = require('./apps/workflowengine/webpack') | |||
@@ -33,6 +34,7 @@ const modules = { | |||
settings, | |||
systemtags, | |||
user_status, | |||
weather_status, | |||
twofactor_backupscodes, | |||
updatenotification, | |||
workflowengine |