Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3669497531 | |||
| c6e12d1a29 | |||
| 5125c64ded | |||
| 576b6192c0 | |||
| db2e1b427c | |||
| 8986daf096 | |||
| 4494f3f7a1 | |||
| 2319051876 | |||
| baf15e082c | |||
| f9f0c7f33b | |||
| 6f3cf0cc07 | |||
| 73755d9507 | |||
| a4e2e8102c | |||
| 8710168485 | |||
| 04d51ea8b4 | |||
| 92afb96a23 | |||
| 2e6e771f20 | |||
| f7626da7c6 | |||
| 16a58c09bd | |||
| 39ab4904c2 | |||
| ad1d3619e9 | |||
| cdd1fd6a03 |
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2020
|
||||
},
|
||||
"rules": {
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"no-console": "off"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
# following files are skipped when exporting using git archive
|
||||
test export-ignore
|
||||
.jshintrc export-ignore
|
||||
.gitlab export-ignore
|
||||
docs export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
dist/
|
||||
node_modules/
|
||||
coverage/
|
||||
.nyc_output/
|
||||
webadmin/dist/
|
||||
installer/src/certs/server.key
|
||||
|
||||
# vim swap files
|
||||
*.swp
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
Please do not use this issue tracker for support requests and bug reports.
|
||||
This issue tracker is used by the Cloudron development team to track actual
|
||||
bugs in the code.
|
||||
|
||||
Please use the forum at https://forum.cloudron.io to report bugs. For
|
||||
confidential issues, please email us at support@cloudron.io.
|
||||
@@ -1,7 +0,0 @@
|
||||
Please do not use this issue tracker for support requests and feature reports.
|
||||
This issue tracker is used by the Cloudron development team to track issues in
|
||||
the code.
|
||||
|
||||
Please use the forum at https://forum.cloudron.io to report bugs. For
|
||||
confidential issues, please email us at support@cloudron.io.
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
{
|
||||
"node": true,
|
||||
"browser": true,
|
||||
"unused": true,
|
||||
"esversion": 11
|
||||
"esversion": 6,
|
||||
"globalstrict": false,
|
||||
"predef": [
|
||||
"$",
|
||||
"angular",
|
||||
"async",
|
||||
"describe",
|
||||
"it",
|
||||
"before",
|
||||
"after",
|
||||
"require",
|
||||
"monaco",
|
||||
"Mimer",
|
||||
"ISTATES"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
The Cloudron Subscription license
|
||||
Copyright (c) 2022 Cloudron UG
|
||||
Copyright (c) 2021 Cloudron UG
|
||||
|
||||
With regard to the Cloudron Software:
|
||||
|
||||
|
||||
@@ -1,65 +1,11 @@
|
||||

|
||||
# Cloudron Dashboard
|
||||
|
||||
# Cloudron
|
||||
This is the front end code of Cloudron. The backend code is [here](https://git.cloudron.io/cloudron/box).
|
||||
|
||||
[Cloudron](https://cloudron.io) is the best way to run apps on your server.
|
||||
## Developing
|
||||
|
||||
Web applications like email, contacts, blog, chat are the backbone of the modern
|
||||
internet. Yet, we live in a world where hosting these essential applications is
|
||||
a complex task.
|
||||
|
||||
We are building the ultimate platform for self-hosting web apps. The Cloudron allows
|
||||
anyone to effortlessly host web applications on their server on their own terms.
|
||||
|
||||
## Features
|
||||
|
||||
* Single click install for apps. Check out the [App Store](https://cloudron.io/appstore.html).
|
||||
|
||||
* Per-app encrypted backups and restores.
|
||||
|
||||
* App updates delivered via the App Store.
|
||||
|
||||
* Secure - Cloudron manages the firewall. All apps are secured with HTTPS. Certificates are
|
||||
installed and renewed automatically.
|
||||
|
||||
* Centralized User & Group management. Control who can access which app.
|
||||
|
||||
* Single Sign On. Use same credentials across all apps.
|
||||
|
||||
* Automatic updates for the Cloudron platform.
|
||||
|
||||
* Trivially migrate to another server keeping your apps and data (for example, switch your
|
||||
infrastructure provider or move to a bigger server).
|
||||
|
||||
* Comprehensive [REST API](https://docs.cloudron.io/api/).
|
||||
|
||||
* [CLI](https://docs.cloudron.io/custom-apps/cli/) to configure apps.
|
||||
|
||||
* Alerts, audit logs, graphs, dns management ... and much more
|
||||
|
||||
## Demo
|
||||
|
||||
Try our demo at https://my.demo.cloudron.io (username: cloudron password: cloudron).
|
||||
|
||||
## Installing
|
||||
|
||||
[Install script](https://docs.cloudron.io/installation/) - [Pricing](https://cloudron.io/pricing.html)
|
||||
|
||||
**Note:** This repo is a small part of what gets installed on your server - there is
|
||||
the dashboard, database addons, graph container, base image etc. Cloudron also relies
|
||||
on external services such as the App Store for apps to be installed. As such, don't
|
||||
clone this repo and npm install and expect something to work.
|
||||
|
||||
## Development
|
||||
|
||||
This is the backend code of Cloudron. The frontend code is [here](https://git.cloudron.io/cloudron/dashboard).
|
||||
|
||||
The way to develop is to first install a full instance of Cloudron in a VM. Then you can use the [hotfix](https://git.cloudron.io/cloudron/cloudron-machine)
|
||||
tool to patch the VM with the latest code.
|
||||
|
||||
```
|
||||
SSH_PASSPHRASE=sshkeypassword cloudron-machine hotfix --cloudron my.example.com --release 6.0.0 --ssh-key keyname
|
||||
```
|
||||
* `npm install`
|
||||
* `gulp develop --api-origin=https://my.example.com`
|
||||
|
||||
## License
|
||||
|
||||
@@ -72,13 +18,3 @@ Just to give some heads up, we are a bit restrictive in merging changes. We are
|
||||
would like to keep our maintenance burden low. It might be best to discuss features first in the [forum](https://forum.cloudron.io),
|
||||
to also figure out how many other people will use it to justify maintenance for a feature.
|
||||
|
||||
# Localization
|
||||
|
||||

|
||||
|
||||
## Support
|
||||
|
||||
* [Documentation](https://docs.cloudron.io/)
|
||||
* [Forum](https://forum.cloudron.io/)
|
||||
|
||||
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs'),
|
||||
ldap = require('./src/ldap.js'),
|
||||
oidc = require('./src/oidc.js'),
|
||||
paths = require('./src/paths.js'),
|
||||
proxyAuth = require('./src/proxyauth.js'),
|
||||
safe = require('safetydance'),
|
||||
server = require('./src/server.js'),
|
||||
settings = require('./src/settings.js'),
|
||||
directoryServer = require('./src/directoryserver.js');
|
||||
|
||||
let logFd;
|
||||
|
||||
async function setupLogging() {
|
||||
if (process.env.BOX_ENV === 'test') return;
|
||||
|
||||
logFd = fs.openSync(paths.BOX_LOG_FILE, 'a');
|
||||
// we used to write using a stream before but it caches internally and there is no way to flush it when things crash
|
||||
process.stdout.write = process.stderr.write = function (...args) {
|
||||
const callback = typeof args[args.length-1] === 'function' ? args.pop() : function () {}; // callback is required for fs.write
|
||||
fs.write.apply(fs, [logFd, ...args, callback]);
|
||||
};
|
||||
}
|
||||
|
||||
// this is also used as the 'uncaughtException' handler which can only have synchronous functions
|
||||
function exitSync(status) {
|
||||
if (status.error) fs.write(logFd, status.error.stack + '\n', function () {});
|
||||
fs.fsyncSync(logFd);
|
||||
fs.closeSync(logFd);
|
||||
process.exit(status.code);
|
||||
}
|
||||
|
||||
async function startServers() {
|
||||
await setupLogging();
|
||||
await server.start(); // do this first since it also inits the database
|
||||
await proxyAuth.start();
|
||||
await ldap.start();
|
||||
|
||||
const conf = await settings.getDirectoryServerConfig();
|
||||
if (conf.enabled) await directoryServer.start();
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const [error] = await safe(startServers());
|
||||
if (error) return exitSync({ error: new Error(`Error starting server: ${JSON.stringify(error)}`), code: 1 });
|
||||
|
||||
// require this here so that logging handler is already setup
|
||||
const debug = require('debug')('box:box');
|
||||
|
||||
process.on('SIGHUP', async function () {
|
||||
debug('Received SIGHUP. Re-reading configs.');
|
||||
const conf = await settings.getDirectoryServerConfig();
|
||||
if (conf.enabled) await directoryServer.checkCertificate();
|
||||
});
|
||||
|
||||
process.on('SIGINT', async function () {
|
||||
debug('Received SIGINT. Shutting down.');
|
||||
|
||||
await proxyAuth.stop();
|
||||
await server.stop();
|
||||
await directoryServer.stop();
|
||||
await ldap.stop();
|
||||
await oidc.stop();
|
||||
setTimeout(process.exit.bind(process), 3000);
|
||||
});
|
||||
|
||||
process.on('SIGTERM', async function () {
|
||||
debug('Received SIGTERM. Shutting down.');
|
||||
|
||||
await proxyAuth.stop();
|
||||
await server.stop();
|
||||
await directoryServer.stop();
|
||||
await ldap.stop();
|
||||
await oidc.stop();
|
||||
setTimeout(process.exit.bind(process), 3000);
|
||||
});
|
||||
|
||||
process.on('uncaughtException', (error) => exitSync({ error, code: 1 }));
|
||||
|
||||
console.log(`Cloudron is up and running. Logs are at ${paths.BOX_LOG_FILE}`); // this goes to journalctl
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
const database = require('./src/database.js');
|
||||
|
||||
const crashNotifier = require('./src/crashnotifier.js');
|
||||
|
||||
// This is triggered by systemd with the crashed unit name as argument
|
||||
async function main() {
|
||||
if (process.argv.length !== 3) return console.error('Usage: crashnotifier.js <unitName>');
|
||||
|
||||
const unitName = process.argv[2];
|
||||
console.log('Started crash notifier for', unitName);
|
||||
|
||||
// eventlog api needs the db
|
||||
await database.initialize();
|
||||
|
||||
await crashNotifier.sendFailureLogs(unitName);
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,6 +0,0 @@
|
||||
# following files are skipped when exporting using git archive
|
||||
test export-ignore
|
||||
docs export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
dist/
|
||||
node_modules/
|
||||
|
||||
# vim swap files
|
||||
*.swp
|
||||
|
||||
# these are not done yet
|
||||
src/translation/ja.json
|
||||
src/translation/pl.json
|
||||
src/translation/si.json
|
||||
src/translation/gl.json
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"node": true,
|
||||
"browser": true,
|
||||
"unused": true,
|
||||
"esversion": 6,
|
||||
"globalstrict": false,
|
||||
"predef": [
|
||||
"$",
|
||||
"angular",
|
||||
"async",
|
||||
"describe",
|
||||
"it",
|
||||
"before",
|
||||
"after",
|
||||
"require",
|
||||
"monaco",
|
||||
"Mimer",
|
||||
"ISTATES"
|
||||
]
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
The Cloudron Subscription license
|
||||
Copyright (c) 2022 Cloudron UG
|
||||
|
||||
With regard to the Cloudron Software:
|
||||
|
||||
This software and associated documentation files (the "Software") may only be
|
||||
used in production, if you (and any entity that you represent) have agreed to,
|
||||
and are in compliance with, the Cloudron Subscription Terms of Service, available
|
||||
at https://cloudron.io/legal/terms.html (the “Subscription Terms”), or other
|
||||
agreement governing the use of the Software, as agreed by you and Cloudron,
|
||||
and otherwise have a valid Cloudron Subscription. Subject to the foregoing sentence,
|
||||
you are free to modify this Software and publish patches to the Software. You agree
|
||||
that Subscription and/or its licensors (as applicable) retain all right, title and
|
||||
interest in and to all such modifications and/or patches, and all such modifications
|
||||
and/or patches may only be used, copied, modified, displayed, distributed, or otherwise
|
||||
exploited with a valid Cloudron subscription. Notwithstanding the foregoing, you may copy
|
||||
and modify the Software for development and testing purposes, without requiring a
|
||||
subscription. You agree that Cloudron and/or its licensors (as applicable) retain
|
||||
all right, title and interest in and to all such modifications. You are not
|
||||
granted any other rights beyond what is expressly stated herein. Subject to the
|
||||
foregoing, it is forbidden to copy, merge, publish, distribute, sublicense,
|
||||
and/or sell the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
For all third party components incorporated into the Cloudron Software, those
|
||||
components are licensed under the original license provided by the owner of the
|
||||
applicable component.
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
# Cloudron Dashboard
|
||||
|
||||
This is the front end code of Cloudron. The backend code is [here](https://git.cloudron.io/cloudron/box).
|
||||
|
||||
## Developing
|
||||
|
||||
* `npm install`
|
||||
* `gulp develop --api-origin=https://my.example.com`
|
||||
|
||||
## License
|
||||
|
||||
Please note that the Cloudron code is under a source-available license. This is not the same as an
|
||||
open source license but ensures the code is available for inspection (and hacking!).
|
||||
|
||||
## Contributions
|
||||
|
||||
Just to give a heads-up, we are a bit restrictive in merging changes. We are a small team and
|
||||
would like to keep our maintenance burden low. It might be best to first discuss features in the [forum](https://forum.cloudron.io),
|
||||
which also helps to determine how many other people will use it to justify maintenance for a feature.
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"name": "dashboard",
|
||||
"version": "1.0.0",
|
||||
"description": "[Cloudron](https://cloudron.io) is the best way to run apps on your server.",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"update-translations": "curl https://translate.cloudron.io/api/components/cloudron/dashboard/file/ -o lang.zip && unzip -jo lang.zip -d ./src/translation/ && rm lang.zip"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "ssh://git@git.cloudron.io:6000/cloudron/dashboard.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.15.4",
|
||||
"bootstrap-sass": "^3.4.1",
|
||||
"chart.js": "^4.1.1",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-autoprefixer": "^8.0.0",
|
||||
"gulp-concat": "^2.6.1",
|
||||
"gulp-cssnano": "^2.1.3",
|
||||
"gulp-ejs": "^5.1.0",
|
||||
"gulp-sass": "^5.1.0",
|
||||
"gulp-serve": "^1.4.0",
|
||||
"gulp-sourcemaps": "^3.0.0",
|
||||
"moment": "^2.29.4",
|
||||
"monaco-editor": "^0.34.0",
|
||||
"node-sass": "^7.0.3",
|
||||
"rimraf": "^3.0.2",
|
||||
"xterm": "^5.1.0",
|
||||
"xterm-addon-attach": "^0.8.0",
|
||||
"xterm-addon-fit": "^0.7.0",
|
||||
"yargs": "^17.5.1"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"env": {
|
||||
"browser": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
|
||||
<!-- Modal image/video viewer -->
|
||||
<div class="modal fade" id="{{ 'mediaViewerModal-' + $id }}" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" style="max-width: 1280px; max-height: calc(100% - 60px);">
|
||||
<div class="modal-content" style="height: 100%; height: 100%; display: flex; background-color: #000; background-clip: border-box;">
|
||||
<img ng-show="mediaViewer.type === 'image'" ng-src="{{ mediaViewer.src }}" style="display: block; margin: auto; max-width: 100%; max-height: 100%;" />
|
||||
<video ng-show="mediaViewer.type === 'video'" controls preload="auto" autoplay ng-src="{{ mediaViewer.src | trustUrl}}" style="display: block; margin: auto; max-width: 100%; max-height: 100%;"></video>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- main content -->
|
||||
|
||||
<div class="toolbar">
|
||||
<div class="btn-group" role="group" style="display: block;">
|
||||
<!-- TODO figure out why a line break in code between the two buttons results in a gap visually without any margin/padding set -->
|
||||
<button class="btn btn-primary" ng-click="goDirectoryUp()" ng-disabled="cwd === ''"><i class="fas fa-arrow-left"></i></button><button class="btn btn-primary" ng-disabled="busyRefresh" ng-click="refresh()"><i class="fas fa-redo" ng-class="{ 'fa-spin': busyRefresh }"></i></button>
|
||||
</div>
|
||||
<div class="btn-group path-parts" role="group">
|
||||
<button class="btn btn-default" ng-disabled="cwd === ''" ng-click="changeDirectory('/')" ng-drop="drop($event, '/')" ng-dragleave="dragExit($event, '/')" ng-dragover="dragEnter($event, '/')"><i class="fas fa-home"></i> {{ rootDirLabel }} </button><button class="btn btn-default" ng-disabled="part.path === cwd" ng-click="changeDirectory(part.path)" ng-drop="drop($event, part.path)" ng-dragleave="dragExit($event, part.path)" ng-dragover="dragEnter($event, part.path)" ng-repeat="part in cwdParts">{{ part.name }}</button>
|
||||
</div>
|
||||
<div style="display: block;">
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fas fa-plus"></i> {{ 'filemanager.toolbar.new' | tr }}</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="hand" ng-click="onNewFile()">{{ 'filemanager.toolbar.newFile' | tr }}</a></li>
|
||||
<li><a class="hand" ng-click="onNewFolder()">{{ 'filemanager.toolbar.newFolder' | tr }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fas fa-upload"></i> {{ 'filemanager.toolbar.upload' | tr }}</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li><a class="hand" ng-click="onUploadFile()">{{ 'filemanager.toolbar.uploadFile' | tr }}</a></li>
|
||||
<li><a class="hand" ng-click="onUploadFolder()">{{ 'filemanager.toolbar.uploadFolder' | tr }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="file-list-header">
|
||||
<table class="table" style="margin: 0;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 42px"> </th>
|
||||
<th style="">{{ 'filemanager.list.name' | tr }}</th>
|
||||
<th style="width:100px">{{ 'filemanager.list.owner' | tr }}</th>
|
||||
<th style="width: 80px">{{ 'filemanager.list.size' | tr }}</th>
|
||||
<th style="width:100px">{{ 'filemanager.list.mtime' | tr }}</th>
|
||||
<th style="width: 45px;"> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="file-list" ng-class="{ 'entry-hovered': dropToBody, 'busy': busy }" context-menu="menuOptionsBlank" ng-mousedown="onClearSelection($event)" ng-drop="drop($event, null)" ng-dragleave="dragExit($event, null)" ng-dragover="dragEnter($event, null)">
|
||||
<table class="table table-hover" style="margin: 0;">
|
||||
<tbody>
|
||||
<tr ng-show="busy && !busyRefresh">
|
||||
<td colspan="6"><center><h2><i class="fa fa-circle-notch fa-spin"></i></h2></center></td>
|
||||
</tr>
|
||||
<tr ng-show="!(busy && !busyRefresh) && entries.length === 0">
|
||||
<td colspan="" class="text-center">{{ 'filemanager.list.empty' | tr }}</td>
|
||||
</tr>
|
||||
<tr style="cursor: default" ng-hide="busy && !busyRefresh" entry-hashkey="{{ entry['$$hashKey'] }}" ng-repeat="entry in entries" ng-mouseup="onMouseup($event, entry)" draggable="true" ng-dragstart="dragStart($event, entry)" ng-drop="drop($event, entry)" context-menu="menuOptions" ng-mousedown="onMousedown($event, entry)" model="entry" ng-dragleave="dragExit($event, entry)" ng-dragover="dragEnter($event, entry)" ng-class="{ 'entry-hovered': entry.hovered, 'entry-selected': isSelected(entry) }">
|
||||
<td style="width: 42px; height: 42px" ng-dblclick="open(entry)" class="text-center">
|
||||
<i ng-show="!entry.previewUrl" class="fas fa-lg {{ entry.icon }}" ng-class="{ 'text-primary': entry.isDirectory && !isSelected(entry) }"></i>
|
||||
<img ng-show="entry.previewUrl" ng-src="{{ entry.previewUrl }}" height="42" width="42" style="object-fit: cover;"/>
|
||||
</td>
|
||||
<td class="elide-table-cell" ng-dblclick="open(entry)" style="padding-left: 5px;">{{ entry.fileName }}<span ng-show="entry.isSymbolicLink" class="text-muted" style="margin-left: 20px;">{{ 'filemanager.list.symlink' | tr:{ target: entry.target } }}</span></td>
|
||||
<td style="width:100px" class="elide-table-cell" ng-dblclick="open(entry)">{{ entry.uid | prettyOwner }}</td>
|
||||
<td style="width: 80px" class="elide-table-cell" ng-dblclick="open(entry)">{{ entry.size | prettyDecimalSize }}</td>
|
||||
<td style="width:100px" class="elide-table-cell" ng-dblclick="open(entry)" uib-tooltip="{{ entry.mtime | prettyLongDate }}" tooltip-append-to-body="true">{{ entry.mtime | prettyDate }}</td>
|
||||
<td style="width: 45px">
|
||||
<button type="button" class="btn btn-xs btn-default context-menu-action" context-menu="menuOptions" model="entry" context-menu-on="click" ng-click="onEntryContextMenu($event, entry)"><i class="fas fa-ellipsis-h"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -1,513 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/* global angular */
|
||||
/* global sanitize, isModalVisible */
|
||||
|
||||
angular.module('Application').component('filetree', {
|
||||
bindings: {
|
||||
backendId: '<',
|
||||
backendType: '<',
|
||||
view: '<',
|
||||
clipboard: '<',
|
||||
onUploadFile: '&',
|
||||
onUploadFolder: '&',
|
||||
onNewFile: '&',
|
||||
onNewFolder: '&',
|
||||
onRenameEntry: '&',
|
||||
onExtractEntry: '&',
|
||||
onChownEntries: '&',
|
||||
onDeleteEntries: '&',
|
||||
onCopyEntries: '&',
|
||||
onCutEntries: '&',
|
||||
onPasteEntries: '&'
|
||||
},
|
||||
templateUrl: 'components/filetree.html?<%= revision %>',
|
||||
controller: [ '$scope', '$translate', '$timeout', 'Client', FileTreeController ]
|
||||
});
|
||||
|
||||
function FileTreeController($scope, $translate, $timeout, Client) {
|
||||
var ctrl = this;
|
||||
|
||||
$scope.backendId = this.backendId;
|
||||
$scope.backendType = this.backendType;
|
||||
$scope.view = this.view;
|
||||
|
||||
$scope.busy = true;
|
||||
$scope.busyRefresh = false;
|
||||
$scope.client = Client;
|
||||
$scope.cwd = null;
|
||||
$scope.cwdParts = [];
|
||||
$scope.rootDirLabel = '';
|
||||
$scope.entries = [];
|
||||
$scope.selected = []; // holds selected entries
|
||||
$scope.dropToBody = false;
|
||||
$scope.applicationLink = '';
|
||||
|
||||
// register so parent can call child
|
||||
$scope.$parent.registerChild($scope);
|
||||
|
||||
function isArchive(f) {
|
||||
return f.match(/\.tgz$/) ||
|
||||
f.match(/\.tar$/) ||
|
||||
f.match(/\.7z$/) ||
|
||||
f.match(/\.zip$/) ||
|
||||
f.match(/\.tar\.gz$/) ||
|
||||
f.match(/\.tar\.xz$/) ||
|
||||
f.match(/\.tar\.bz2$/);
|
||||
}
|
||||
|
||||
$scope.menuOptions = []; // shown for entries
|
||||
$scope.menuOptionsBlank = []; // shown for empty space in folder
|
||||
|
||||
function sort() {
|
||||
return $scope.entries.sort(function (a, b) {
|
||||
if (a.fileName.toLowerCase() < b.fileName.toLowerCase()) return -1;
|
||||
return 1;
|
||||
}).sort(function (a, b) {
|
||||
if ((a.isDirectory && b.isDirectory) || (!a.isDirectory && !b.isDirectory)) return 0;
|
||||
if (a.isDirectory && !b.isDirectory) return -1;
|
||||
return 1;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.isSelected = function (entry) {
|
||||
return $scope.selected.indexOf(entry) !== -1;
|
||||
};
|
||||
|
||||
function download(entry) {
|
||||
var filePath = sanitize($scope.cwd + '/' + entry.fileName);
|
||||
|
||||
Client.filesGet($scope.backendId, $scope.backendType, filePath, 'download', function (error) {
|
||||
if (error) return Client.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
$scope.dragStart = function ($event, entry) {
|
||||
var filePaths = $scope.selected.map(function (entry) { return sanitize($scope.cwd + '/' + entry.fileName); });
|
||||
$event.originalEvent.dataTransfer.setData('application/cloudron-filemanager', JSON.stringify(filePaths));
|
||||
};
|
||||
|
||||
$scope.dragEnter = function ($event, entry) {
|
||||
$event.originalEvent.stopPropagation();
|
||||
$event.originalEvent.preventDefault();
|
||||
|
||||
// if entry is string, we come from breadcrumb
|
||||
if (entry && typeof entry === 'string') $event.currentTarget.classList.add('entry-hovered');
|
||||
else if (entry && entry.isDirectory) entry.hovered = true;
|
||||
else $scope.dropToBody = true;
|
||||
|
||||
$event.originalEvent.dataTransfer.dropEffect = 'move';
|
||||
};
|
||||
|
||||
$scope.dragExit = function ($event, entry) {
|
||||
$event.originalEvent.stopPropagation();
|
||||
$event.originalEvent.preventDefault();
|
||||
|
||||
// if entry is string, we come from breadcrumb
|
||||
if (entry && typeof entry === 'string') $event.currentTarget.classList.remove('entry-hovered');
|
||||
else if (entry && entry.isDirectory) entry.hovered = false;
|
||||
$scope.dropToBody = false;
|
||||
|
||||
$event.originalEvent.dataTransfer.dropEffect = 'move';
|
||||
};
|
||||
|
||||
$scope.drop = function (event, entry) {
|
||||
event.originalEvent.stopPropagation();
|
||||
event.originalEvent.preventDefault();
|
||||
|
||||
$scope.dropToBody = false;
|
||||
|
||||
if (!event.originalEvent.dataTransfer.items[0]) return;
|
||||
|
||||
var targetFolder;
|
||||
if (entry === null) targetFolder = $scope.cwd + '/';
|
||||
else if (typeof entry === 'string') targetFolder = sanitize(entry);
|
||||
else targetFolder = sanitize($scope.cwd + '/' + (entry && entry.isDirectory ? entry.fileName : ''));
|
||||
|
||||
var dataTransfer = event.originalEvent.dataTransfer;
|
||||
var dragContent = dataTransfer.getData('application/cloudron-filemanager');
|
||||
|
||||
// check if we have internal drag'n'drop
|
||||
if (dragContent) {
|
||||
var moved = 0;
|
||||
|
||||
// we expect a JSON.stringified Array here
|
||||
try {
|
||||
dragContent = JSON.parse(dragContent);
|
||||
} catch (e) {
|
||||
console.error('Wrong drag content.', e);
|
||||
return;
|
||||
}
|
||||
|
||||
// move files
|
||||
async.eachLimit(dragContent, 5, function (oldFilePath, callback) {
|
||||
var fileName = oldFilePath.split('/').slice(-1);
|
||||
var newFilePath = sanitize(targetFolder + '/' + fileName);
|
||||
|
||||
// if we drop the item on itself
|
||||
if (oldFilePath === targetFolder) return callback();
|
||||
|
||||
// if nothing changes
|
||||
if (newFilePath === oldFilePath) return callback();
|
||||
|
||||
moved++;
|
||||
|
||||
// TODO this will overwrite files in destination!
|
||||
Client.filesRename($scope.backendId, $scope.backendType, oldFilePath, newFilePath, callback);
|
||||
}, function (error) {
|
||||
if (error) return Client.error(error);
|
||||
|
||||
// only refresh if anything has changed
|
||||
if (moved) $scope.refresh();
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// figure if a folder was dropped on a modern browser, in this case the first would have to be a directory
|
||||
var folderItem;
|
||||
try {
|
||||
folderItem = dataTransfer.items[0].webkitGetAsEntry();
|
||||
if (folderItem.isFile) return $scope.$parent.uploadFiles(event.originalEvent.dataTransfer.files, targetFolder, false);
|
||||
} catch (e) {
|
||||
return $scope.$parent.uploadFiles(event.originalEvent.dataTransfer.files, targetFolder, false);
|
||||
}
|
||||
|
||||
// if we got here we have a folder drop and a modern browser
|
||||
// now traverse the folder tree and create a file list
|
||||
var fileList = [];
|
||||
function traverseFileTree(item, path, callback) {
|
||||
if (item.isFile) {
|
||||
// Get file
|
||||
item.file(function (file) {
|
||||
fileList.push(file);
|
||||
callback();
|
||||
});
|
||||
} else if (item.isDirectory) {
|
||||
// Get folder contents
|
||||
var dirReader = item.createReader();
|
||||
dirReader.readEntries(function (entries) {
|
||||
async.each(entries, function (entry, callback) {
|
||||
traverseFileTree(entry, path + item.name + '/', callback);
|
||||
}, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
traverseFileTree(folderItem, '', function (error) {
|
||||
if (error) return console.error(error);
|
||||
|
||||
$scope.$parent.uploadFiles(fileList, targetFolder, false);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.refresh = function () {
|
||||
$scope.$parent.refresh();
|
||||
};
|
||||
|
||||
function amendIcons() {
|
||||
$scope.entries.forEach(function (e) {
|
||||
e.icon = 'fa-file';
|
||||
e.previewUrl = null;
|
||||
|
||||
if (e.isDirectory) e.icon = 'fa-folder';
|
||||
if (e.isSymbolicLink) e.icon = 'fa-link';
|
||||
if (e.isFile) {
|
||||
var mimeType = Mimer().get(e.fileName.toLowerCase());
|
||||
var mimeGroup = mimeType.split('/')[0];
|
||||
if (mimeGroup === 'text') e.icon = 'fa-file-alt';
|
||||
// if (mimeGroup === 'image') e.icon = 'fa-file-image';
|
||||
if (mimeGroup === 'image') {
|
||||
e.icon = 'fa-file-image';
|
||||
e.previewUrl = Client.filesGetLink($scope.backendId, $scope.backendType, sanitize($scope.cwd + '/' + e.fileName));
|
||||
}
|
||||
if (mimeGroup === 'video') e.icon = 'fa-file-video';
|
||||
if (mimeGroup === 'audio') e.icon = 'fa-file-audio';
|
||||
if (mimeType === 'text/csv') e.icon = 'fa-file-csv';
|
||||
if (mimeType === 'application/pdf') e.icon = 'fa-file-pdf';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// called from the parent
|
||||
$scope.onRefresh = function () {
|
||||
$scope.selected = [];
|
||||
$scope.busy = true;
|
||||
$scope.busyRefresh = true;
|
||||
|
||||
Client.filesGet($scope.backendId, $scope.backendType, $scope.cwd, 'data', function (error, result) {
|
||||
if (error && error.statusCode !== 404) return Client.error(error);
|
||||
|
||||
$scope.entries = result ? result.entries : [];
|
||||
amendIcons();
|
||||
sort();
|
||||
|
||||
$scope.busyRefresh = false;
|
||||
$scope.busy = false;
|
||||
});
|
||||
};
|
||||
|
||||
function openDirectory(path) {
|
||||
$scope.cwd = path;
|
||||
$scope.selected = [];
|
||||
|
||||
$scope.cwdParts = path.split('/').filter(function (p) { return !!p; }).map(function (p, i) { return { name: decodeURIComponent(p), path: path.split('/').slice(0, i+1).join('/') }; });
|
||||
|
||||
// refresh will set busy to false once done
|
||||
$scope.refresh();
|
||||
}
|
||||
|
||||
function openFile(entry) {
|
||||
var mimeType = Mimer().get(entry.fileName);
|
||||
var mimeGroup = mimeType.split('/')[0];
|
||||
var path = sanitize($scope.cwd + '/' + entry.fileName);
|
||||
|
||||
if (mimeGroup === 'video' || mimeGroup === 'image') {
|
||||
$scope.mediaViewer.show(entry);
|
||||
} else if (mimeType === 'application/pdf') {
|
||||
Client.filesGet($scope.backendId, $scope.backendType, path, 'open', function (error) { if (error) return Client.error(error); });
|
||||
} else if (mimeGroup === 'text' || mimeGroup === 'application') {
|
||||
$scope.$parent.textEditor.show($scope.cwd, entry);
|
||||
} else {
|
||||
Client.filesGet($scope.backendId, $scope.backendType, path, 'open', function (error) { if (error) return Client.error(error); });
|
||||
}
|
||||
|
||||
$scope.busy = false;
|
||||
}
|
||||
|
||||
$scope.open = function (entry) {
|
||||
if (entry.isDirectory) openDirectory(sanitize($scope.cwd + '/' + entry.fileName));
|
||||
else if (entry.isFile) openFile(entry);
|
||||
};
|
||||
|
||||
$scope.goDirectoryUp = function () {
|
||||
openDirectory(sanitize($scope.cwd + '/..'));
|
||||
};
|
||||
|
||||
$scope.changeDirectory = function (path) {
|
||||
openDirectory(sanitize(path));
|
||||
};
|
||||
|
||||
$scope.onClearSelection = function ($event) {
|
||||
// we don't stop propagation if targets don't match we got the whole list click event
|
||||
if ($event.currentTarget !== $event.target) return;
|
||||
|
||||
$scope.selected = [];
|
||||
};
|
||||
|
||||
$scope.onMousedown = function ($event, entry) {
|
||||
if ($event.button === 2) {
|
||||
$scope.onMouseup($event, entry);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.onMouseup = function ($event, entry) {
|
||||
var i = $scope.selected.indexOf(entry);
|
||||
var multi = ($event.ctrlKey || $event.metaKey);
|
||||
var shift = $event.shiftKey;
|
||||
|
||||
if (shift) {
|
||||
if ($scope.selected.length === 0) {
|
||||
$scope.selected = [ entry ];
|
||||
} else {
|
||||
var pos = $scope.entries.indexOf(entry);
|
||||
var selectedPositions = $scope.selected.map(function (s) { return $scope.entries.indexOf(s); }).sort();
|
||||
|
||||
if (pos < selectedPositions[0]) {
|
||||
$scope.selected = $scope.entries.slice(pos, selectedPositions[0]+1);
|
||||
} else if (selectedPositions[1] && pos > selectedPositions[1]) {
|
||||
$scope.selected = $scope.entries.slice(selectedPositions[1], pos+1);
|
||||
} else {
|
||||
$scope.selected = $scope.entries.slice(selectedPositions[0], pos+1);
|
||||
}
|
||||
}
|
||||
} else if (multi) {
|
||||
if (i === -1) {
|
||||
$scope.selected.push(entry);
|
||||
} else if ($event.button === 0) { // only do this on left click
|
||||
$scope.selected.splice(i, 1);
|
||||
}
|
||||
} else {
|
||||
$scope.selected = [ entry ];
|
||||
}
|
||||
};
|
||||
|
||||
$scope.onEntryContextMenu = function ($event, entry) {
|
||||
if ($scope.selected.indexOf(entry) !== -1) return;
|
||||
$scope.selected.push(entry);
|
||||
};
|
||||
|
||||
$scope.actionSelectAll = function () {
|
||||
$scope.selected = $scope.entries.slice();
|
||||
};
|
||||
|
||||
// just events to the parent controller
|
||||
$scope.onUploadFile = function () { ctrl.onUploadFile({ cwd: $scope.cwd }); };
|
||||
$scope.onUploadFolder = function () { ctrl.onUploadFolder({ cwd: $scope.cwd }); };
|
||||
$scope.onNewFile = function () { ctrl.onNewFile({ cwd: $scope.cwd }); };
|
||||
$scope.onNewFolder = function () { ctrl.onNewFolder({ cwd: $scope.cwd }); };
|
||||
|
||||
$scope.mediaViewer = {
|
||||
type: '',
|
||||
src: '',
|
||||
entry: null,
|
||||
|
||||
show: function (entry) {
|
||||
var filePath = sanitize($scope.cwd + '/' + entry.fileName);
|
||||
|
||||
$scope.mediaViewer.entry = entry;
|
||||
$scope.mediaViewer.type = Mimer().get(entry.fileName).split('/')[0];
|
||||
$scope.mediaViewer.src = Client.filesGetLink($scope.backendId, $scope.backendType, filePath);
|
||||
|
||||
$('#mediaViewerModal-' + $scope.$id).modal('show');
|
||||
},
|
||||
|
||||
close: function () {
|
||||
// set an empty pixel image to bust the cached img to avoid flickering on slow load
|
||||
$scope.mediaViewer.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C/HgAGgwJ/lK3Q6wAAAABJRU5ErkJggg==';
|
||||
|
||||
$('#mediaViewerModal-' + $scope.$id).modal('hide');
|
||||
}
|
||||
};
|
||||
|
||||
$translate(['filemanager.list.menu.edit', 'filemanager.list.menu.cut', 'filemanager.list.menu.copy', 'filemanager.list.menu.paste', 'filemanager.list.menu.rename', 'filemanager.list.menu.chown', 'filemanager.list.menu.extract', 'filemanager.list.menu.download', 'filemanager.list.menu.delete' ]).then(function (tr) {
|
||||
$scope.menuOptions = [
|
||||
{
|
||||
text: tr['filemanager.list.menu.edit'],
|
||||
displayed: function ($itemScope, $event, entry) { return !entry.isDirectory && !entry.isSymbolicLink; },
|
||||
enabled: function () { return $scope.selected.length === 1; },
|
||||
hasBottomDivider: true,
|
||||
click: function ($itemScope, $event, entry) { $scope.open(entry); }
|
||||
}, {
|
||||
text: tr['filemanager.list.menu.cut'],
|
||||
click: function ($itemScope, $event, entry) { ctrl.onCutEntries({ cwd: $scope.cwd, entries: $scope.selected.slice() }); }
|
||||
}, {
|
||||
text: tr['filemanager.list.menu.copy'],
|
||||
click: function ($itemScope, $event, entry) { ctrl.onCopyEntries({ cwd: $scope.cwd, entries: $scope.selected.slice() }); }
|
||||
}, {
|
||||
text: tr['filemanager.list.menu.paste'],
|
||||
hasBottomDivider: true,
|
||||
enabled: function () { return ctrl.clipboard.length; },
|
||||
click: function ($itemScope, $event, entry) { ctrl.onPasteEntries({ cwd: $scope.cwd, entry: entry }); }
|
||||
}, {
|
||||
text: tr['filemanager.list.menu.rename'],
|
||||
enabled: function () { return $scope.selected.length === 1; },
|
||||
click: function ($itemScope, $event, entry) { ctrl.onRenameEntry({ cwd: $scope.cwd, entry: entry }); }
|
||||
}, {
|
||||
text: tr['filemanager.list.menu.chown'],
|
||||
click: function ($itemScope, $event, entry) { ctrl.onChownEntries({ cwd: $scope.cwd, entries: $scope.selected }); }
|
||||
}, {
|
||||
text: tr['filemanager.list.menu.extract'],
|
||||
displayed: function ($itemScope, $event, entry) { return !entry.isDirectory && isArchive(entry.fileName); },
|
||||
click: function ($itemScope, $event, entry) { ctrl.onExtractEntry({ cwd: $scope.cwd, entry: entry }); }
|
||||
}, {
|
||||
text: tr['filemanager.list.menu.download'],
|
||||
enabled: function () { return $scope.selected.length === 1; },
|
||||
click: function ($itemScope, $event, entry) { download(entry); }
|
||||
}, {
|
||||
text: tr['filemanager.list.menu.delete'],
|
||||
hasTopDivider: true,
|
||||
click: function ($itemScope, $event, entry) { ctrl.onDeleteEntries({ cwd: $scope.cwd, entries: $scope.selected }); }
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
$translate(['filemanager.toolbar.newFile', 'filemanager.toolbar.newFolder', 'filemanager.list.menu.paste', 'filemanager.list.menu.selectAll' ]).then(function (tr) {
|
||||
$scope.menuOptionsBlank = [
|
||||
{
|
||||
text: tr['filemanager.toolbar.newFile'],
|
||||
click: function ($itemScope, $event) { ctrl.onNewFile({ cwd: $scope.cwd }); }
|
||||
}, {
|
||||
text: tr['filemanager.toolbar.newFolder'],
|
||||
click: function ($itemScope, $event) { ctrl.onNewFolder({ cwd: $scope.cwd }); }
|
||||
}, {
|
||||
text: tr['filemanager.list.menu.paste'],
|
||||
hasTopDivider: true,
|
||||
hasBottomDivider: true,
|
||||
enabled: function () { return ctrl.clipboard.length; },
|
||||
click: function ($itemScope, $event) { ctrl.onPasteEntries({ cwd: $scope.cwd, entry: null }); }
|
||||
}, {
|
||||
text: tr['filemanager.list.menu.selectAll'],
|
||||
click: function ($itemScope, $event) { $scope.actionSelectAll(); }
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
function scrollInView(element) {
|
||||
if (!element) return;
|
||||
|
||||
// This assumes the DOM tree being that rigid
|
||||
function isVisible(ele) {
|
||||
var container = ele.parentElement.parentElement.parentElement;
|
||||
var eleTop = ele.offsetTop;
|
||||
var eleBottom = eleTop + ele.clientHeight;
|
||||
|
||||
var containerTop = container.scrollTop;
|
||||
var containerBottom = containerTop + container.clientHeight;
|
||||
|
||||
// The element is fully visible in the container
|
||||
return (
|
||||
(eleTop >= containerTop && eleBottom <= containerBottom) ||
|
||||
// Some part of the element is visible in the container
|
||||
(eleTop < containerTop && containerTop < eleBottom) ||
|
||||
(eleTop < containerBottom && containerBottom < eleBottom)
|
||||
);
|
||||
}
|
||||
|
||||
if (!isVisible(element)) element.scrollIntoView();
|
||||
}
|
||||
|
||||
function openSelected() {
|
||||
if (!$scope.selected.length) return;
|
||||
|
||||
$scope.open($scope.selected[0]);
|
||||
}
|
||||
|
||||
function selectNext() {
|
||||
var entries = sort();
|
||||
|
||||
if (!$scope.selected.length) return $scope.selected = [ entries[0] ];
|
||||
|
||||
var curIndex = $scope.entries.indexOf($scope.selected[0]);
|
||||
if (curIndex !== -1 && curIndex < $scope.entries.length-1) {
|
||||
var entry = entries[++curIndex];
|
||||
$scope.selected = [ entry ];
|
||||
scrollInView(document.querySelector('[entry-hashkey="' + entry['$$hashKey'] + '"]'));
|
||||
}
|
||||
}
|
||||
|
||||
function selectPrev() {
|
||||
var entries = sort();
|
||||
|
||||
if (!$scope.selected.length) return $scope.selected = [ entries.slice(-1) ];
|
||||
|
||||
var curIndex = $scope.entries.indexOf($scope.selected[0]);
|
||||
if (curIndex !== -1 && curIndex !== 0) {
|
||||
var entry = entries[--curIndex];
|
||||
$scope.selected = [ entry ];
|
||||
scrollInView(document.querySelector('[entry-hashkey="' + entry['$$hashKey'] + '"]'));
|
||||
}
|
||||
}
|
||||
|
||||
openDirectory('.');
|
||||
|
||||
$('.file-list').on('scroll', function (event) {
|
||||
if (event.target.scrollTop > 10) event.target.classList.add('top-scroll-indicator');
|
||||
else event.target.classList.remove('top-scroll-indicator');
|
||||
});
|
||||
|
||||
// handle shortcuts
|
||||
window.addEventListener('keydown', function (event) {
|
||||
if ($scope.$parent.activeView !== $scope.view || $scope.$parent.viewerOpen || isModalVisible()) return;
|
||||
|
||||
if (event.key === 'ArrowDown') {
|
||||
$scope.$apply(selectNext);
|
||||
} else if (event.key === 'ArrowUp') {
|
||||
$scope.$apply(selectPrev);
|
||||
} else if (event.key === 'Enter') {
|
||||
$scope.$apply(openSelected);
|
||||
} else if (event.key === 'Backspace') {
|
||||
if ($scope.view === 'fileTree') $scope.goDirectoryUp();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,133 +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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="16"
|
||||
height="16"
|
||||
id="svg2"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.91 r13725"
|
||||
version="1.0"
|
||||
sodipodi:docname="avatar-default-symbolic.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#e7e7e7"
|
||||
borderopacity="1"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="11.964497"
|
||||
inkscape:cx="6.5536056"
|
||||
inkscape:cy="-0.025360958"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:showpageshadow="false"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1030"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="25"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-global="true">
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="0,112"
|
||||
id="guide2383" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="78.156291,0"
|
||||
id="guide2389" />
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3672"
|
||||
visible="true"
|
||||
enabled="true" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="22.008699,4.1542523"
|
||||
id="guide2950" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="11.22532,22.008699"
|
||||
id="guide2952" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<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 />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Calque 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccsccccc"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
id="path3935"
|
||||
d="m -13.771529,5.9050966 c 0.181174,0.8569201 0.2823,1.5051186 0.135325,2.3620387 -1.145861,0.9506717 -4.076448,1.3778558 -4.072056,2.3620387 l -0.393673,2.558875 c 0,0.978388 2.731928,1.771529 6.101933,1.771529 3.370005,0 6.101933,-0.793141 6.101933,-1.771529 L -6.29174,10.629174 c -0.0047,-0.8423279 -2.952548,-1.377856 -4.084358,-2.3620387 -0.09668,-0.7953524 -0.01972,-1.5666863 0.147627,-2.3620387 l -3.543058,0 z" />
|
||||
<path
|
||||
transform="matrix(0.34209356,0,0,0.34209356,-8.638748,-12.26548)"
|
||||
d="m -9.75,73.09375 c -3.766412,0.121068 -7.468069,1.386362 -11.40625,3.25 a 1.25331,1.25331 0 0 0 -0.6875,1.4375 l 0.625,2.53125 a 1.25331,1.25331 0 0 0 0.78125,0.84375 c 0.161757,0.06256 0.275429,0.183794 0.71875,0.3125 2.335298,0.677989 5.907957,1.15625 9.90625,1.15625 3.9982931,0 7.5709518,-0.478261 9.90625,-1.15625 0.44332111,-0.128707 0.55699247,-0.24994 0.71875,-0.3125 a 1.25331,1.25331 0 0 0 0.78125,-0.8125 L 2.25,78.03125 a 1.25331,1.25331 0 0 0 -0.53125,-1.375 C -2.2051532,74.042333 -5.9835879,72.972682 -9.75,73.09375 z"
|
||||
id="path3937"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
inkscape:original="M -9.71875 74.34375 C -13.230599 74.456635 -16.76467 75.641953 -20.625 77.46875 L -20 80 C -19.731211 80.103955 -19.729288 80.147142 -19.375 80.25 C -17.218663 80.876033 -13.703662 81.375 -9.8125 81.375 C -5.9213382 81.375 -2.4063369 80.876033 -0.25 80.25 C 0.10428761 80.147142 0.10621054 80.103955 0.375 80 L 1.03125 77.6875 C -2.7172738 75.190412 -6.2069011 74.230865 -9.71875 74.34375 z "
|
||||
inkscape:radius="1.2531847"
|
||||
sodipodi:type="inkscape:offset" />
|
||||
<rect
|
||||
transform="matrix(0.9205234,-0.39068744,0.39068744,0.9205234,0,0)"
|
||||
ry="1.1810193"
|
||||
rx="1.1810193"
|
||||
y="-2.754653"
|
||||
x="-15.569602"
|
||||
height="2.1871843"
|
||||
width="1.0935922"
|
||||
id="rect3939"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
id="rect3941"
|
||||
width="1.0935922"
|
||||
height="2.1871843"
|
||||
x="6.5567312"
|
||||
y="6.6361833"
|
||||
rx="1.1810193"
|
||||
ry="1.1810193"
|
||||
transform="matrix(-0.9205234,-0.39068744,-0.39068744,0.9205234,0,0)" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccc"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
id="path3943"
|
||||
d="m -12,0 c -1.630647,0 -2.952548,1.2337743 -2.952548,2.7557118 0.01278,0.5632387 0.06085,1.232346 0.393673,2.7557117 0.196837,0.5905097 1.558851,2.1652021 1.574692,2.3620387 0.381733,0.1968365 1.771529,0.1968365 2.165203,0 0,-0.1968366 1.181019,-1.771529 1.377855,-2.3620387 C -9.066594,3.9281919 -9.06754,3.3462214 -9.047452,2.7557118 -9.047452,1.2337743 -10.369352,0 -12,0 z" />
|
||||
<path
|
||||
id="path3157"
|
||||
d="m 38,0 c -1.630647,0 -2.9375,1.2280625 -2.9375,2.75 0.0037,0.1620664 0.01579,0.3963239 0.03125,0.59375 -0.27885,0.118349 -0.299198,0.6610508 -0.0625,1.21875 0.09386,0.2211566 0.213411,0.3909677 0.34375,0.53125 0.03167,0.1567366 0.02336,0.2271022 0.0625,0.40625 0.196837,0.5905097 1.577909,2.1781634 1.59375,2.375 0.381733,0.1968365 1.762576,0.1968365 2.15625,0 0,-0.1968366 1.178164,-1.7844903 1.375,-2.375 C 40.60622,5.3151913 40.62213,5.1903792 40.65625,5.03125 40.764832,4.8997227 40.857512,4.7509639 40.9375,4.5625 41.162363,4.0326858 41.147829,3.5269131 40.90625,3.375 40.920493,3.1615298 40.931227,2.9343906 40.9375,2.75 40.9375,1.2280625 39.630648,0 38,0 z m -1.78125,8.40625 c -1.233461,0.8706787 -3.941711,1.2750309 -3.9375,2.21875 l -0.375,2.5625 c 0,0.519013 0.775005,0.988493 2,1.3125 l 0.1875,0.71875 A 0.42874928,0.42874928 0 0 0 34.375,15.5 c 0.05534,0.0214 0.09834,0.04972 0.25,0.09375 C 35.42389,15.825686 36.63221,16 38,16 39.36779,16 40.60736,15.825686 41.40625,15.59375 41.557907,15.54972 41.569664,15.5214 41.625,15.5 a 0.42874928,0.42874928 0 0 0 0.28125,-0.28125 L 42.125,14.5 c 1.208619,-0.323691 1.96875,-0.797472 1.96875,-1.3125 l -0.375,-2.5625 C 43.714419,9.848863 41.21753,9.3437322 39.9375,8.5 A 0.97584188,0.97584188 0 0 1 39.625,8.75 C 39.020006,9.0524961 38.608286,9 38.09375,9 37.836482,9 37.587947,9.0004922 37.34375,8.96875 37.099553,8.937008 36.902156,8.909026 36.59375,8.75 a 0.97584188,0.97584188 0 0 1 -0.375,-0.34375 z"
|
||||
style="fill:#bebebe;fill-opacity:1;stroke:none"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#bebebe;fill-opacity:1;stroke:none"
|
||||
d="m 8,0.7783785 c -2.256463,0 -4.0648649,1.699373 -4.0648649,3.8054054 0.00509,0.2242649 0.021845,0.5484266 0.043244,0.8216216 -0.3858679,0.1637694 -0.4140257,0.9147514 -0.086487,1.6864865 0.1298861,0.3060329 0.295314,0.5410147 0.4756757,0.7351351 0.043823,0.2168896 0.032325,0.3142604 0.086486,0.5621622 0.1511376,0.4534118 0.7470076,1.3420395 1.2972972,2.0756757 0.05396,0.563421 0.109936,1.132004 0,1.772973 -1.5856236,1.315524 -5.67094212,1.881347 -5.66486451,3.243243 L 0,16 16,16 15.91351,15.481081 c -0.0065,-1.1656 -4.098682,-1.881347 -5.664862,-3.243243 -0.06337,-0.521335 -0.07545,-1.043272 -0.04324,-1.556757 0.501434,-0.7738141 1.172201,-1.7868737 1.34054,-2.2918917 0.0605,-0.2557354 0.08252,-0.4284482 0.129729,-0.6486487 0.150255,-0.1820053 0.278504,-0.3878554 0.38919,-0.6486486 0.311162,-0.7331483 0.291049,-1.4330284 -0.04324,-1.6432432 0.01971,-0.2953966 0.03456,-0.6097082 0.04324,-0.8648649 0,-2.1060324 -1.8084,-3.8054054 -4.064865,-3.8054054 z"
|
||||
id="path3159"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 7.8 KiB |
@@ -1,40 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 568 400"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="background-image-placeholder.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
width="568"
|
||||
height="400"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.0029297"
|
||||
inkscape:cx="109.18014"
|
||||
inkscape:cy="337.01266"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1048"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
<!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) -->
|
||||
<path
|
||||
d="m 374.10635,275.08863 v 7.50887 c 0,12.44125 -10.08533,22.52659 -22.52659,22.52659 H 171.36704 c -12.44124,0 -22.52658,-10.08534 -22.52658,-22.52659 V 162.45567 c 0,-12.44124 10.08534,-22.52659 22.52658,-22.52659 h 7.50887 v 22.52659 h -4.69305 a 2.8158239,2.8158239 0 0 0 -2.81582,2.81583 v 114.51018 a 2.8158239,2.8158239 0 0 0 2.81582,2.81582 h 174.58109 a 2.8158239,2.8158239 0 0 0 2.81581,-2.81582 v -4.69305 z M 393.81713,117.4025 H 219.23606 a 2.8158239,2.8158239 0 0 0 -2.81582,2.81582 v 114.51017 a 2.8158239,2.8158239 0 0 0 2.81582,2.81582 h 174.58107 a 2.8158239,2.8158239 0 0 0 2.81581,-2.81582 V 120.21832 a 2.8158239,2.8158239 0 0 0 -2.81581,-2.81582 z m 2.81581,-22.52659 c 12.44126,0 22.5266,10.08534 22.5266,22.52659 v 120.14181 c 0,12.44125 -10.08535,22.5266 -22.5266,22.5266 h -180.2127 c -12.44125,0 -22.5266,-10.08535 -22.5266,-22.5266 V 117.4025 c 0,-12.44125 10.08535,-22.52659 22.5266,-22.52659 z m -123.89623,52.56204 c 0,10.3674 -8.40478,18.77216 -18.77216,18.77216 -10.3674,0 -18.77217,-8.40476 -18.77217,-18.77216 0,-10.3674 8.40477,-18.77216 18.77217,-18.77216 10.3674,0 18.77216,8.40476 18.77216,18.77216 z m -33.78989,45.05318 18.54454,-18.54455 c 2.19916,-2.19915 5.76494,-2.19915 7.96456,0 L 284,192.49113 l 48.58001,-48.58 c 2.19916,-2.19916 5.76493,-2.19916 7.96456,0 l 33.5618,33.56227 v 37.54432 H 238.94683 Z"
|
||||
id="path2"
|
||||
style="fill:#999999;stroke-width:0.469304" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.6 KiB |
@@ -1,890 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
require.config({ paths: { 'vs': '3rdparty/vs' }});
|
||||
|
||||
// create main application module
|
||||
var app = angular.module('Application', ['pascalprecht.translate', 'ngCookies', 'angular-md5', 'ui-notification', 'ngDrag', 'ui.bootstrap', 'ui.bootstrap.contextMenu']);
|
||||
|
||||
angular.module('Application').filter('prettyOwner', function () {
|
||||
return function (uid) {
|
||||
if (uid === 0) return 'root';
|
||||
if (uid === 33) return 'www-data';
|
||||
if (uid === 1000) return 'cloudron';
|
||||
if (uid === 1001) return 'git';
|
||||
|
||||
return uid;
|
||||
};
|
||||
});
|
||||
|
||||
// disable sce for footer https://code.angularjs.org/1.5.8/docs/api/ng/service/$sce
|
||||
app.config(function ($sceProvider) {
|
||||
$sceProvider.enabled(false);
|
||||
});
|
||||
|
||||
app.filter('trustUrl', ['$sce', function ($sce) {
|
||||
return function (recordingUrl) {
|
||||
return $sce.trustAsResourceUrl(recordingUrl);
|
||||
};
|
||||
}]);
|
||||
|
||||
// https://stackoverflow.com/questions/25621321/angularjs-ng-drag
|
||||
var ngDragEventDirectives = {};
|
||||
angular.forEach(
|
||||
'drag dragend dragenter dragexit dragleave dragover dragstart drop'.split(' '),
|
||||
function(eventName) {
|
||||
var directiveName = 'ng' + eventName.charAt(0).toUpperCase() + eventName.slice(1);
|
||||
|
||||
ngDragEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse/*, $rootScope */) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
compile: function($element, attr) {
|
||||
var fn = $parse(attr[directiveName], null, true);
|
||||
|
||||
return function ngDragEventHandler(scope, element) {
|
||||
element.on(eventName, function(event) {
|
||||
var callback = function() {
|
||||
fn(scope, {$event: event});
|
||||
};
|
||||
|
||||
scope.$apply(callback);
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
}];
|
||||
}
|
||||
);
|
||||
angular.module('ngDrag', []).directive(ngDragEventDirectives);
|
||||
|
||||
function sanitize(filePath) {
|
||||
filePath = filePath.split('/').filter(function (a) { return !!a; }).reduce(function (a, v) {
|
||||
if (v === '.'); // do nothing
|
||||
else if (v === '..') a.pop();
|
||||
else a.push(v);
|
||||
return a;
|
||||
}, []).map(function (p) {
|
||||
// small detour to safely handle special characters and whitespace
|
||||
return encodeURIComponent(decodeURIComponent(p));
|
||||
}).join('/');
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
function isModalVisible() {
|
||||
return !!document.getElementsByClassName('modal in').length;
|
||||
}
|
||||
|
||||
var VIEW = {
|
||||
LEFT: 'left',
|
||||
RIGHT: 'right'
|
||||
};
|
||||
|
||||
var OWNERS = [
|
||||
{ name: 'cloudron', value: 1000 },
|
||||
{ name: 'www-data', value: 33 },
|
||||
{ name: 'git', value: 1001 },
|
||||
{ name: 'root', value: 0 }
|
||||
];
|
||||
|
||||
app.controller('FileManagerController', ['$scope', '$translate', '$timeout', 'Client', function ($scope, $translate, $timeout, Client) {
|
||||
var search = decodeURIComponent(window.location.search).slice(1).split('&').map(function (item) { return item.split('='); }).reduce(function (o, k) { o[k[0]] = k[1]; return o; }, {});
|
||||
|
||||
// expose enums
|
||||
$scope.VIEW = VIEW;
|
||||
$scope.OWNERS = OWNERS;
|
||||
|
||||
$scope.initialized = false;
|
||||
$scope.status = null;
|
||||
$scope.client = Client;
|
||||
$scope.title = '';
|
||||
$scope.backendId = search.id;
|
||||
$scope.backendType = search.type;
|
||||
$scope.volumes = [];
|
||||
$scope.splitView = !!window.localStorage.splitView;
|
||||
$scope.activeView = VIEW.LEFT;
|
||||
$scope.viewerOpen = false;
|
||||
|
||||
$scope.clipboard = []; // holds cut or copied entries
|
||||
$scope.clipboardCut = false; // if action is cut or copy
|
||||
|
||||
|
||||
// add a hook for children to refresh both tree views
|
||||
|
||||
$scope.children = [];
|
||||
$scope.registerChild = function (child) { $scope.children.push(child); };
|
||||
$scope.refresh = function () {
|
||||
$scope.children.forEach(function (child) {
|
||||
child.onRefresh();
|
||||
});
|
||||
};
|
||||
|
||||
function collectFiles(entry, callback) {
|
||||
var pathFrom = entry.pathFrom;
|
||||
|
||||
if (entry.isDirectory) {
|
||||
Client.filesGet($scope.backendId, $scope.backendType, entry.fullFilePath, 'data', function (error, result) {
|
||||
if (error) return callback(error);
|
||||
if (!result.entries) return callback(new Error('not a folder'));
|
||||
|
||||
// amend fullFilePath
|
||||
result.entries.forEach(function (e) {
|
||||
e.fullFilePath = sanitize(entry.fullFilePath + '/' + e.fileName);
|
||||
e.pathFrom = pathFrom; // we stash the original path for pasting
|
||||
});
|
||||
|
||||
var collectedFiles = [];
|
||||
async.eachLimit(result.entries, 5, function (entry, callback) {
|
||||
collectFiles(entry, function (error, result) {
|
||||
if (error) return callback(error);
|
||||
|
||||
collectedFiles = collectedFiles.concat(result);
|
||||
|
||||
callback();
|
||||
});
|
||||
}, function (error) {
|
||||
if (error) return callback(error);
|
||||
|
||||
callback(null, collectedFiles);
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, [ entry ]);
|
||||
}
|
||||
|
||||
// entries need to be an actual copy
|
||||
$scope.actionCut = function (cwd, entries) {
|
||||
$scope.clipboard = entries; //$scope.selected.slice();
|
||||
$scope.clipboard.forEach(function (entry) {
|
||||
entry.fullFilePath = sanitize(cwd + '/' + entry.fileName);
|
||||
});
|
||||
$scope.clipboardCut = true;
|
||||
};
|
||||
|
||||
// entries need to be an actual copy
|
||||
$scope.actionCopy = function (cwd, entries) {
|
||||
$scope.clipboard = entries; //$scope.selected.slice();
|
||||
$scope.clipboard.forEach(function (entry) {
|
||||
entry.fullFilePath = sanitize(cwd + '/' + entry.fileName);
|
||||
entry.pathFrom = cwd; // we stash the original path for pasting
|
||||
});
|
||||
$scope.clipboardCut = false;
|
||||
};
|
||||
|
||||
$scope.actionPaste = function (cwd, destinationEntry) {
|
||||
if ($scope.clipboardCut) {
|
||||
// move files
|
||||
async.eachLimit($scope.clipboard, 5, function (entry, callback) {
|
||||
var newFilePath = sanitize(cwd + '/' + ((destinationEntry && destinationEntry.isDirectory) ? destinationEntry.fileName : '') + '/' + entry.fileName);
|
||||
|
||||
// TODO this will overwrite files in destination!
|
||||
Client.filesRename($scope.backendId, $scope.backendType, entry.fullFilePath, newFilePath, callback);
|
||||
}, function (error) {
|
||||
if (error) return Client.error(error);
|
||||
|
||||
// clear clipboard
|
||||
$scope.clipboard = [];
|
||||
|
||||
$scope.refresh();
|
||||
});
|
||||
} else {
|
||||
// copy files
|
||||
|
||||
// first collect all files recursively
|
||||
var collectedFiles = [];
|
||||
|
||||
async.eachLimit($scope.clipboard, 5, function (entry, callback) {
|
||||
collectFiles(entry, function (error, result) {
|
||||
if (error) return callback(error);
|
||||
|
||||
collectedFiles = collectedFiles.concat(result);
|
||||
|
||||
callback();
|
||||
});
|
||||
}, function (error) {
|
||||
if (error) return Client.error(error);
|
||||
|
||||
async.eachLimit(collectedFiles, 5, function (entry, callback) {
|
||||
var newFilePath = sanitize(cwd + '/' + ((destinationEntry && destinationEntry.isDirectory) ? destinationEntry.fileName : '') + '/' + entry.fullFilePath.slice(entry.pathFrom.length));
|
||||
|
||||
// This will NOT overwrite but finds a unique new name to copy to
|
||||
// we prefix with a / to ensure we don't do relative target paths
|
||||
Client.filesCopy($scope.backendId, $scope.backendType, entry.fullFilePath, '/' + newFilePath, callback);
|
||||
}, function (error) {
|
||||
if (error) return Client.error(error);
|
||||
|
||||
// clear clipboard
|
||||
$scope.clipboard = [];
|
||||
|
||||
$scope.refresh();
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// handle uploads
|
||||
|
||||
$scope.uploadStatus = {
|
||||
error: null,
|
||||
busy: false,
|
||||
fileName: '',
|
||||
count: 0,
|
||||
countDone: 0,
|
||||
size: 0,
|
||||
done: 0,
|
||||
percentDone: 0,
|
||||
files: [],
|
||||
targetFolder: ''
|
||||
};
|
||||
|
||||
$scope.uploadFiles = function (files, targetFolder, overwrite) {
|
||||
if (!files || !files.length) return;
|
||||
|
||||
overwrite = !!overwrite;
|
||||
|
||||
// prevent it from getting closed
|
||||
$('#uploadModal').modal({
|
||||
backdrop: 'static',
|
||||
keyboard: false
|
||||
});
|
||||
|
||||
$scope.uploadStatus.files = files;
|
||||
$scope.uploadStatus.targetFolder = targetFolder;
|
||||
$scope.uploadStatus.error = null;
|
||||
$scope.uploadStatus.busy = true;
|
||||
$scope.uploadStatus.count = files.length;
|
||||
$scope.uploadStatus.countDone = 0;
|
||||
$scope.uploadStatus.size = 0;
|
||||
$scope.uploadStatus.sizeDone = 0;
|
||||
$scope.uploadStatus.done = 0;
|
||||
$scope.uploadStatus.percentDone = 0;
|
||||
|
||||
for (var i = 0; i < files.length; ++i) {
|
||||
$scope.uploadStatus.size += files[i].size;
|
||||
}
|
||||
|
||||
async.eachSeries(files, function (file, callback) {
|
||||
var filePath = sanitize(targetFolder + '/' + (file.webkitRelativePath || file.name));
|
||||
|
||||
$scope.uploadStatus.fileName = file.name;
|
||||
|
||||
Client.filesUpload($scope.backendId, $scope.backendType, filePath, file, overwrite, function (loaded) {
|
||||
$scope.uploadStatus.percentDone = ($scope.uploadStatus.done+loaded) * 100 / $scope.uploadStatus.size;
|
||||
$scope.uploadStatus.sizeDone = loaded;
|
||||
}, function (error) {
|
||||
if (error) return callback(error);
|
||||
|
||||
$scope.uploadStatus.done += file.size;
|
||||
$scope.uploadStatus.percentDone = $scope.uploadStatus.done * 100 / $scope.uploadStatus.size;
|
||||
$scope.uploadStatus.countDone++;
|
||||
|
||||
callback();
|
||||
});
|
||||
}, function (error) {
|
||||
$scope.uploadStatus.busy = false;
|
||||
|
||||
if (error && error.statusCode === 409) {
|
||||
$scope.uploadStatus.error = 'exists';
|
||||
return;
|
||||
} else if (error) {
|
||||
console.error(error);
|
||||
$scope.uploadStatus.error = 'generic';
|
||||
return;
|
||||
}
|
||||
|
||||
$('#uploadModal').modal('hide');
|
||||
|
||||
$scope.uploadStatus.fileName = '';
|
||||
$scope.uploadStatus.count = 0;
|
||||
$scope.uploadStatus.size = 0;
|
||||
$scope.uploadStatus.sizeDone = 0;
|
||||
$scope.uploadStatus.done = 0;
|
||||
$scope.uploadStatus.percentDone = 100;
|
||||
$scope.uploadStatus.files = [];
|
||||
$scope.uploadStatus.targetFolder = '';
|
||||
|
||||
$scope.refresh();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.retryUpload = function (overwrite) {
|
||||
$scope.uploadFiles($scope.uploadStatus.files, $scope.uploadStatus.targetFolder, !!overwrite);
|
||||
};
|
||||
|
||||
|
||||
// file and folder upload hooks, stashing $scope.uploadCwd for now
|
||||
|
||||
$scope.uploadCwd = '';
|
||||
$('#uploadFileInput').on('change', function (e ) {
|
||||
$scope.uploadFiles(e.target.files || [], $scope.uploadCwd, false);
|
||||
});
|
||||
$scope.onUploadFile = function (cwd) {
|
||||
$scope.uploadCwd = cwd;
|
||||
$('#uploadFileInput').click();
|
||||
};
|
||||
|
||||
$('#uploadFolderInput').on('change', function (e ) {
|
||||
$scope.uploadFiles(e.target.files || [], $scope.uploadCwd, false);
|
||||
});
|
||||
$scope.onUploadFolder = function (cwd) {
|
||||
$scope.uploadCwd = cwd;
|
||||
$('#uploadFolderInput').click();
|
||||
};
|
||||
|
||||
|
||||
// handle delete
|
||||
|
||||
$scope.deleteEntries = {
|
||||
busy: false,
|
||||
error: null,
|
||||
cwd: '',
|
||||
entries: [],
|
||||
|
||||
show: function (cwd, entries) {
|
||||
$scope.deleteEntries.error = null;
|
||||
$scope.deleteEntries.cwd = cwd;
|
||||
$scope.deleteEntries.entries = entries;
|
||||
|
||||
$('#entriesDeleteModal').modal('show');
|
||||
},
|
||||
|
||||
submit: function () {
|
||||
$scope.deleteEntries.busy = true;
|
||||
|
||||
async.eachLimit($scope.deleteEntries.entries, 5, function (entry, callback) {
|
||||
var filePath = sanitize($scope.deleteEntries.cwd + '/' + entry.fileName);
|
||||
|
||||
Client.filesRemove($scope.backendId, $scope.backendType, filePath, callback);
|
||||
}, function (error) {
|
||||
$scope.deleteEntries.busy = false;
|
||||
if (error) return Client.error(error);
|
||||
|
||||
$scope.refresh();
|
||||
|
||||
$('#entriesDeleteModal').modal('hide');
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// rename entry
|
||||
|
||||
$scope.renameEntry = {
|
||||
busy: false,
|
||||
error: null,
|
||||
entry: null,
|
||||
cwd: '',
|
||||
newName: '',
|
||||
|
||||
show: function (cwd, entry) {
|
||||
$scope.renameEntry.error = null;
|
||||
$scope.renameEntry.cwd = cwd;
|
||||
$scope.renameEntry.entry = entry;
|
||||
$scope.renameEntry.newName = entry.fileName;
|
||||
$scope.renameEntry.busy = false;
|
||||
|
||||
$('#renameEntryModal').modal('show');
|
||||
},
|
||||
|
||||
submit: function () {
|
||||
$scope.renameEntry.busy = true;
|
||||
|
||||
var oldFilePath = sanitize($scope.renameEntry.cwd + '/' + $scope.renameEntry.entry.fileName);
|
||||
var newFilePath = sanitize(($scope.renameEntry.newName[0] === '/' ? '' : ($scope.renameEntry.cwd + '/')) + $scope.renameEntry.newName);
|
||||
|
||||
Client.filesRename($scope.backendId, $scope.backendType, oldFilePath, newFilePath, function (error) {
|
||||
$scope.renameEntry.busy = false;
|
||||
if (error) return Client.error(error);
|
||||
|
||||
$scope.refresh();
|
||||
|
||||
$('#renameEntryModal').modal('hide');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// chown entries
|
||||
|
||||
$scope.chownEntries = {
|
||||
busy: false,
|
||||
error: null,
|
||||
entries: [],
|
||||
newOwner: 0,
|
||||
recursive: false,
|
||||
showRecursiveOption: false,
|
||||
|
||||
show: function (cwd, entries) {
|
||||
$scope.chownEntries.error = null;
|
||||
$scope.chownEntries.cwd = cwd;
|
||||
$scope.chownEntries.entries = entries;
|
||||
// set default uid from first file
|
||||
$scope.chownEntries.newOwner = entries[0].uid;
|
||||
$scope.chownEntries.busy = false;
|
||||
|
||||
// default for directories is recursive
|
||||
$scope.chownEntries.recursive = !!entries.find(function (entry) { return entry.isDirectory; });
|
||||
$scope.chownEntries.showRecursiveOption = false;
|
||||
|
||||
$('#chownEntriesModal').modal('show');
|
||||
},
|
||||
|
||||
submit: function () {
|
||||
$scope.chownEntries.busy = true;
|
||||
|
||||
async.eachLimit($scope.chownEntries.entries, 5, function (entry, callback) {
|
||||
var filePath = sanitize($scope.chownEntries.cwd + '/' + entry.fileName);
|
||||
|
||||
Client.filesChown($scope.backendId, $scope.backendType, filePath, $scope.chownEntries.newOwner, $scope.chownEntries.recursive, callback);
|
||||
}, function (error) {
|
||||
$scope.chownEntries.busy = false;
|
||||
if (error) return Client.error(error);
|
||||
|
||||
$scope.refresh();
|
||||
|
||||
$('#chownEntriesModal').modal('hide');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// new file
|
||||
|
||||
$scope.newFile = {
|
||||
busy: false,
|
||||
error: null,
|
||||
cwd: '',
|
||||
name: '',
|
||||
|
||||
show: function (cwd) {
|
||||
$scope.newFile.error = null;
|
||||
$scope.newFile.name = '';
|
||||
$scope.newFile.busy = false;
|
||||
$scope.newFile.cwd = cwd;
|
||||
|
||||
$scope.newFileForm.$setUntouched();
|
||||
$scope.newFileForm.$setPristine();
|
||||
|
||||
$('#newFileModal').modal('show');
|
||||
},
|
||||
|
||||
submit: function () {
|
||||
$scope.newFile.busy = true;
|
||||
$scope.newFile.error = null;
|
||||
|
||||
var filePath = sanitize($scope.newFile.cwd + '/' + $scope.newFile.name);
|
||||
|
||||
Client.filesUpload($scope.backendId, $scope.backendType, filePath, new File([], $scope.newFile.name), false, function () {}, function (error) {
|
||||
$scope.newFile.busy = false;
|
||||
if (error && error.statusCode === 409) return $scope.newFile.error = 'exists';
|
||||
if (error) return Client.error(error);
|
||||
|
||||
$scope.refresh();
|
||||
|
||||
$('#newFileModal').modal('hide');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// new folder
|
||||
|
||||
$scope.newFolder = {
|
||||
busy: false,
|
||||
error: null,
|
||||
cwd: '',
|
||||
name: '',
|
||||
|
||||
show: function (cwd) {
|
||||
$scope.newFolder.error = null;
|
||||
$scope.newFolder.name = '';
|
||||
$scope.newFolder.busy = false;
|
||||
$scope.newFolder.cwd = cwd;
|
||||
|
||||
$scope.newFolderForm.$setUntouched();
|
||||
$scope.newFolderForm.$setPristine();
|
||||
|
||||
$('#newFolderModal').modal('show');
|
||||
},
|
||||
|
||||
submit: function () {
|
||||
$scope.newFolder.busy = true;
|
||||
$scope.newFolder.error = null;
|
||||
|
||||
var filePath = sanitize($scope.newFolder.cwd + '/' + $scope.newFolder.name);
|
||||
|
||||
Client.filesCreateDirectory($scope.backendId, $scope.backendType, filePath, function (error) {
|
||||
$scope.newFolder.busy = false;
|
||||
if (error && error.statusCode === 409) return $scope.newFolder.error = 'exists';
|
||||
if (error) return Client.error(error);
|
||||
|
||||
$scope.refresh();
|
||||
|
||||
$('#newFolderModal').modal('hide');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// extract archives
|
||||
|
||||
$scope.extractStatus = {
|
||||
error: null,
|
||||
busy: false,
|
||||
fileName: ''
|
||||
};
|
||||
|
||||
$scope.extractEntry = function (cwd, entry) {
|
||||
var filePath = sanitize(cwd + '/' + entry.fileName);
|
||||
|
||||
if (entry.isDirectory) return;
|
||||
|
||||
// prevent it from getting closed
|
||||
$('#extractModal').modal({
|
||||
backdrop: 'static',
|
||||
keyboard: false
|
||||
});
|
||||
|
||||
$scope.extractStatus.fileName = entry.fileName;
|
||||
$scope.extractStatus.error = null;
|
||||
$scope.extractStatus.busy = true;
|
||||
|
||||
Client.filesExtract($scope.backendId, $scope.backendType, filePath, function (error) {
|
||||
$scope.extractStatus.busy = false;
|
||||
|
||||
if (error) {
|
||||
console.error(error);
|
||||
$scope.extractStatus.error = $translate.instant('filemanager.extract.error', error.message);
|
||||
return;
|
||||
}
|
||||
|
||||
$('#extractModal').modal('hide');
|
||||
|
||||
$scope.refresh();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// split view handling
|
||||
|
||||
$scope.toggleSplitView = function () {
|
||||
$scope.splitView = !$scope.splitView;
|
||||
if (!$scope.splitView) {
|
||||
$scope.activeView = VIEW.LEFT;
|
||||
delete window.localStorage.splitView;
|
||||
} else {
|
||||
window.localStorage.splitView = true;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.setActiveView = function (view) {
|
||||
$scope.activeView = view;
|
||||
};
|
||||
|
||||
|
||||
// monaco text editor
|
||||
|
||||
var LANGUAGES = [];
|
||||
require(['vs/editor/editor.main'], function() { LANGUAGES = monaco.languages.getLanguages(); });
|
||||
|
||||
function getLanguage(filename) {
|
||||
var ext = '.' + filename.split('.').pop();
|
||||
var language = LANGUAGES.find(function (l) {
|
||||
if (!l.extensions) return false;
|
||||
return !!l.extensions.find(function (e) { return e === ext; });
|
||||
}) || '';
|
||||
return language ? language.id : '';
|
||||
}
|
||||
|
||||
$scope.textEditor = {
|
||||
busy: false,
|
||||
cwd: null,
|
||||
entry: null,
|
||||
editor: null,
|
||||
unsaved: false,
|
||||
visible: false,
|
||||
|
||||
show: function (cwd, entry) {
|
||||
$scope.textEditor.cwd = cwd;
|
||||
$scope.textEditor.entry = entry;
|
||||
$scope.textEditor.busy = false;
|
||||
$scope.textEditor.unsaved = false;
|
||||
$scope.textEditor.visible = true;
|
||||
|
||||
// clear model if any
|
||||
if ($scope.textEditor.editor && $scope.textEditor.editor.getModel()) $scope.textEditor.editor.setModel(null);
|
||||
|
||||
$scope.viewerOpen = true;
|
||||
// document.getElementById('textEditorModal').style['display'] = 'flex';
|
||||
|
||||
var filePath = sanitize($scope.textEditor.cwd + '/' + entry.fileName);
|
||||
var language = getLanguage(entry.fileName);
|
||||
|
||||
Client.filesGet($scope.backendId, $scope.backendType, filePath, 'data', function (error, result) {
|
||||
if (error) return Client.error(error);
|
||||
|
||||
if (!$scope.textEditor.editor) {
|
||||
$timeout(function () {
|
||||
$scope.textEditor.editor = monaco.editor.create(document.getElementById('textEditorContainer'), {
|
||||
value: result,
|
||||
language: language,
|
||||
theme: 'vs-dark'
|
||||
});
|
||||
$scope.textEditor.editor.getModel().onDidChangeContent(function () { $scope.textEditor.unsaved = true; });
|
||||
}, 200);
|
||||
} else {
|
||||
$scope.textEditor.editor.setModel(monaco.editor.createModel(result, language));
|
||||
$scope.textEditor.editor.getModel().onDidChangeContent(function () { $scope.textEditor.unsaved = true; }); // have to re-attach whenever model changes
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
save: function (callback) {
|
||||
$scope.textEditor.busy = true;
|
||||
|
||||
var newContent = $scope.textEditor.editor.getValue();
|
||||
var filePath = sanitize($scope.textEditor.cwd + '/' + $scope.textEditor.entry.fileName);
|
||||
var file = new File([newContent], 'file');
|
||||
|
||||
Client.filesUpload($scope.backendId, $scope.backendType, filePath, file, true, function () {}, function (error) {
|
||||
if (error) return Client.error(error);
|
||||
|
||||
$scope.refresh();
|
||||
|
||||
$timeout(function () {
|
||||
$scope.textEditor.unsaved = false;
|
||||
$scope.textEditor.busy = false;
|
||||
if (typeof callback === 'function') return callback();
|
||||
}, 1000);
|
||||
});
|
||||
},
|
||||
|
||||
close: function () {
|
||||
$scope.textEditor.visible = false;
|
||||
$scope.viewerOpen = false;
|
||||
$('#textEditorCloseModal').modal('hide');
|
||||
},
|
||||
|
||||
onClose: function () {
|
||||
$scope.textEditor.visible = false;
|
||||
$scope.viewerOpen = false;
|
||||
$('#textEditorCloseModal').modal('hide');
|
||||
},
|
||||
|
||||
saveAndClose: function () {
|
||||
$scope.textEditor.save(function () {
|
||||
$scope.textEditor.onClose();
|
||||
});
|
||||
},
|
||||
|
||||
maybeClose: function () {
|
||||
if (!$scope.textEditor.unsaved) return $scope.textEditor.onClose();
|
||||
$('#textEditorCloseModal').modal('show');
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
// restart app or mail logic
|
||||
|
||||
$scope.restartBusy = false;
|
||||
|
||||
$scope.onRestartApp = function () {
|
||||
$scope.restartBusy = true;
|
||||
|
||||
function waitUntilRestarted(callback) {
|
||||
Client.getApp($scope.backendId, function (error, result) {
|
||||
if (error) return callback(error);
|
||||
|
||||
if (result.installationState === ISTATES.INSTALLED) return callback();
|
||||
setTimeout(waitUntilRestarted.bind(null, callback), 2000);
|
||||
});
|
||||
}
|
||||
|
||||
Client.restartApp($scope.backendId, function (error) {
|
||||
if (error) console.error('Failed to restart app.', error);
|
||||
|
||||
waitUntilRestarted(function (error) {
|
||||
if (error) console.error('Failed wait for restart.', error);
|
||||
|
||||
$scope.restartBusy = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.onRestartMail = function () {
|
||||
$scope.restartBusy = true;
|
||||
|
||||
function waitUntilRestarted(callback) {
|
||||
Client.getService('mail', function (error, result) {
|
||||
if (error) return callback(error);
|
||||
|
||||
if (result.status === 'active') return callback();
|
||||
setTimeout(waitUntilRestarted.bind(null, callback), 2000);
|
||||
});
|
||||
}
|
||||
|
||||
Client.restartService('mail', function (error) {
|
||||
if (error) console.error('Failed to restart mail.', error);
|
||||
|
||||
waitUntilRestarted(function (error) {
|
||||
if (error) console.error('Failed wait for restart.', error);
|
||||
|
||||
$scope.restartBusy = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// init code
|
||||
|
||||
function fetchVolumesInfo(mounts) {
|
||||
$scope.volumes = [];
|
||||
|
||||
async.each(mounts, function (mount, callback) {
|
||||
Client.getVolume(mount.volumeId, function (error, result) {
|
||||
if (error) return callback(error);
|
||||
|
||||
$scope.volumes.push(result);
|
||||
|
||||
callback();
|
||||
});
|
||||
}, function (error) {
|
||||
if (error) console.error('Failed to fetch volumes info.', error);
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
|
||||
Client.getStatus(function (error, status) {
|
||||
if (error) return Client.initError(error, init);
|
||||
|
||||
if (!status.activated) {
|
||||
console.log('Not activated yet, redirecting', status);
|
||||
window.location.href = '/';
|
||||
return;
|
||||
}
|
||||
|
||||
// check version and force reload if needed
|
||||
if (!localStorage.version) {
|
||||
localStorage.version = status.version;
|
||||
} else if (localStorage.version !== status.version) {
|
||||
localStorage.version = status.version;
|
||||
window.location.reload(true);
|
||||
}
|
||||
|
||||
$scope.status = status;
|
||||
|
||||
console.log('Running filemanager version ', localStorage.version);
|
||||
|
||||
// get user profile as the first thing. this populates the "scope" and affects subsequent API calls
|
||||
Client.refreshUserInfo(function (error) {
|
||||
if (error) return Client.initError(error, init);
|
||||
|
||||
var getter;
|
||||
if ($scope.backendType === 'app') {
|
||||
getter = Client.getApp.bind(Client, $scope.backendId);
|
||||
} else if ($scope.backendType === 'volume') {
|
||||
getter = Client.getVolume.bind(Client, $scope.backendId);
|
||||
} else if ($scope.backendType === 'mail') {
|
||||
getter = function (next) { next(null, null); };
|
||||
}
|
||||
|
||||
getter(function (error, result) {
|
||||
if (error) {
|
||||
$scope.initialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// fine to do async
|
||||
if ($scope.backendType === 'app') fetchVolumesInfo(result.mounts || []);
|
||||
|
||||
switch ($scope.backendType) {
|
||||
case 'app':
|
||||
$scope.title = result.label || result.fqdn;
|
||||
$scope.rootDirLabel = '/app/data/';
|
||||
$scope.applicationLink = 'https://' + result.fqdn;
|
||||
break;
|
||||
case 'volume':
|
||||
$scope.title = result.name;
|
||||
$scope.rootDirLabel = result.hostPath;
|
||||
break;
|
||||
case 'mail':
|
||||
$scope.title = 'mail';
|
||||
$scope.rootDirLabel = 'mail';
|
||||
break;
|
||||
}
|
||||
|
||||
window.document.title = $scope.title + ' - ' + $translate.instant('filemanager.title');
|
||||
|
||||
// now mark the Client to be ready
|
||||
Client.setReady();
|
||||
|
||||
// openPath('');
|
||||
|
||||
$scope.initialized = true;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
|
||||
// toplevel key input handling
|
||||
|
||||
window.addEventListener('keydown', function (event) {
|
||||
if((navigator.platform.match('Mac') ? event.metaKey : event.ctrlKey) && event.key === 's') {
|
||||
if (!$scope.textEditor.visible) return;
|
||||
|
||||
event.preventDefault();
|
||||
$scope.$apply($scope.textEditor.save);
|
||||
} else if((navigator.platform.match('Mac') ? event.metaKey : event.ctrlKey) && event.key === 'c') {
|
||||
if ($scope.textEditor.visible) return;
|
||||
if ($scope.selected.length === 0) return;
|
||||
if (isModalVisible()) return;
|
||||
|
||||
event.preventDefault();
|
||||
$scope.$apply($scope.actionCopy);
|
||||
} else if((navigator.platform.match('Mac') ? event.metaKey : event.ctrlKey) && event.key === 'x') {
|
||||
if ($scope.textEditor.visible) return;
|
||||
if ($scope.selected.length === 0) return;
|
||||
if (isModalVisible()) return;
|
||||
|
||||
event.preventDefault();
|
||||
$scope.$apply($scope.actionCut);
|
||||
} else if((navigator.platform.match('Mac') ? event.metaKey : event.ctrlKey) && event.key === 'v') {
|
||||
if ($scope.textEditor.visible) return;
|
||||
if ($scope.clipboard.length === 0) return;
|
||||
if (isModalVisible()) return;
|
||||
|
||||
event.preventDefault();
|
||||
$scope.$apply($scope.actionPaste);
|
||||
} else if((navigator.platform.match('Mac') ? event.metaKey : event.ctrlKey) && event.key === 'a') {
|
||||
if ($scope.textEditor.visible) return;
|
||||
if (isModalVisible()) return;
|
||||
|
||||
event.preventDefault();
|
||||
$scope.$apply($scope.actionSelectAll);
|
||||
} else if(event.key === 'Escape') {
|
||||
if ($scope.textEditor.visible) return $scope.$apply($scope.textEditor.maybeClose);
|
||||
else $scope.$apply(function () { $scope.selected = []; });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// setup all the dialog focus handling
|
||||
['newFileModal', 'newFolderModal', 'renameEntryModal'].forEach(function (id) {
|
||||
$('#' + id).on('shown.bs.modal', function () {
|
||||
$(this).find('[autofocus]:first').focus();
|
||||
});
|
||||
});
|
||||
|
||||
// selects filename (without extension)
|
||||
['renameEntryModal'].forEach(function (id) {
|
||||
$('#' + id).on('shown.bs.modal', function () {
|
||||
var elem = $(this).find('[autofocus]:first');
|
||||
var text = elem.val();
|
||||
elem[0].setSelectionRange(0, text.indexOf('.'));
|
||||
});
|
||||
});
|
||||
}]);
|
||||
@@ -1,40 +0,0 @@
|
||||
/* This file contains helpers which should not be part of client.js */
|
||||
|
||||
angular.module('Application').directive('passwordReveal', function () {
|
||||
var svgEye = '<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="eye" class="svg-inline--fa fa-eye fa-w-18" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M572.52 241.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400a144 144 0 1 1 144-144 143.93 143.93 0 0 1-144 144zm0-240a95.31 95.31 0 0 0-25.31 3.79 47.85 47.85 0 0 1-66.9 66.9A95.78 95.78 0 1 0 288 160z"></path></svg>';
|
||||
var svgEyeSlash = '<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="eye-slash" class="svg-inline--fa fa-eye-slash fa-w-20" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M320 400c-75.85 0-137.25-58.71-142.9-133.11L72.2 185.82c-13.79 17.3-26.48 35.59-36.72 55.59a32.35 32.35 0 0 0 0 29.19C89.71 376.41 197.07 448 320 448c26.91 0 52.87-4 77.89-10.46L346 397.39a144.13 144.13 0 0 1-26 2.61zm313.82 58.1l-110.55-85.44a331.25 331.25 0 0 0 81.25-102.07 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64a308.15 308.15 0 0 0-147.32 37.7L45.46 3.37A16 16 0 0 0 23 6.18L3.37 31.45A16 16 0 0 0 6.18 53.9l588.36 454.73a16 16 0 0 0 22.46-2.81l19.64-25.27a16 16 0 0 0-2.82-22.45zm-183.72-142l-39.3-30.38A94.75 94.75 0 0 0 416 256a94.76 94.76 0 0 0-121.31-92.21A47.65 47.65 0 0 1 304 192a46.64 46.64 0 0 1-1.54 10l-73.61-56.89A142.31 142.31 0 0 1 320 112a143.92 143.92 0 0 1 144 144c0 21.63-5.29 41.79-13.9 60.11z"></path></svg>';
|
||||
|
||||
return {
|
||||
link: function (scope, elements) {
|
||||
var element = elements[0];
|
||||
|
||||
if (!element.parentNode) {
|
||||
console.error('Wrong password-reveal directive usage. Element has no parent.');
|
||||
return;
|
||||
}
|
||||
|
||||
var eye = document.createElement('i');
|
||||
eye.innerHTML = svgEyeSlash;
|
||||
eye.style.width = '18px';
|
||||
eye.style.height = '18px';
|
||||
eye.style.position = 'relative';
|
||||
eye.style.float = 'right';
|
||||
eye.style.marginTop = '-24px';
|
||||
eye.style.marginRight = '10px';
|
||||
eye.style.cursor = 'pointer';
|
||||
|
||||
eye.addEventListener('click', function () {
|
||||
if (element.type === 'password') {
|
||||
element.type = 'text';
|
||||
eye.innerHTML = svgEye;
|
||||
} else {
|
||||
element.type = 'password';
|
||||
eye.innerHTML = svgEyeSlash;
|
||||
}
|
||||
});
|
||||
|
||||
element.parentNode.style.position = 'relative';
|
||||
element.parentNode.insertBefore(eye, element.nextSibling);
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,120 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height" />
|
||||
|
||||
<title> Login to <%= title %> </title>
|
||||
|
||||
<link href="<%= icon %>" rel="icon" type="image/png">
|
||||
|
||||
<!-- Theme CSS -->
|
||||
<link type="text/css" rel="stylesheet" href="<%= dashboardOrigin %>/theme.css">
|
||||
|
||||
<!-- Fontawesome -->
|
||||
<link type="text/css" rel="stylesheet" href="<%= dashboardOrigin %>/3rdparty/fontawesome/css/all.css"/>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="layout-root">
|
||||
|
||||
<div class="layout-content">
|
||||
<div class="card" style="padding: 20px; margin-top: 100px; max-width: 620px;">
|
||||
<div class="row">
|
||||
<div class="col-md-12" style="text-align: center;">
|
||||
<img width="128" height="128" style="margin-top: -84px" src="<%= icon %>"/>
|
||||
<br/>
|
||||
<h1><small>{{ login.loginTo }}</small> <%= title %></h1>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h4 class="has-error" id="message"></h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<form name="loginForm" onsubmit="return onLogin(event)">
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="inputUsername">{{ login.username }}</label>
|
||||
<input type="text" class="form-control" id="inputUsername" name="username" autofocus required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="inputPassword">{{ login.password }}</label>
|
||||
<input type="password" class="form-control" name="password" id="inputPassword" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="inputTotpToken">{{ login.2faToken }}</label>
|
||||
<input type="text" class="form-control" name="totpToken" id="inputTotpToken" value="">
|
||||
</div>
|
||||
<button class="btn btn-primary btn-outline pull-right" type="submit" id="login">{{ login.signInAction }}</button>
|
||||
</form>
|
||||
<!-- <a ng-href="" class="hand" ng-click="showPasswordReset()">Reset password</a> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
function onLogin(event) {
|
||||
event.preventDefault();
|
||||
|
||||
var username = document.getElementById('inputUsername').value;
|
||||
var password = document.getElementById('inputPassword').value;
|
||||
var totpToken = document.getElementById('inputTotpToken').value;
|
||||
|
||||
fetch('/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
redirect: 'manual',
|
||||
body: JSON.stringify({ username: username, password: password, totpToken: totpToken })
|
||||
}).then(function (response) {
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
document.getElementById('message').innerText = "{{ login.errorIncorrectCredentials }}"; // FIXME this needs proper escaping for translated strings, single quotes break easily!
|
||||
return;
|
||||
}
|
||||
|
||||
var search = decodeURIComponent(window.location.search).slice(1).split('&').map(function (item) { return item.split('='); }).reduce(function (o, k) { o[k[0]] = k[1]; return o; }, {});
|
||||
window.location.href = search.redirect || '/';
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// patch up for password reveal see dashboard/js/utils.js
|
||||
var element = document.getElementById('inputPassword');
|
||||
|
||||
var svgEye = '<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="eye" class="svg-inline--fa fa-eye fa-w-18" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M572.52 241.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400a144 144 0 1 1 144-144 143.93 143.93 0 0 1-144 144zm0-240a95.31 95.31 0 0 0-25.31 3.79 47.85 47.85 0 0 1-66.9 66.9A95.78 95.78 0 1 0 288 160z"></path></svg>';
|
||||
var svgEyeSlash = '<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="eye-slash" class="svg-inline--fa fa-eye-slash fa-w-20" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M320 400c-75.85 0-137.25-58.71-142.9-133.11L72.2 185.82c-13.79 17.3-26.48 35.59-36.72 55.59a32.35 32.35 0 0 0 0 29.19C89.71 376.41 197.07 448 320 448c26.91 0 52.87-4 77.89-10.46L346 397.39a144.13 144.13 0 0 1-26 2.61zm313.82 58.1l-110.55-85.44a331.25 331.25 0 0 0 81.25-102.07 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64a308.15 308.15 0 0 0-147.32 37.7L45.46 3.37A16 16 0 0 0 23 6.18L3.37 31.45A16 16 0 0 0 6.18 53.9l588.36 454.73a16 16 0 0 0 22.46-2.81l19.64-25.27a16 16 0 0 0-2.82-22.45zm-183.72-142l-39.3-30.38A94.75 94.75 0 0 0 416 256a94.76 94.76 0 0 0-121.31-92.21A47.65 47.65 0 0 1 304 192a46.64 46.64 0 0 1-1.54 10l-73.61-56.89A142.31 142.31 0 0 1 320 112a143.92 143.92 0 0 1 144 144c0 21.63-5.29 41.79-13.9 60.11z"></path></svg>';
|
||||
|
||||
var eye = document.createElement('i');
|
||||
eye.innerHTML = svgEyeSlash;
|
||||
eye.style.width = '18px';
|
||||
eye.style.height = '18px';
|
||||
eye.style.position = 'relative';
|
||||
eye.style.float = 'right';
|
||||
eye.style.marginTop = '-24px';
|
||||
eye.style.marginRight = '10px';
|
||||
eye.style.cursor = 'pointer';
|
||||
|
||||
eye.addEventListener('click', function () {
|
||||
if (element.type === 'password') {
|
||||
element.type = 'text';
|
||||
eye.innerHTML = svgEye;
|
||||
} else {
|
||||
element.type = 'password';
|
||||
eye.innerHTML = svgEyeSlash;
|
||||
}
|
||||
});
|
||||
|
||||
element.parentNode.style.position = 'relative';
|
||||
element.parentNode.insertBefore(eye, element.nextSibling);
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,156 +0,0 @@
|
||||
{
|
||||
"apps": {
|
||||
"title": "As Minhas Aplicações",
|
||||
"noApps": {
|
||||
"description": "Que tal instalar algumas? Vê a <a href=\"{{ appStoreLink }}\">Loja de Aplicações</a>",
|
||||
"title": "Sem aplicações instaladas!"
|
||||
},
|
||||
"groupsFilterHeader": "Selecionar Grupo",
|
||||
"addApplinkAction": "Adicionar Applink",
|
||||
"noAccess": {
|
||||
"title": "Não tem acesso a nenhuma aplicação.",
|
||||
"description": "Assim que tiver, elas vão aparecer aqui."
|
||||
},
|
||||
"configActionTooltip": "Configurar",
|
||||
"logsActionTooltip": "Eventos",
|
||||
"infoActionTooltip": "Informação",
|
||||
"adminPageActionTooltip": "Página de Adminstração",
|
||||
"searchPlaceholder": "Pesquisar Aplicações",
|
||||
"stateFilterHeader": "Todos os Estados",
|
||||
"tagsFilterHeader": "Etiquetas: {{ tags }}",
|
||||
"tagsFilterHeaderAll": "Todas as Etiquetas",
|
||||
"domainsFilterHeader": "Todos os Domínios",
|
||||
"auth": {
|
||||
"sso": "Entrar com as credenciais Cloudron",
|
||||
"nosso": "Entrar com conta dedicada",
|
||||
"email": "Entrar com endereço de email"
|
||||
},
|
||||
"addAppAction": "Adicionar Aplicação",
|
||||
"addAppproxyAction": "Adicionar Appproxy",
|
||||
"filter": {
|
||||
"clearAll": "Limpar Tudo"
|
||||
}
|
||||
},
|
||||
"main": {
|
||||
"displayName": "Nome de Apresentação",
|
||||
"rebootDialog": {
|
||||
"warning": "Reiniciar o servidor irá causar que todas as aplicações instaladas neste Cloudron fiquem indisponíveis temporariamente!",
|
||||
"description": "Utilize isto para aplicar atualizações de segurança ou se experienciar comportamento inesperado. Todas as aplicações e serviços em execução neste Cloudron vão iniciar automaticamente quando o reinício estiver completo.",
|
||||
"title": "Realmente reiniciar o servidor?",
|
||||
"rebootAction": "Reiniciar agora"
|
||||
},
|
||||
"offline": "O Cloudron está offline. A ligar novamente…",
|
||||
"dialog": {
|
||||
"cancel": "Cancelar",
|
||||
"save": "Guardar",
|
||||
"close": "Fechar",
|
||||
"no": "Não",
|
||||
"yes": "Sim"
|
||||
},
|
||||
"logout": "Terminar Sessão",
|
||||
"username": "Nome de Utilizador",
|
||||
"actions": "Ações",
|
||||
"table": {
|
||||
"date": "Data"
|
||||
},
|
||||
"pagination": {
|
||||
"next": "seguinte",
|
||||
"prev": "anterior",
|
||||
"perPageSelector": "Mostrar {{ n }} por página"
|
||||
},
|
||||
"action": {
|
||||
"reboot": "Reiniciar",
|
||||
"logs": "Eventos"
|
||||
},
|
||||
"clipboard": {
|
||||
"copied": "Copiado para a área de transferência",
|
||||
"clickToCopy": "Clique para copiar",
|
||||
"clickToCopyBackupId": "Clique para copiar o ID da cópia de segurança"
|
||||
},
|
||||
"searchPlaceholder": "Pesquisar",
|
||||
"multiselect": {
|
||||
"selected": "{{ n }} selecionados",
|
||||
"select": "Selecionar",
|
||||
"filterPlaceholder": "Escreva para filtrar opções"
|
||||
},
|
||||
"prettyDate": {
|
||||
"justNow": "agora mesmo",
|
||||
"yeserday": "Ontem",
|
||||
"minutesAgo": "{{ m }} minutos atrás",
|
||||
"hoursAgo": "{{ h }} horas atrás"
|
||||
},
|
||||
"navbar": {
|
||||
"users": "Utilizadores"
|
||||
},
|
||||
"disableAction": "Desativar",
|
||||
"enableAction": "Ativar",
|
||||
"statusEnabled": "Ativado",
|
||||
"statusDisabled": "Desativado"
|
||||
},
|
||||
"appstore": {
|
||||
"category": {
|
||||
"analytics": "Estatísticas",
|
||||
"game": "Jogos",
|
||||
"project": "Gestão de Projetos",
|
||||
"all": "Tudo",
|
||||
"popular": "Popular",
|
||||
"newApps": "Novas Aplicações",
|
||||
"chat": "Chat",
|
||||
"blog": "Blog",
|
||||
"document": "Documentos",
|
||||
"crm": "CRM",
|
||||
"forum": "Fórum",
|
||||
"gallery": "Galeria",
|
||||
"finance": "Finanças",
|
||||
"git": "Alojamento de Código",
|
||||
"email": "Email",
|
||||
"hosting": "Alojamento Web",
|
||||
"media": "Multimédia",
|
||||
"learning": "Aprendizagem",
|
||||
"notes": "Notas",
|
||||
"sync": "Sincronização de Ficheiros",
|
||||
"wiki": "Wiki",
|
||||
"vpn": "VPN",
|
||||
"federated": "Federados"
|
||||
},
|
||||
"installDialog": {
|
||||
"lastUpdated": "Última atualização a {{ date }}",
|
||||
"locationPlaceholder": "Deixe em branco para usar o domínio de raiz",
|
||||
"userManagementNone": "Esta aplicação tem a sua própria gestão de utilizadores. Esta definição determina se a aplicação está ou não visível no painel do utilizador.",
|
||||
"memoryRequirement": "Requere pelo menos {{ size }} de memória",
|
||||
"location": "Localização",
|
||||
"manualWarning": "Adicione um registo A manualmente para <b>{{ location }}</b> apontando para o endereço IP público deste Cloudron",
|
||||
"userManagement": "Gestão de utilizadores",
|
||||
"userManagementMailbox": "Todos os utilizadores com uma caixa de correio neste Cloudron têm acesso.",
|
||||
"userManagementLeaveToApp": "Deixar a gestão de utilizadores para a aplicação",
|
||||
"userManagementAllUsers": "Permitir todos os utilizadores deste Cloudron",
|
||||
"installAnywayAction": "Instalar na mesma",
|
||||
"doInstallAction": "Instalar {{ dnsOverwrite ? 'e sobrescrever DNS' : '' }}",
|
||||
"userManagementSelectUsers": "Apenas permitir os seguintes utilizadores e grupos",
|
||||
"errorUserManagementSelectAtLeastOne": "Selecione pelo menos um utilizador ou grupo",
|
||||
"users": "Utilizadores",
|
||||
"groups": "Grupos",
|
||||
"configuredForCloudronEmail": "Esta aplicação está pré-configurada para ser utilizada com o <a href=\"{{ emailDocsLink }}\" target=\"_blank\">Email do Cloudron</a>.",
|
||||
"lowOnResources": "Este Cloudron está baixo em recursos.",
|
||||
"pleaseUpgradeServer": "Por favor, atualize para um servidor com mais memória. Em alternativa, liberte recursos removendo aplicações que não utiliza.",
|
||||
"subscriptionRequired": "Para instalar mais aplicação, uma subscrição paga é necessária.",
|
||||
"setupSubscriptionAction": "Configurar Subscrição",
|
||||
"installAction": "Instalar",
|
||||
"cloudflarePortWarning": "O proxy do Cloudflare deve estar desativado para o domínio da aplicação para que possa aceder a esta porta",
|
||||
"titleAndVersion": "Esta aplicação inclui {{ title }} {{ version }}"
|
||||
},
|
||||
"title": "Loja de Aplicações",
|
||||
"searchPlaceholder": "Pesquise por alternativas como Github, Dropbox, Slack, Trello, …",
|
||||
"noAppsFound": "Nenhuma aplicação encontrada.",
|
||||
"appMissing": "Falta uma aplicação? Contacte-nos.",
|
||||
"unstable": "Instável",
|
||||
"appNotFoundDialog": {
|
||||
"description": "Não existe nenhuma aplicação <b>{{ appId }}</b> com a versão <b>{{ version }}</b>.",
|
||||
"title": "Aplicação não encontrada"
|
||||
},
|
||||
"accountDialog": {
|
||||
"titleSignUp": "Registar com Cloudron.io",
|
||||
"titleLogin": "Entrar com Cloudron.io"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
<!-- Modal postinstall confirm -->
|
||||
<div class="modal fade" id="appPostInstallConfirmModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<img ng-src="{{appPostInstallConfirm.app.iconUrl}}" onerror="this.onerror=null;this.src='img/appicon_fallback.png'" class="app-info-icon"/>
|
||||
<h5 class="app-info-title">
|
||||
{{ appPostInstallConfirm.app.manifest.title }}
|
||||
<span class="app-info-meta text-small">{{ 'app.appInfo.package' | tr }} <a ng-href="/#/appstore/{{appPostInstallConfirm.app.manifest.id}}?version={{appPostInstallConfirm.app.manifest.version}}">v{{ appPostInstallConfirm.app.manifest.version }}</a> </span>
|
||||
<br/>
|
||||
<span ng-show="appPostInstallConfirm.app.manifest.documentationUrl"><a target="_blank" ng-href="{{appPostInstallConfirm.app.manifest.documentationUrl}}">{{ 'app.docsAction' | tr }}</a> </span>
|
||||
<br/>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p ng-show="appPostInstallConfirm.app.manifest.addons.email">{{ 'app.appInfo.ssoEmail' | tr }}</p>
|
||||
<p ng-show="appPostInstallConfirm.app.sso && !appPostInstallConfirm.app.manifest.addons.email">{{ 'app.appInfo.sso' | tr }}</p>
|
||||
|
||||
<div ng-bind-html="appPostInstallConfirm.app.manifest.postInstallMessage | markdown2html"></div>
|
||||
<div ng-show="appPostInstallConfirm.app.manifest.documentationUrl" ng-bind-html="'app.appInfo.appDocsUrl' | tr:{ docsUrl: appPostInstallConfirm.app.manifest.documentationUrl, title: appPostInstallConfirm.app.manifest.title, forumUrl: (appPostInstallConfirm.app.manifest.forumUrl || 'https://forum.cloudron.io') }"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="form-group pull-left">
|
||||
<input type="checkbox" id="appPostInstallConfirmCheckbox" ng-model="appPostInstallConfirm.confirmed">
|
||||
<label class="control-label" for="appPostInstallConfirmCheckbox">{{ 'app.appInfo.postInstallConfirmCheckbox' | tr }}</label>
|
||||
</div>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
|
||||
<a class="btn btn-success" ng-href="{{ appPostInstallConfirm.confirmed ? ('https://' + appPostInstallConfirm.app.fqdn) : '' }}" target="_blank" ng-disabled="!appPostInstallConfirm.confirmed" ng-click="appPostInstallConfirm.submit()">{{ 'app.appInfo.openAction' | tr:{ app: appPostInstallConfirm.app.manifest.title } }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal applinks edit -->
|
||||
<div class="modal fade" id="applinksEditModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'app.editApplinkDialog.title' | tr }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form name="applinksEditForm" role="form" ng-submit="applinksEdit.submit()" autocomplete="off">
|
||||
<div class="form-group" ng-class="{ 'has-error': (applinksEditForm.upstreamUri.$dirty && applinksEditForm.upstreamUri.$invalid) || (!applinksEditForm.upstreamUri.$dirty && applinksEdit.error.upstreamUri) }">
|
||||
<label class="control-label">{{ 'app.applinks.upstreamUri' | tr }}</label>
|
||||
<input type="text" class="form-control" ng-model="applinksEdit.upstreamUri" name="upstreamUri" id="inputUpstreamUri" autofocus autocomplete="off" required>
|
||||
<span class="text-danger" ng-show="applinksEdit.error.upstreamUri">{{ applinksEdit.error.upstreamUri }}</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label">{{ 'app.applinks.label' | tr }}</label>
|
||||
<input type="text" class="form-control" ng-model="applinksEdit.label" name="label" id="inputLabel" autocomplete="off">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<label class="control-label">{{ 'app.display.icon' | tr }}</label>
|
||||
</div>
|
||||
<div id="previewIcon" class="app-custom-icon" ng-click="applinksEdit.showCustomIconSelector()">
|
||||
<img ng-src="{{ applinksEdit.iconUrl() || 'img/appicon_fallback.png' }}" fallback-icon="img/appicon_fallback.png" onerror="imageErrorHandler(this)"/>
|
||||
<div class="overlay"></div>
|
||||
</div>
|
||||
<a href="" style="font-weight: normal;" ng-click="applinksEdit.resetCustomIcon()">{{ 'app.applinks.clearIconAction' | tr }}</a> - <span class="text-small">{{ 'app.applinks.clearIconDescription' | tr }}</span>
|
||||
<input type="file" id="applinksEditIconFileInput" style="display: none" accept="image/png"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label">{{ 'app.display.tags' | tr }}</label>
|
||||
<tag-input class="form-control" placeholder="{{ 'app.display.tagsPlaceholder' | tr }}" taglist="applinksEdit.tags" name="tags" uib-tooltip="{{ 'app.display.tagsTooltip' | tr }}"></tag-input>
|
||||
</div>
|
||||
|
||||
<label class="control-label">{{ 'app.accessControl.userManagement.dashboardVisibility' | tr }} <sup><a ng-href="https://docs.cloudron.io/apps/#dashboard-visibility" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
|
||||
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" ng-model="applinksEdit.accessRestrictionOption" value="any">
|
||||
<span>{{ 'app.accessControl.userManagement.visibleForAllUsers' | tr }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" ng-model="applinksEdit.accessRestrictionOption" value="groups">
|
||||
<span>{{ 'app.accessControl.userManagement.visibleForSelected' | tr }}</span>
|
||||
<span class="label label-danger" ng-show="applinksEdit.accessRestrictionOption === 'groups' && !applinksEdit.isAccessRestrictionValid()">{{ 'appstore.installDialog.errorUserManagementSelectAtLeastOne' | tr }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<div style="margin-left: 20px; display: flex;">
|
||||
<div>
|
||||
{{ 'appstore.installDialog.users' | tr }}: <multiselect name="accessUsersSelect" class="input-sm stretch" ng-model="applinksEdit.accessRestriction.users" ng-disabled="applinksEdit.accessRestrictionOption !== 'groups'" options="(user.username || user.email) for user in allUsers" data-multiple="true" filter-after-rows="5" scroll-after-rows="10"></multiselect>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{ 'appstore.installDialog.groups' | tr }}: <multiselect name="accessGroupsSelect" class="input-sm stretch" ng-model="applinksEdit.accessRestriction.groups" ng-disabled="applinksEdit.accessRestrictionOption !== 'groups'" options="group.name for group in allGroups" data-multiple="true" filter-after-rows="5" scroll-after-rows="10"></multiselect>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input class="ng-hide" type="submit" ng-disabled="applinksEditForm.$invalid || applinksEdit.busyEdit || applinks.busyRemove"/>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger pull-left" ng-click="applinksEdit.remove()" ng-disabled="applinksEdit.busyRemove || applinksEdit.busyEdit"><i class="fa fa-circle-notch fa-spin" ng-show="applinksEdit.busyRemove"></i> {{ 'app.editApplinkDialog.deleteAction' | tr }}</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
|
||||
<button type="button" class="btn btn-success" ng-click="applinksEdit.submit()" ng-disabled="applinksEditForm.$invalid || applinksEdit.busyRemove || applinksEdit.busyEdit"><i class="fa fa-circle-notch fa-spin" ng-show="applinksEdit.busyEdit"></i> {{ 'main.dialog.save' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content content-large">
|
||||
|
||||
<!-- Workaround for select-all issue, see commit message -->
|
||||
<div style="font-size: 1px;"> </div>
|
||||
|
||||
<div class="animateMeOpacity ng-hide" ng-show="installedApps.length === 0 && user.isAtLeastAdmin">
|
||||
<div class="col-md-12" style="text-align: center;">
|
||||
<br/><br/><br/><br/>
|
||||
<h1><i class="fa fa-cloud-download fa-fw"></i> {{ 'apps.noApps.title' | tr }}</h1>
|
||||
<br/></br>
|
||||
<h3 ng-bind-html="'apps.noApps.description' | tr:{ appStoreLink: '#/appstore' }"></h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="animateMeOpacity ng-hide" ng-show="installedApps.length === 0 && !user.isAtLeastAdmin">
|
||||
<div class="col-md-12" style="text-align: center;">
|
||||
<br/><br/><br/><br/>
|
||||
<h1>{{ 'apps.noAccess.title' | tr }}</h1>
|
||||
<br/></br>
|
||||
<h3>{{ 'apps.noAccess.description' | tr }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 class="view-header" ng-show="installedApps.length > 0">
|
||||
{{ 'apps.title' | tr }}
|
||||
<div class="view-header-search-bar">
|
||||
<form class="form-inline">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" style="width: 300px" placeholder="{{ 'apps.searchPlaceholder' | tr }}" id="appSearch" ng-model="appSearch"/>
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-default" type="button" ng-class="{ 'active': showFilter, 'btn-warning': showFilter || selectedTags.length || selectedState.state || !selectedGroup._unset || !selectedDomain._alldomains }" ng-click="showFilter = !showFilter"><i class="fas fa-filter"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div ng-show="showFilter" class="view-header-filter-bar">
|
||||
<form class="form-inline">
|
||||
<multiselect ng-model="selectedGroup" ng-show="user.isAtLeastAdmin && groups.length > 1" ms-header="{{ selectedGroup.name }}" options="group.name for group in groups" data-multiple="false" filter-after-rows="5" scroll-after-rows="10"></multiselect>
|
||||
<multiselect ng-model="selectedState" ng-show="user.isAtLeastAdmin" ms-header="{{ 'apps.stateFilterHeader' | tr }}" ms-selected="{{ selectedState }}" options="state.label for state in states" data-multiple="false"></multiselect>
|
||||
<multiselect ng-model="selectedTags" ng-show="user.isAtLeastAdmin && tags.length > 0" ms-header="{{ 'apps.tagsFilterHeaderAll' | tr }}" ms-selected="{{ 'apps.tagsFilterHeader' | tr:{ tags: selectedTags.join(', ') } }}" options="tag for tag in tags" data-multiple="true" filter-after-rows="5" scroll-after-rows="10"></multiselect>
|
||||
<multiselect ng-model="selectedDomain" data-compare-by="domain" ms-selected="{{ selectedDomain.domain }}" options="domain.domain for domain in filterDomains" data-multiple="false" filter-after-rows="5" scroll-after-rows="10"></multiselect>
|
||||
<button class="btn btn-warning" ng-disabled="!selectedTags.length && !selectedState.state && selectedGroup._unset && selectedDomain._alldomains" ng-click="clearAllFilter()">{{ 'apps.filter.clearAll' | tr }}</button>
|
||||
</form>
|
||||
</div>
|
||||
</h1>
|
||||
|
||||
<div class="animateMeOpacity ng-hide" ng-show="installedApps.length > 0">
|
||||
<div class="app-grid">
|
||||
<div class="grid-item" ng-class="{ 'stopped': app.runState === 'stopped' }" ng-repeat="app in installedApps | selectedGroupAccessFilter:selectedGroup | selectedStateFilter:selectedState | selectedTagFilter:selectedTags | selectedDomainFilter:selectedDomain | appSearchFilter:appSearch | orderBy:'fqdn'">
|
||||
<div class="grid-item-content" uib-tooltip="{{ app.fqdn }}">
|
||||
<a ng-show="app.type !== APP_TYPES.LINK && isOperator(app)" ng-href="#/app/{{ app.id}}/display" class="btn btn-lg btn-default grid-item-action"><i class="fas fa-cog"></i></a>
|
||||
<div ng-show="app.type === APP_TYPES.LINK && isOperator(app)" ng-click="applinksEdit.show(app)" class="btn btn-lg btn-default grid-item-action"><i class="fas fa-cog"></i></div>
|
||||
<a ng-href="{{ app | applicationLink }}" ng-click="onAppClick(app, $event)" target="_blank">
|
||||
<div class="grid-item-top">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 text-center" style="padding-left: 5px; padding-right: 5px;">
|
||||
<img ng-src="{{ app.iconUrl || 'img/appicon_fallback.png' }}" fallback-icon="img/appicon_fallback.png" onerror="imageErrorHandler(this)" class="app-icon"/>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 text-center">
|
||||
<div class="grid-item-top-title" data-fittext>{{ app.label || app.subdomain || app.fqdn }}</div>
|
||||
<div class="text-muted status" style="text-overflow: ellipsis; white-space: nowrap; overflow: hidden" uib-tooltip="{{ app | appProgressMessage }}">
|
||||
{{ app | installationStateLabel }}
|
||||
</div>
|
||||
<div class="status" ng-style="{ 'visibility': isOperator(app) && (app | installationActive) ? 'visible' : 'hidden' }">
|
||||
<div class="progress progress-striped active">
|
||||
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{ app.progress }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="usermanagement-indicator" ng-show="app.type !== APP_TYPES.LINK">
|
||||
<i class="fas fa-user" ng-show="app.ssoAuth && !app.manifest.addons.email" uib-tooltip="{{ 'apps.auth.sso' | tr }}" tooltip-placement="right"></i>
|
||||
<i class="far fa-user" ng-show="!app.ssoAuth && !app.manifest.addons.email" uib-tooltip="{{ 'apps.auth.nosso' | tr }}" tooltip-placement="right"></i>
|
||||
<i class="fas fa-envelope" ng-show="app.manifest.addons.email" uib-tooltip="{{ 'apps.auth.email' | tr }}" tooltip-placement="right"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</a>
|
||||
|
||||
<!-- we check the version here because the box updater does not know when an app gets updated -->
|
||||
<div class="app-update-badge" ng-click="showAppConfigure(app, 'updates')" ng-show="config.update[app.id].manifest.version && config.update[app.id].manifest.version !== app.manifest.version && (app | installSuccess) && !(app.error || app.runState === 'stopped')">
|
||||
<i class="fa fa-arrow-up fa-inverse"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,316 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/* global angular:false */
|
||||
/* global $:false */
|
||||
/* global APP_TYPES */
|
||||
/* global onAppClick */
|
||||
|
||||
angular.module('Application').controller('AppsController', ['$scope', '$translate', '$interval', '$location', 'Client', function ($scope, $translate, $interval, $location, Client) {
|
||||
var ALL_DOMAINS_DOMAIN = { _alldomains: true, domain: 'All Domains' }; // dummy record for the single select filter
|
||||
var GROUP_ACCESS_UNSET = { _unset: true, name: 'Select Group' }; // dummy record for the single select filter
|
||||
|
||||
$scope.installedApps = Client.getInstalledApps();
|
||||
$scope.tags = Client.getAppTags();
|
||||
$scope.states = [
|
||||
{ state: '', label: 'All States' },
|
||||
{ state: 'running', label: 'Running' },
|
||||
{ state: 'stopped', label: 'Stopped' },
|
||||
{ state: 'update_available', label: 'Update Available' },
|
||||
{ state: 'not_responding', label: 'Not Responding' }
|
||||
];
|
||||
$scope.selectedState = $scope.states[0];
|
||||
$scope.selectedTags = [];
|
||||
$scope.selectedGroup = GROUP_ACCESS_UNSET;
|
||||
$scope.selectedDomain = ALL_DOMAINS_DOMAIN;
|
||||
$scope.filterDomains = [ ALL_DOMAINS_DOMAIN ];
|
||||
$scope.config = Client.getConfig();
|
||||
$scope.user = Client.getUserInfo();
|
||||
$scope.domains = [];
|
||||
$scope.appSearch = '';
|
||||
$scope.groups = [ GROUP_ACCESS_UNSET ];
|
||||
$scope.APP_TYPES = APP_TYPES;
|
||||
$scope.showFilter = false;
|
||||
$scope.filterActive = false;
|
||||
|
||||
$scope.allUsers = [];
|
||||
$scope.allGroups = [];
|
||||
|
||||
$translate(['apps.stateFilterHeader', 'apps.domainsFilterHeader', 'apps.groupsFilterHeader', 'app.states.running', 'app.states.stopped', 'app.states.notResponding', 'app.states.updateAvailable']).then(function (tr) {
|
||||
if (tr['apps.domainsFilterHeader']) ALL_DOMAINS_DOMAIN.domain = tr['apps.domainsFilterHeader'];
|
||||
if (tr['apps.groupsFilterHeader']) GROUP_ACCESS_UNSET.name = tr['apps.groupsFilterHeader'];
|
||||
if (tr['apps.stateFilterHeader']) $scope.states[0].label = tr['apps.stateFilterHeader'];
|
||||
if (tr['app.states.running']) $scope.states[1].label = tr['app.states.running'];
|
||||
if (tr['app.states.stopped']) $scope.states[2].label = tr['app.states.stopped'];
|
||||
if (tr['app.states.notResponding']) $scope.states[4].label = tr['app.states.notResponding'];
|
||||
if (tr['app.states.updateAvailable']) $scope.states[3].label = tr['app.states.updateAvailable'];
|
||||
});
|
||||
|
||||
$scope.$watch('selectedTags', function (newVal, oldVal) {
|
||||
if (newVal === oldVal) return;
|
||||
|
||||
localStorage.selectedTags = newVal.join(',');
|
||||
});
|
||||
|
||||
$scope.$watch('selectedState', function (newVal, oldVal) {
|
||||
if (newVal === oldVal) return;
|
||||
|
||||
if (newVal === $scope.states[0]) localStorage.removeItem('selectedState');
|
||||
else localStorage.selectedState = newVal.state;
|
||||
});
|
||||
|
||||
$scope.$watch('selectedGroup', function (newVal, oldVal) {
|
||||
if (newVal === oldVal) return;
|
||||
|
||||
if (newVal === GROUP_ACCESS_UNSET) localStorage.removeItem('selectedGroup');
|
||||
else localStorage.selectedGroup = newVal.id;
|
||||
});
|
||||
|
||||
$scope.$watch('selectedDomain', function (newVal, oldVal) {
|
||||
if (newVal === oldVal) return;
|
||||
|
||||
if (newVal._alldomains) localStorage.removeItem('selectedDomain');
|
||||
else localStorage.selectedDomain = newVal.domain;
|
||||
});
|
||||
|
||||
$scope.onAppClick = function (app, $event) { onAppClick(app, $event, $scope.isOperator(app), $scope); };
|
||||
|
||||
$scope.clearAllFilter = function () {
|
||||
$scope.selectedState = $scope.states[0];
|
||||
$scope.selectedTags = [];
|
||||
$scope.selectedGroup = GROUP_ACCESS_UNSET;
|
||||
$scope.selectedDomain = ALL_DOMAINS_DOMAIN;
|
||||
};
|
||||
|
||||
$scope.appPostInstallConfirm = {
|
||||
app: {},
|
||||
message: '',
|
||||
confirmed: false,
|
||||
|
||||
show: function (app) {
|
||||
$scope.appPostInstallConfirm.app = app;
|
||||
$scope.appPostInstallConfirm.message = app.manifest.postInstallMessage;
|
||||
$scope.appPostInstallConfirm.confirmed = false;
|
||||
|
||||
$('#appPostInstallConfirmModal').modal('show');
|
||||
|
||||
return false; // prevent propagation and default
|
||||
},
|
||||
|
||||
submit: function () {
|
||||
if (!$scope.appPostInstallConfirm.confirmed) return;
|
||||
|
||||
$scope.appPostInstallConfirm.app.pendingPostInstallConfirmation = false;
|
||||
delete localStorage['confirmPostInstall_' + $scope.appPostInstallConfirm.app.id];
|
||||
|
||||
$('#appPostInstallConfirmModal').modal('hide');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.applinksEdit = {
|
||||
error: {},
|
||||
busyEdit: false,
|
||||
busyRemove: false,
|
||||
applink: {},
|
||||
id: '',
|
||||
upstreamUri: '',
|
||||
label: '',
|
||||
tags: '',
|
||||
accessRestrictionOption: '',
|
||||
accessRestriction: { users: [], groups: [] },
|
||||
icon: { data: null },
|
||||
|
||||
iconUrl: function () {
|
||||
if ($scope.applinksEdit.icon.data === '__original__') { // user clicked reset
|
||||
// https://png-pixel.com/ white pixel placeholder
|
||||
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=';
|
||||
} else if ($scope.applinksEdit.icon.data) { // user uploaded icon
|
||||
return $scope.applinksEdit.icon.data;
|
||||
} else { // current icon
|
||||
return $scope.applinksEdit.applink.iconUrl;
|
||||
}
|
||||
},
|
||||
|
||||
resetCustomIcon: function () {
|
||||
$scope.applinksEdit.icon.data = '__original__';
|
||||
},
|
||||
|
||||
showCustomIconSelector: function () {
|
||||
$('#applinksEditIconFileInput').click();
|
||||
},
|
||||
|
||||
isAccessRestrictionValid: function () {
|
||||
return !!($scope.applinksEdit.accessRestriction.users.length || $scope.applinksEdit.accessRestriction.groups.length);
|
||||
},
|
||||
|
||||
show: function (applink) {
|
||||
$scope.applinksEdit.error = {};
|
||||
$scope.applinksEdit.busyEdit = false;
|
||||
$scope.applinksEdit.busyRemove = false;
|
||||
$scope.applinksEdit.applink = applink;
|
||||
$scope.applinksEdit.id = applink.id;
|
||||
$scope.applinksEdit.upstreamUri = applink.upstreamUri;
|
||||
$scope.applinksEdit.label = applink.label;
|
||||
$scope.applinksEdit.accessRestrictionOption = applink.accessRestriction ? 'groups' : 'any';
|
||||
$scope.applinksEdit.accessRestriction = { users: [], groups: [] };
|
||||
$scope.applinksEdit.icon = { data: null };
|
||||
|
||||
var userSet, groupSet;
|
||||
if (applink.accessRestriction) {
|
||||
userSet = {};
|
||||
applink.accessRestriction.users.forEach(function (uid) { userSet[uid] = true; });
|
||||
$scope.allUsers.forEach(function (u) { if (userSet[u.id] === true) $scope.applinksEdit.accessRestriction.users.push(u); });
|
||||
|
||||
groupSet = {};
|
||||
if (applink.accessRestriction.groups) applink.accessRestriction.groups.forEach(function (gid) { groupSet[gid] = true; });
|
||||
$scope.allGroups.forEach(function (g) { if (groupSet[g.id] === true) $scope.applinksEdit.accessRestriction.groups.push(g); });
|
||||
}
|
||||
|
||||
// translate for tag-input
|
||||
$scope.applinksEdit.tags = applink.tags ? applink.tags.join(' ') : '';
|
||||
|
||||
$scope.applinksEditForm.$setUntouched();
|
||||
$scope.applinksEditForm.$setPristine();
|
||||
|
||||
$('#applinksEditModal').modal('show');
|
||||
|
||||
return false; // prevent propagation and default
|
||||
},
|
||||
|
||||
submit: function () {
|
||||
$scope.applinksEdit.busyEdit = true;
|
||||
$scope.applinksEdit.error = {};
|
||||
|
||||
var accessRestriction = null;
|
||||
if ($scope.applinksEdit.accessRestrictionOption === 'groups') {
|
||||
accessRestriction = { users: [], groups: [] };
|
||||
accessRestriction.users = $scope.applinksEdit.accessRestriction.users.map(function (u) { return u.id; });
|
||||
accessRestriction.groups = $scope.applinksEdit.accessRestriction.groups.map(function (g) { return g.id; });
|
||||
}
|
||||
|
||||
var icon;
|
||||
if ($scope.applinksEdit.icon.data === '__original__') { // user reset the icon
|
||||
icon = '';
|
||||
} else if ($scope.applinksEdit.icon.data) { // user loaded custom icon
|
||||
icon = $scope.applinksEdit.icon.data.replace(/^data:image\/[a-z]+;base64,/, '');
|
||||
}
|
||||
|
||||
var data = {
|
||||
upstreamUri: $scope.applinksEdit.upstreamUri,
|
||||
label: $scope.applinksEdit.label,
|
||||
accessRestriction: accessRestriction,
|
||||
icon: icon,
|
||||
tags: $scope.applinksEdit.tags.split(' ').map(function (t) { return t.trim(); }).filter(function (t) { return !!t; })
|
||||
};
|
||||
|
||||
Client.updateApplink($scope.applinksEdit.id, data, function (error) {
|
||||
$scope.applinksEdit.busyEdit = false;
|
||||
|
||||
if (error && error.statusCode === 400 && error.message.includes('upstreamUri')) {
|
||||
$scope.applinksEdit.error.upstreamUri = error.message;
|
||||
$scope.applinksEditForm.$setUntouched();
|
||||
$scope.applinksEditForm.$setPristine();
|
||||
return;
|
||||
}
|
||||
if (error) return console.error('Failed to update applink', error);
|
||||
|
||||
Client.refreshInstalledApps();
|
||||
|
||||
$('#applinksEditModal').modal('hide');
|
||||
});
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
$scope.applinksEdit.busyRemove = true;
|
||||
|
||||
Client.removeApplink($scope.applinksEdit.id, function (error) {
|
||||
$scope.applinksEdit.busyRemove = false;
|
||||
|
||||
if (error) return console.error('Failed to remove applink', error);
|
||||
|
||||
Client.refreshInstalledApps();
|
||||
|
||||
$('#applinksEditModal').modal('hide');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.showAppConfigure = function (app, view) {
|
||||
$location.path('/app/' + app.id + '/' + view);
|
||||
};
|
||||
|
||||
$scope.isOperator = function (app) {
|
||||
return app.accessLevel === 'operator' || app.accessLevel === 'admin';
|
||||
};
|
||||
|
||||
Client.onReady(function () {
|
||||
setTimeout(function () { $('#appSearch').focus(); }, 1);
|
||||
|
||||
// refresh the new list immediately when switching from another view (appstore)
|
||||
Client.refreshInstalledApps(function () {
|
||||
var refreshAppsTimer = $interval(Client.refreshInstalledApps.bind(Client, function () {}), 5000);
|
||||
$scope.$on('$destroy', function () {
|
||||
$interval.cancel(refreshAppsTimer);
|
||||
});
|
||||
});
|
||||
|
||||
if (!$scope.user.isAtLeastAdmin) return;
|
||||
|
||||
// load local settings and apply tag filter
|
||||
if (localStorage.selectedTags) {
|
||||
if (!$scope.tags.length) localStorage.removeItem('selectedTags');
|
||||
else $scope.selectedTags = localStorage.selectedTags.split(',');
|
||||
}
|
||||
|
||||
if (localStorage.selectedState) $scope.selectedState = $scope.states.find(function (s) { return s.state === localStorage.selectedState; }) || $scope.states[0];
|
||||
|
||||
Client.getGroups(function (error, result) {
|
||||
if (error) Client.error(error);
|
||||
|
||||
$scope.groups = [ GROUP_ACCESS_UNSET ].concat(result);
|
||||
$scope.allGroups = result;
|
||||
|
||||
if (localStorage.selectedGroup) $scope.selectedGroup = $scope.groups.find(function (g) { return g.id === localStorage.selectedGroup; }) || GROUP_ACCESS_UNSET;
|
||||
});
|
||||
|
||||
Client.getDomains(function (error, result) {
|
||||
if (error) Client.error(error);
|
||||
|
||||
$scope.domains = result;
|
||||
$scope.filterDomains = [ALL_DOMAINS_DOMAIN].concat(result);
|
||||
|
||||
if (localStorage.selectedDomain) $scope.selectedDomain = $scope.filterDomains.find(function (d) { return d.domain === localStorage.selectedDomain; }) || ALL_DOMAINS_DOMAIN;
|
||||
});
|
||||
|
||||
Client.getAllUsers(function (error, users) {
|
||||
if (error) Client.error(error);
|
||||
|
||||
$scope.allUsers = users;
|
||||
});
|
||||
});
|
||||
|
||||
$('#applinksEditIconFileInput').get(0).onchange = function (event) {
|
||||
var fr = new FileReader();
|
||||
fr.onload = function () {
|
||||
$scope.$apply(function () {
|
||||
// var file = event.target.files[0];
|
||||
$scope.applinksEdit.icon.data = fr.result;
|
||||
});
|
||||
};
|
||||
fr.readAsDataURL(event.target.files[0]);
|
||||
};
|
||||
|
||||
// setup all the dialog focus handling
|
||||
['applinksAddModal', 'applinksEditModal'].forEach(function (id) {
|
||||
$('#' + id).on('shown.bs.modal', function () {
|
||||
$(this).find("[autofocus]:first").focus();
|
||||
});
|
||||
});
|
||||
|
||||
$('.collapse').on('shown.bs.collapse', function(){
|
||||
$(this).parent().find('.fa-angle-right').removeClass('fa-angle-right').addClass('fa-angle-down');
|
||||
}).on('hidden.bs.collapse', function(){
|
||||
$(this).parent().find('.fa-angle-down').removeClass('fa-angle-down').addClass('fa-angle-right');
|
||||
});
|
||||
|
||||
$('.modal-backdrop').remove();
|
||||
}]);
|
||||
@@ -1,788 +0,0 @@
|
||||
<!-- Modal subscription -->
|
||||
<div class="modal fade" id="subscriptionRequiredModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'email.subscriptionDialog.title' | tr }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>{{ 'email.subscriptionDialog.description' | tr }}</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
|
||||
<button type="button" class="btn btn-success" ng-click="openSubscriptionSetup()">{{ 'email.subscriptionDialog.setupAction' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal enable email -->
|
||||
<div class="modal fade" id="enableEmailModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'email.enableEmailDialog.title' | tr:{ domain: domain.domain } }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p ng-bind-html="'email.enableEmailDialog.description' | tr:{ domain: domain.domain, requiredPortsDocsLink: 'https://docs.cloudron.io/email/#required-ports' }"></p>
|
||||
<p class="text-warning" ng-show="domain.provider === 'noop' || domain.provider === 'manual'" ng-bind-html="'email.enableEmailDialog.noProviderInfo' | tr"></p>
|
||||
<p class="text-danger" ng-show="adminDomain.provider === 'cloudflare'" ng-bind-html="'email.enableEmailDialog.cloudflareInfo' | tr:{ adminDomain: config.adminDomain, mailFqdn: config.mailFqdn }"></p>
|
||||
<div ng-hide="domain.provider === 'noop' || domain.provider === 'manual'">
|
||||
<p>
|
||||
<label class="control-label">
|
||||
<input type="checkbox" ng-model="incomingEmail.setupDns"> {{ 'email.enableEmailDialog.setupDnsCheckbox' | tr }}
|
||||
</label>
|
||||
</p>
|
||||
<span ng-bind-html="'email.enableEmailDialog.setupDnsInfo' | tr:{ importEmailDocsLink: 'https://docs.cloudron.io/guides/import-email' }"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
|
||||
<button type="button" class="btn btn-success" ng-click="incomingEmail.enable()">{{ 'email.enableEmailDialog.enableAction' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal disable email -->
|
||||
<div class="modal fade" id="disableEmailModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'email.disableEmailDialog.title' | tr:{ domain: domain.domain } }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div ng-bind-html="'email.disableEmailDialog.description' | tr:{ domain: domain.domain }"></div>
|
||||
<br/>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
|
||||
<button type="button" class="btn btn-danger" ng-click="incomingEmail.disable()">{{ 'email.disableEmailDialog.disableAction' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal add mailbox -->
|
||||
<div class="modal fade" id="mailboxAddModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'email.addMailboxDialog.title' | tr }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form name="mailboxadd_form" role="form" ng-submit="mailboxes.add.submit()" autocomplete="off">
|
||||
<input type="password" style="display: none;">
|
||||
<div class="form-group" ng-class="{ 'has-error': mailboxes.add.error }">
|
||||
<label class="control-label">{{ 'email.addMailboxDialog.name' | tr }}</label>
|
||||
<div class="control-label" ng-show="mailboxes.add.error">
|
||||
<small>{{ mailboxes.add.error.message }}</small>
|
||||
</div>
|
||||
<div class="input-group form-inline" style="margin-top: 10px;">
|
||||
<input type="text" class="form-control" ng-model="mailboxes.add.name" required autofocus autocomplete="off"/>
|
||||
<div class="input-group-addon">@{{ domain.domain }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">{{ 'email.addMailboxDialog.owner' | tr }}</label>
|
||||
<div class="control-label">
|
||||
<multiselect ng-model="mailboxes.add.owner" options="o.display for o in owners" data-compare-by="name" data-header-key="header" data-divider-key="divider" data-multiple="false" filter-after-rows="5" scroll-after-rows="10"></multiselect>
|
||||
</div>
|
||||
</div>
|
||||
<input class="hide" type="submit" ng-disabled="mailboxadd_form.$invalid || mailboxes.add.busy || !mailboxes.add.owner"/>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
|
||||
<button type="button" class="btn btn-success" ng-click="mailboxes.add.submit()" ng-disabled="mailboxadd_form.$invalid || mailboxes.add.busy || !mailboxes.add.owner"><i class="fa fa-circle-notch fa-spin" ng-show="mailboxes.add.busy"></i> {{ 'main.dialog.save' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal edit mailbox -->
|
||||
<div class="modal fade" id="mailboxEditModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'email.editMailboxDialog.title' | tr:{ name: mailboxes.edit.name, domain: domain.domain } }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form name="mailboxedit_form" role="form" ng-submit="mailboxes.edit.submit()" autocomplete="off">
|
||||
<input type="password" style="display: none;">
|
||||
<div class="form-group">
|
||||
<label class="control-label">{{ 'email.editMailboxDialog.owner' | tr }}</label>
|
||||
<div class="control-label">
|
||||
<multiselect ng-model="mailboxes.edit.owner" options="o.display for o in owners" data-compare-by="name" data-header-key="header" data-divider-key="divider" data-multiple="false" filter-after-rows="5" scroll-after-rows="10"></multiselect>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group aliases">
|
||||
<label class="control-label">{{ 'email.editMailboxDialog.aliases' | tr }}</label>
|
||||
<div class="has-error" ng-show="mailboxes.edit.error">{{ mailboxes.edit.error.message }}</div>
|
||||
|
||||
<div class="row" ng-repeat="alias in mailboxes.edit.aliases | orderBy:'reversedSortingNotation'">
|
||||
<div class="col col-lg-11">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control input-sm" ng-model="alias.name" autofocus>
|
||||
|
||||
<div class="input-group-btn">
|
||||
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">
|
||||
<span>@{{ alias.domain }}</span>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right" role="menu">
|
||||
<li ng-repeat="incomingDomain in incomingDomains">
|
||||
<a href="" ng-click="alias.domain = incomingDomain.domain">{{ incomingDomain.domain }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col col-lg-1">
|
||||
<button class="btn btn-danger btn-sm" ng-click="mailboxes.edit.delAlias($event, alias)"><i class="far fa-trash-alt"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="mailboxes.edit.aliases.length === 0">
|
||||
{{ 'email.editMailboxDialog.noAliases' | tr }} <a href="" ng-click="mailboxes.edit.addAlias($event)">{{ 'email.editMailboxDialog.addAliasAction' | tr }}</a>
|
||||
</div>
|
||||
<div ng-show="mailboxes.edit.aliases.length > 0" style="margin-top: 5px;">
|
||||
<a href="" ng-click="mailboxes.edit.addAlias($event)">{{ 'email.editMailboxDialog.addAnotherAliasAction' | tr }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="storageQuota">
|
||||
<input id="storageQuota" type="checkbox" ng-model="mailboxes.edit.storageQuotaEnabled">
|
||||
{{ 'email.editMailboxDialog.enableStorageQuota' | tr }} <b ng-hide="!mailboxes.edit.storageQuotaEnabled">: {{ mailboxes.edit.storageQuota | prettyDecimalSize }}</b>
|
||||
</input>
|
||||
</label>
|
||||
<div style="padding: 0 10px;">
|
||||
<slider id="storageQuota" ng-disabled="!mailboxes.edit.storageQuotaEnabled" ng-model="mailboxes.edit.storageQuota" step="500000000" ticks-snap-bounds="1000000000" tooltip="hide" ticks="storageQuotaTicks"></slider>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="mailboxes.edit.enablePop3"> {{ 'email.updateMailboxDialog.enablePop3' | tr }}</input>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="mailboxes.edit.active"> {{ 'email.updateMailboxDialog.activeCheckbox' | tr }}</input>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<input class="hide" type="submit" ng-disabled="mailboxedit_form.$invalid || mailboxes.edit.busy || !mailboxes.edit.owner"/>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
|
||||
<button type="button" class="btn btn-success" ng-click="mailboxes.edit.submit()" ng-disabled="mailboxedit_form.$invalid || mailboxes.edit.busy || !mailboxes.edit.owner"><i class="fa fa-circle-notch fa-spin" ng-show="mailboxes.edit.busy"></i> {{ 'main.dialog.save' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal remove mailbox -->
|
||||
<div class="modal fade" id="mailboxRemoveModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'email.deleteMailboxDialog.title' | tr:{ name: mailboxes.remove.mailbox.name, domain: domain.domain } }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div ng-bind-html="'email.deleteMailboxDialog.description' | tr"></div>
|
||||
<br/>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="mailboxes.remove.deleteMails">{{ 'email.deleteMailboxDialog.purgeMailboxCheckbox' | tr }}</input>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
|
||||
<button type="button" class="btn btn-danger" ng-click="mailboxes.remove.submit()" ng-disabled="mailboxes.remove.busy"><i class="fa fa-circle-notch fa-spin" ng-show="mailboxes.remove.busy"></i> {{ 'email.deleteMailboxDialog.deleteAction' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal import mailboxes -->
|
||||
<div class="modal fade" id="mailboxImportModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'email.mailboxImportDialog.title' | tr }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div ng-show="!mailboxImport.done">
|
||||
<div ng-show="!mailboxImport.busy">
|
||||
<p ng-bind-html=" 'email.mailboxImportDialog.description' | tr:{ docsLink: 'https://cloudron.io/documentation/email/#import-mailboxes' } "></p>
|
||||
<input type="file" style="display: none;" id="mailboxImportFileInput" accept="application/json,text/csv"/>
|
||||
<button class="btn btn-primary" ng-click="mailboxImport.openFileInput()">{{ 'email.mailboxImportDialog.fileInput' | tr }}</button>
|
||||
<br/>
|
||||
<br/>
|
||||
<p class="text-danger" ng-show="mailboxImport.error.file">{{ mailboxImport.error.file }}</p>
|
||||
<p class="text-info" ng-show="mailboxImport.mailboxes.length">{{ 'email.mailboxImportDialog.mailboxesFound' | tr:{ count: mailboxImport.mailboxes.length } }}</p>
|
||||
</div>
|
||||
<div ng-show="mailboxImport.busy" class="progress progress-striped active">
|
||||
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{ mailboxImport.percent }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="mailboxImport.done">
|
||||
<p>{{ 'email.mailboxImportDialog.success' | tr:{ count: mailboxImport.success } }}</p>
|
||||
<div ng-show="mailboxImport.error.import.length">
|
||||
<p class="text-danger">{{ 'email.mailboxImportDialog.failed' | tr }}</p>
|
||||
<div ng-repeat="tmp in mailboxImport.error.import"><b>{{ tmp.mailbox.name }}@{{ tmp.mailbox.domain }}:</b> {{ tmp.error.message }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
|
||||
<button type="button" class="btn btn-primary" ng-click="mailboxImport.import()" ng-show="!mailboxImport.done" ng-disabled="mailboxImport.busy || !mailboxImport.mailboxes.length"><i class="fa fa-circle-notch fa-spin" ng-show="mailboxImport.busy"></i> {{ 'email.mailboxImportDialog.importAction' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal add mailinglist -->
|
||||
<div class="modal fade" id="mailinglistAddModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'email.addMailinglistDialog.title' | tr }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form name="mailinglistadd_form" role="form" ng-submit="mailinglists.add.submit()" autocomplete="off">
|
||||
<input type="password" style="display: none;">
|
||||
<div class="form-group" ng-class="{ 'has-error': mailinglists.add.error.name }">
|
||||
<label class="control-label">{{ 'email.addMailinglistDialog.name' | tr }}</label>
|
||||
<div class="control-label" ng-show="mailinglists.add.error.name"><small>{{ mailinglists.add.error.name }}</small></div>
|
||||
<div class="input-group form-inline" style="margin-top: 10px;">
|
||||
<input type="text" class="form-control" ng-model="mailinglists.add.name" required autofocus autocomplete="off"/>
|
||||
<div class="input-group-addon">@{{ domain.domain }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">{{ 'email.addMailinglistDialog.members' | tr }}</label><br/>
|
||||
<div class="has-error control-label" ng-show="mailinglists.add.error.members"><small>{{ mailinglists.add.error.members }}</small></div>
|
||||
<textarea ng-model="mailinglists.add.membersTxt" class="form-control" rows="5"></textarea>
|
||||
<small>{{ 'email.addMailinglistDialog.membersInfo' | tr }}</small>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="mailinglists.add.membersOnly">{{ 'email.addMailinglistDialog.membersOnlyCheckbox' | tr }}</input>
|
||||
</label>
|
||||
</div>
|
||||
<input class="hide" type="submit" ng-disabled="mailinglistadd_form.$invalid || mailinglists.add.membersTxt.length === 0 || mailinglists.add.busy"/>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
|
||||
<button type="button" class="btn btn-success" ng-click="mailinglists.add.submit()" ng-disabled="mailinglistadd_form.$invalid || mailinglists.add.membersTxt.length === 0 || mailinglists.add.busy"><i class="fa fa-circle-notch fa-spin" ng-show="mailinglists.add.busy"></i> {{ 'main.dialog.save' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal edit mailinglist -->
|
||||
<div class="modal fade" id="mailinglistEditModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'email.editMailinglistDialog.title' | tr:{ name: mailinglists.edit.name, domain: domain.domain } }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form name="mailinglistedit_form" role="form" ng-submit="mailinglists.edit.submit()" autocomplete="off">
|
||||
<input type="password" style="display: none;">
|
||||
<div class="form-group" ng-class="{ 'has-error': mailinglists.edit.error.members }">
|
||||
<label class="control-label">{{ 'email.addMailinglistDialog.members' | tr }}</label><br/>
|
||||
<div class="has-error control-label" ng-show="mailinglists.edit.error.members"><small>{{ mailinglists.edit.error.members }}</small></div>
|
||||
<textarea ng-model="mailinglists.edit.membersTxt" class="form-control" rows="5" autofocus></textarea>
|
||||
<small>{{ 'email.addMailinglistDialog.membersInfo' | tr }}</small>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="mailinglists.edit.membersOnly">{{ 'email.addMailinglistDialog.membersOnlyCheckbox' | tr }}</input>
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="mailinglists.edit.active"> {{ 'email.updateMailinglistDialog.activeCheckbox' | tr }}</input>
|
||||
</label>
|
||||
</div>
|
||||
<input class="hide" type="submit" ng-disabled="mailinglistedit_form.$invalid || mailinglists.edit.busy"/>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
|
||||
<button type="button" class="btn btn-success" ng-click="mailinglists.edit.submit()" ng-disabled="mailinglistedit_form.$invalid || mailinglists.edit.busy"><i class="fa fa-circle-notch fa-spin" ng-show="mailinglists.edit.busy"></i> {{ 'main.dialog.save' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal remove mailinglist -->
|
||||
<div class="modal fade" id="mailinglistRemoveModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'email.deleteMailinglistDialog.title' | tr:{ name: mailinglists.remove.list.name, domain: domain.domain } }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p ng-bind-html="'email.deleteMailinglistDialog.description' | tr:{ name: mailinglists.remove.list.name, domain: domain.domain }"></p>`
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
|
||||
<button type="button" class="btn btn-danger" ng-click="mailinglists.remove.submit()" ng-disabled="mailinglists.remove.busy"><i class="fa fa-circle-notch fa-spin" ng-show="mailinglist.remove.busy"></i> {{ 'email.deleteMailinglistDialog.deleteAction' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal how to connect -->
|
||||
<div class="modal fade" id="howToConnectInfoModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4>{{ 'email.howToConnectInfoModal' | tr }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p ng-bind-html=" 'email.incoming.howToConnectDescription' | tr:{ domain: domain.domain } "></p>
|
||||
|
||||
<p><b>{{ 'email.incoming.incomingUserInfo' | tr }}</b><br/><i>mailboxname</i>@{{ domain.domain }}</p>
|
||||
<p><b>{{ 'email.incoming.incomingPasswordInfo' | tr }}</b><br/>{{ 'email.incoming.incomingPasswordUsage' | tr }}</p>
|
||||
<p><b>{{ 'email.incoming.incomingServerInfo' | tr }}</b><br/>{{ 'email.incoming.server' | tr }}: <span ng-click-select>{{config.mailFqdn}}</span><br/>{{ 'email.incoming.port' | tr }}: 993 (TLS)</p>
|
||||
<p><b>{{ 'email.incoming.outgointServerInfo' | tr }}</b><br/>{{ 'email.incoming.server' | tr }}: <span ng-click-select>{{config.mailFqdn}}</span><br/>{{ 'email.incoming.port' | tr }}: 587 (STARTTLS) or 465 (TLS)</p>
|
||||
<p><b>{{ 'email.incoming.sieveServerInfo' | tr }}</b><br/>{{ 'email.incoming.server' | tr }}: <span ng-click-select>{{config.mailFqdn}}</span><br/>{{ 'email.incoming.port' | tr }}: 4190 (STARTTLS)</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="!ready" class="loading-banner">
|
||||
<h1><i class="fa fa-circle-notch fa-spin"></i></h1>
|
||||
</div>
|
||||
|
||||
<div class="content" ng-show="ready">
|
||||
<a href="/#/email" class="back-to-view-link"><i class="fas fa-arrow-left"></i> {{ 'email.backAction' | tr }}</a>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="text-left">
|
||||
<h3>
|
||||
{{ 'email.config.title' | tr:{ domain: domain.domain } }}
|
||||
|
||||
<div class="dropdown pull-right" style="display: inline-block">
|
||||
<button class="btn btn-sm btn-default dropdown-toggle" type="button" data-toggle="dropdown" uib-tooltip="{{ 'app.docsActionTooltip' | tr }}" tooltip-append-to-body="true" tooltip-placement="bottom">
|
||||
<i class="fas fa-book"></i>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li><a href="https://docs.cloudron.io/email/" target="_blank">{{ 'app.docsAction' | tr }}</a></li>
|
||||
<li ng-class="{ 'disabled': !domain.mailConfig.enabled }"><a href="" ng-click="howToConnectInfo.show()">{{ 'email.config.clientConfiguration' | tr }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<uib-tabset active="activeTab">
|
||||
<uib-tab index="'mailboxes'" select="setView('mailboxes')" heading="{{ 'email.incoming.tabTitle' | tr }}">
|
||||
<div class="card card-large" style="margin-bottom: 15px;">
|
||||
<h4>{{ 'email.incoming.title' | tr }}</h4>
|
||||
|
||||
<p ng-show="domain.mailConfig.enabled">{{ 'email.incoming.enabled' | tr }}</p>
|
||||
<p ng-hide="domain.mailConfig.enabled">{{ 'email.incoming.disabled' | tr }}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<button class="pull-right" ng-class="domain.mailConfig.enabled ? 'btn btn-danger' : 'btn btn-primary'" ng-click="incomingEmail.toggleEmailEnabled()" ng-disabled="incomingEmail.busy" ng-show="user.isAtLeastAdmin">
|
||||
<i class="fa fa-circle-notch fa-spin" ng-show="incomingEmail.busy"></i>
|
||||
{{ domain.mailConfig.enabled ? ('email.incoming.disableAction' | tr) : ('email.incoming.enableAction' | tr) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="text-left">
|
||||
<h3 style="margin-bottom: 15px;">{{ 'email.incoming.mailboxes.title' | tr }}
|
||||
<button class="btn btn-primary btn-outline pull-right" ng-click="mailboxes.add.show()" ng-disabled="!domain.mailConfig.enabled" tooltip-enable="!domain.mailConfig.enabled" uib-tooltip="{{ 'email.incoming.mailboxes.disabledTooltip' | tr }}"><i class="fa fa-inbox"></i> {{ 'email.incoming.mailboxes.addAction' | tr }}</button>
|
||||
<div class="btn-group pull-right" style="margin-left: 5px;">
|
||||
<button class="btn btn-default" ng-click="mailboxImport.show()" uib-tooltip="{{ 'email.incoming.mailboxes.importTooltip' | tr }}" tooltip-append-to-body="true"><i class="fas fa-download"></i></button>
|
||||
<div class="btn-group" role="group">
|
||||
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" uib-tooltip="{{ 'email.incoming.mailboxes.exportTooltip' | tr }}" tooltip-append-to-body="true">
|
||||
<i class="fas fa-upload"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li><a href="" ng-click="mailboxExport('csv')">{{ 'email.incoming.mailboxes.mailboxExport.csv' | tr }}</a></li>
|
||||
<li><a href="" ng-click="mailboxExport('json')">{{ 'email.incoming.mailboxes.mailboxExport.json' | tr }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<input class="form-control pull-right" style="width: 200px;" placeholder="{{ 'main.searchPlaceholder' | tr }}" type="text" ng-model="mailboxes.search" ng-model-options="{ debounce: 1000 }" ng-change="mailboxes.updateFilter()" />
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div class="card card-large" style="margin-bottom: 15px;">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ 'email.incoming.mailboxes.name' | tr }}</th>
|
||||
<th>{{ 'email.incoming.mailboxes.owner' | tr }}</th>
|
||||
<th>{{ 'email.incoming.mailboxes.aliases' | tr }}</th>
|
||||
<th>{{ 'email.incoming.mailboxes.usage' | tr }}</th>
|
||||
<th class="text-right">{{ 'main.actions' | tr }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="mailbox in mailboxes.mailboxes | filter:mailboxes.search" ng-class="{'text-muted': !mailbox.active}">
|
||||
<td class="hand" ng-click="mailboxes.edit.show(mailbox)">
|
||||
{{ mailbox.name }}
|
||||
</td>
|
||||
<td class="hand" ng-click="mailboxes.edit.show(mailbox)">
|
||||
{{ mailbox.ownerDisplayName }}
|
||||
</td>
|
||||
<td class="hand elide-table-cell" ng-click="mailboxes.edit.show(mailbox)">
|
||||
<span ng-repeat="alias in mailbox.aliases"> {{ alias.name + '@' + alias.domain }}</span>
|
||||
</td>
|
||||
<td class="hand no-wrap" ng-click="mailboxes.edit.show(mailbox)">
|
||||
<span ng-show="mailUsage !== null">
|
||||
{{ mailUsage[mailbox.fullName].quotaValue | prettyDecimalSize }} <span ng-show="mailUsage[mailbox.fullName].quotaLimit">/ {{ mailUsage[mailbox.fullName].quotaLimit | prettyDecimalSize }}</span>
|
||||
</span>
|
||||
<span ng-show="mailUsage === null">
|
||||
{{ 'main.loadingPlaceholder' | tr }} ...
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-right no-wrap">
|
||||
<button class="btn btn-xs btn-default" ng-click="mailboxes.edit.show(mailbox)"><i class="fa fa-pencil-alt"></i></button>
|
||||
<button class="btn btn-xs btn-danger" ng-click="mailboxes.remove.show(mailbox)"><i class="far fa-trash-alt"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="pull-right">
|
||||
<button class="btn btn-default btn-outline btn-xs" ng-click="mailboxes.showPrevPage()" ng-class="{ 'btn-primary': mailboxes.currentPage > 1 }" ng-disabled="mailboxes.busy || mailboxes.currentPage <= 1"><i class="fa fa-angle-double-left"></i> {{ 'main.pagination.prev' | tr }}</button>
|
||||
<button class="btn btn-default btn-outline btn-xs" ng-click="mailboxes.showNextPage()" ng-class="{ 'btn-primary': mailboxes.perPage <= mailboxes.mailboxes.length }" ng-disabled="mailboxes.busy || mailboxes.perPage > mailboxes.mailboxes.length">{{ 'main.pagination.next' | tr }} <i class="fa fa-angle-double-right"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="text-left">
|
||||
<h3 style="margin-bottom: 15px;">{{ 'email.incoming.mailinglists.title' | tr }}
|
||||
<button class="btn btn-primary btn-outline pull-right" ng-click="mailinglists.add.show()" ng-disabled="!domain.mailConfig.enabled" tooltip-enable="!domain.mailConfig.enabled" uib-tooltip="{{ 'email.incoming.mailboxes.disabledTooltip' | tr }}"><i class="fa fa-list"></i> {{ 'email.incoming.mailboxes.addAction' | tr }}</button>
|
||||
<input class="form-control pull-right" style="width: 200px;" placeholder="{{ 'main.searchPlaceholder' | tr }}" type="text" ng-model="mailinglists.search" ng-model-options="{ debounce: 1000 }" ng-change="mailinglists.updateFilter()" />
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div class="card card-large" style="margin-bottom: 15px;">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{{ 'email.incoming.mailinglists.description' | tr }}
|
||||
<br/>
|
||||
<br/>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 0.5%;"></th>
|
||||
<th>{{ 'email.incoming.mailinglists.name' | tr }}</th>
|
||||
<th>{{ 'email.incoming.mailinglists.members' | tr }}</th>
|
||||
<th class="text-right">{{ 'main.actions' | tr }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="list in mailinglists.mailinglists | filter:mailinglists.search | orderBy:'name'" ng-class="{'text-muted': !list.active}">
|
||||
<td>
|
||||
<i class="fas fa-door-closed" ng-show="list.membersOnly" uib-tooltip="{{ 'email.incoming.mailinglists.membersOnlyTooltip' | tr }}"></i>
|
||||
<i class="fas fa-door-open" ng-show="!list.membersOnly" uib-tooltip="{{ 'email.incoming.mailinglists.everyoneTooltip' | tr }}"></i>
|
||||
</td>
|
||||
<td class="hand" ng-click="mailinglists.edit.show(list)">
|
||||
{{ list.name }}
|
||||
</td>
|
||||
<td class="hand" ng-click="mailinglists.edit.show(list)">
|
||||
{{ list.members.join(', ') }}
|
||||
</td>
|
||||
<td class="text-right no-wrap">
|
||||
<button class="btn btn-xs btn-default" ng-click="mailinglists.edit.show(list)"><i class="fa fa-pencil-alt"></i></button>
|
||||
<button class="btn btn-xs btn-danger" ng-click="mailinglists.remove.show(list)"><i class="far fa-trash-alt"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="pull-right">
|
||||
<button class="btn btn-default btn-outline btn-xs" ng-click="mailinglists.showPrevPage()" ng-class="{ 'btn-primary': mailinglists.currentPage > 1 }" ng-disabled="mailinglists.busy || mailinglists.currentPage <= 1"><i class="fa fa-angle-double-left"></i> {{ 'main.pagination.prev' | tr }}</button>
|
||||
<button class="btn btn-default btn-outline btn-xs" ng-click="mailinglists.showNextPage()" ng-class="{ 'btn-primary': mailinglists.perPage <= mailinglists.mailinglists.length }" ng-disabled="mailinglists.busy || mailinglists.perPage > mailinglists.mailinglists.length">{{ 'main.pagination.next' | tr }} <i class="fa fa-angle-double-right"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="text-left">
|
||||
<h3>{{ 'email.incoming.catchall.title' | tr }}</h3>
|
||||
</div>
|
||||
|
||||
<div class="card card-large" style="margin-bottom: 15px;">
|
||||
<div class="row">
|
||||
<div class="col-md-12" ng-bind-html=" 'email.incoming.catchall.description' | tr "></div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<multiselect ng-model="catchall.mailboxes" options="mailbox.display for mailbox in catchall.availableMailboxes" data-compare-by="display" data-multiple="true" filter-after-rows="5" scroll-after-rows="10"></multiselect>
|
||||
<button class="btn btn-outline btn-primary" ng-click="catchall.submit()" ng-disabled="catchall.busy || !domain.mailConfig.enabled" tooltip-enable="!domain.mailConfig.enabled" uib-tooltip="{{ 'email.incoming.mailboxes.disabledTooltip' | tr }}">
|
||||
<i class="fa fa-circle-notch fa-spin" ng-show="catchall.busy"></i> {{ 'email.incoming.catchall.saveAction' | tr }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</uib-tab>
|
||||
|
||||
<uib-tab index="'outbound'" ng-if="user.isAtLeastAdmin" select="setView('outbound')" heading="{{ 'email.outbound.tabTitle' | tr }}">
|
||||
<div class="card card-large" style="margin-bottom: 15px;">
|
||||
<h4>{{ 'email.outbound.title' | tr }} <sup><a ng-href="https://docs.cloudron.io/email/#relay-outbound-mails" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></h4>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12" ng-bind-html=" 'email.outbound.description' | tr "></div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="form-group">
|
||||
<select class="form-control" style="width: 50%;" ng-model="mailRelay.preset" ng-options="a.name for a in mailRelayPresets track by a.provider" ng-change="mailRelay.presetChanged()"></select>
|
||||
</div>
|
||||
|
||||
<p class="small text-danger" ng-show="mailRelay.preset.provider === 'noop'">
|
||||
<span ng-if="domain.domain === config.adminDomain">{{ 'email.outbound.noopAdminDomainWarning' | tr }}</span>
|
||||
<span ng-if="domain.domain !== config.adminDomain">{{ 'email.outbound.noopNonAdminDomainWarning' | tr }}</span>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="usesExternalServer(mailRelay.preset.provider)">
|
||||
<div class="col-md-6">
|
||||
<div>
|
||||
<form name="mailRelayForm" role="form" ng-submit="mailRelay.submit()" autocomplete="off" novalidate>
|
||||
<div class="form-group" ng-class="{ 'has-error': (mailRelayForm.host.$dirty && mailRelayForm.host.$invalid) }">
|
||||
<label class="control-label">{{ 'email.outbound.mailRelay.host' | tr }}</label>
|
||||
<div class="control-label" ng-show="(!mailRelayForm.host.$dirty && mailRelay.error.host) || (mailRelayForm.host.$dirty && mailRelayForm.host.$invalid)">
|
||||
<small ng-show="!mailRelayForm.host.$dirty && mailRelay.error.host">{{ mailRelay.error.host }}</small>
|
||||
</div>
|
||||
<input type="text" class="form-control" ng-model="mailRelay.relay.host" name="host" required>
|
||||
</div>
|
||||
<div class="form-group" ng-class="{ 'has-error': (mailRelayForm.port.$dirty && mailRelayForm.port.$invalid) }">
|
||||
<label class="control-label">{{ 'email.outbound.mailRelay.port' | tr }}</label>
|
||||
<div class="control-label" ng-show="(!mailRelayForm.port.$dirty && mailRelay.error.port) || (mailRelayForm.port.$dirty && mailRelayForm.port.$invalid)">
|
||||
<small ng-show="!mailRelayForm.port.$dirty && mailRelay.error.port">{{ mailRelay.error.port }}</small>
|
||||
</div>
|
||||
<input type="number" class="form-control" ng-model="mailRelay.relay.port" name="port" required>
|
||||
</div>
|
||||
|
||||
<div class="checkbox" ng-show="mailRelay.relay.provider === 'external-smtp' || mailRelay.relay.provider === 'external-smtp-noauth'" >
|
||||
<label>
|
||||
<input type="checkbox" ng-model="mailRelay.relay.acceptSelfSignedCerts">{{ 'email.outbound.mailRelay.selfsignedCheckbox' | tr }}</input>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Postmark, Sendgrid, SparkPost -->
|
||||
<div ng-show="usesTokenAuth(mailRelay.relay.provider)" class="form-group" ng-class="{ 'has-error': (mailRelayForm.serverApiToken.$dirty && mailRelayForm.serverApiToken.$invalid) }">
|
||||
<label class="control-label">{{ 'email.outbound.mailRelay.apiTokenOrKey' | tr }}</label>
|
||||
<div class="control-label" ng-show="(!mailRelayForm.serverApiToken.$dirty && mailRelay.error.serverApiToken) || (mailRelayForm.serverApiToken.$dirty && mailRelayForm.serverApiToken.$invalid)">
|
||||
<small ng-show="!mailRelayForm.serverApiToken.$dirty && mailRelay.error.serverApiToken">{{ mailRelay.error.serverApiToken }}</small>
|
||||
</div>
|
||||
<input type="text" class="form-control" ng-model="mailRelay.relay.serverApiToken" name="serverApiToken" ng-required="usesTokenAuth(mailRelay.relay.provider)">
|
||||
</div>
|
||||
|
||||
<!-- Other -->
|
||||
<div ng-show="usesPasswordAuth(mailRelay.relay.provider)" class="form-group" ng-class="{ 'has-error': (mailRelayForm.username.$dirty && mailRelayForm.username.$invalid) }">
|
||||
<label class="control-label">{{ 'email.outbound.mailRelay.username' | tr }}</label>
|
||||
<div class="control-label" ng-show="(!mailRelayForm.username.$dirty && mailRelay.error.username) || (mailRelayForm.username.$dirty && mailRelayForm.username.$invalid)">
|
||||
<small ng-show="!mailRelayForm.username.$dirty && mailRelay.error.username">{{ mailRelay.error.username }}</small>
|
||||
</div>
|
||||
<input type="text" class="form-control" ng-model="mailRelay.relay.username" name="username" ng-required="usesPasswordAuth(mailRelay.relay.provider)">
|
||||
</div>
|
||||
|
||||
<div ng-show="usesPasswordAuth(mailRelay.relay.provider)" class="form-group" ng-class="{ 'has-error': (mailRelayForm.password.$dirty && mailRelayForm.password.$invalid) }">
|
||||
<label class="control-label">{{ 'email.outbound.mailRelay.password' | tr }}</label>
|
||||
<div class="control-label" ng-show="(!mailRelayForm.password.$dirty && mailRelay.error.password) || (mailRelayForm.password.$dirty && mailRelayForm.password.$invalid)">
|
||||
<small ng-show="!mailRelayForm.password.$dirty && mailRelay.error.password">{{ mailRelay.error.password }}</small>
|
||||
</div>
|
||||
<input type="password" class="form-control" ng-model="mailRelay.relay.password" name="password" ng-required="usesPasswordAuth(mailRelay.relay.provider)" password-reveal>
|
||||
</div>
|
||||
|
||||
<input class="ng-hide" type="submit" ng-disabled="mailRelayForm.$invalid"/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<button class="btn btn-primary pull-right" ng-click="mailRelay.submit()" ng-disabled="(usesExternalServer(mailRelay.preset.provider) && (!mailRelayForm.$dirty || mailRelayForm.$invalid)) || mailRelay.busy"><i class="fa fa-circle-notch fa-spin" ng-show="mailRelay.busy"></i> {{ 'email.outbound.mailRelay.saveAction' | tr }}</button>
|
||||
|
||||
<span class="has-error text-center" ng-show="mailRelay.error">{{ mailRelay.error }}</span>
|
||||
<span class="text-success text-center text-bold" ng-show="mailRelay.success">{{ 'email.outbound.mailRelay.saveSuccess' | tr }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="mailRelay.preset.spfDoc">
|
||||
<br/>
|
||||
<div class="col-md-12">
|
||||
<span class="text-info" ng-bind-html="'email.outbound.mailRelay.spfDocInfo' | tr:{ name: mailRelay.preset.name, spfDocsLink: mailRelay.preset.spfDoc }"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</uib-tab>
|
||||
|
||||
<uib-tab index="'settings'" select="setView('settings')" heading="{{ 'email.settings.tabTitle' | tr }}">
|
||||
<div class="card card-large" style="margin-bottom: 15px;">
|
||||
<h4>{{ 'email.masquerading.title' | tr }}</h4>
|
||||
<p ng-bind-html=" 'email.masquerading.description' | tr "></p>
|
||||
<div class="row">
|
||||
<div class="col-md-12 text-right">
|
||||
<button class="pull-right" ng-class="domain.mailConfig.mailFromValidation ? 'btn btn-danger' : 'btn btn-primary'" ng-disabled="mailFromValidation.busy" ng-click="mailFromValidation.submit()">
|
||||
<i class="fa fa-circle-notch fa-spin" ng-show="mailFromValidation.busy"></i> {{ domain.mailConfig.mailFromValidation ? ('email.masquerading.enableAction' | tr) : ('email.masquerading.disableAction' | tr) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-large">
|
||||
<h4>{{ 'email.signature.title' | tr }}</h4>
|
||||
<p ng-bind-html=" 'email.signature.description' | tr "></p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<form role="form" name="bannerForm" ng-submit="banner.submit()" autocomplete="off">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<label class="control-label" style="width: 100%">{{ 'email.signature.plainTextFormat' | tr }}</label>
|
||||
<textarea ng-model="banner.text" class="form-control" rows="4"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label" style="width: 100%">{{ 'email.signature.htmlFormat' | tr }}</label>
|
||||
<textarea ng-model="banner.html" class="form-control" rows="4"></textarea>
|
||||
</div>
|
||||
|
||||
<input class="ng-hide" type="submit" ng-disabled="banner.$invalid || banner.busy"/>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12 text-right">
|
||||
<button class="btn btn-outline btn-primary pull-right" ng-click="banner.submit()" ng-disabled="banner.$invalid || banner.busy">
|
||||
<i class="fa fa-circle-notch fa-spin" ng-show="banner.busy"></i> {{ 'email.signature.saveAction' | tr }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</uib-tab>
|
||||
|
||||
<uib-tab index="'status'" ng-if="user.isAtLeastAdmin" select="setView('status')" heading="{{ 'email.status.tabTitle' | tr }}">
|
||||
<!-- nothing to show if incoming mail is disabled and using a relay -->
|
||||
<div class="card card-large" style="margin-bottom: 15px;" ng-hide="!domain.mailConfig.enabled && domain.mailConfig.relay.provider !== 'cloudron-smtp'">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h4>{{ 'email.dnsStatus.title' | tr }}
|
||||
<button class="btn btn-xs btn-primary btn-outline pull-right" ng-click="incomingEmail.setDnsRecords()">
|
||||
<i class="fa fa-circle-notch fa-spin" ng-show="incomingEmail.setupDnsBusy"></i> {{ 'email.dnsStatus.reSetupAction' | tr }}
|
||||
</button>
|
||||
</h4>
|
||||
<span ng-bind-html="'email.dnsStatus.description' | tr:{ emailDnsDocsLink:'https://docs.cloudron.io/email/#dns-records'}"></span>
|
||||
<br/>
|
||||
<br/>
|
||||
<div ng-repeat="record in expectedDnsRecordsTypes">
|
||||
<div class="row" ng-if="expectedDnsRecords[record.value].expected">
|
||||
<div class="col-xs-12">
|
||||
<p class="text-muted">
|
||||
<i ng-hide="refreshBusy" ng-class="expectedDnsRecords[record.value].status ? 'fa fa-check-circle text-success' : 'fa fa-exclamation-triangle text-danger'"></i>
|
||||
<a href="" data-toggle="collapse" data-parent="#accordion" data-target="#collapse_dns_{{ record.value }}">{{ record.name }} record</a>
|
||||
<button class="btn btn-xs btn-default" ng-click="refreshStatus()" ng-disabled="refreshBusy" ng-show="!expectedDnsRecords[record.value].status"><i class="fa fa-sync-alt" ng-class="{ 'fa-pulse': refreshBusy }"></i></button>
|
||||
</p>
|
||||
<div id="collapse_dns_{{ record.value }}" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
<p ng-show="record.name === 'MX' && domain.provider === 'namecheap'">{{ 'email.dnsStatus.namecheapInfo' | tr }} <sup><a ng-href="https://docs.cloudron.io/domains/#namecheap-dns" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></p>
|
||||
<p ng-show="record.name === 'PTR'">{{ 'email.dnsStatus.ptrInfo' | tr }} <sup><a ng-href="https://docs.cloudron.io/troubleshooting/#ptr-record" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></p>
|
||||
<p ng-show="expectedDnsRecords[record.value].name">{{ 'email.dnsStatus.hostname' | tr }}: <b ng-click-select><tt>{{ expectedDnsRecords[record.value].name }}</tt></b></p>
|
||||
<p ng-hide="expectedDnsRecords[record.value].name">{{ 'email.dnsStatus.domain' | tr }}: <b ng-click-select><tt>{{ expectedDnsRecords[record.value].domain }}</tt></b></p>
|
||||
<p>{{ 'email.dnsStatus.type' | tr }}: <b ng-click-select><tt>{{ expectedDnsRecords[record.value].type }}</tt></b></p>
|
||||
<p style="overflow: auto; white-space: nowrap;">{{ 'email.dnsStatus.expected' | tr }}: <b ng-click-select><tt>{{ expectedDnsRecords[record.value].expected }}</tt></b></p>
|
||||
<p style="overflow: auto; white-space: nowrap;">{{ 'email.dnsStatus.current' | tr }}: <b ng-click-select><tt>{{ expectedDnsRecords[record.value].value ? expectedDnsRecords[record.value].value : ('['+('email.dnsStatus.recordNotSet' | tr)+']') }}</tt></b></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-large" style="margin-bottom: 15px;" ng-if="domain.mailConfig.relay.provider !== 'noop'">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h4>{{ 'email.smtpStatus.title' | tr }} <sup><a ng-href="https://docs.cloudron.io/email/#smtp-status" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></h4>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<p class="text-muted">
|
||||
<i ng-hide="refreshBusy" ng-class="domain.mailStatus.relay.status ? 'fa fa-check-circle text-success' : 'fa fa-exclamation-triangle text-danger'"></i>
|
||||
<a href="" data-toggle="collapse" data-parent="#accordion" data-target="#collapse_outbound_smtp">
|
||||
{{ domain.mailConfig.relay.provider === 'cloudron-smtp' ? ('email.smtpStatus.outboudDirect' | tr) : ('email.smtpStatus.outboudRelay' | tr) }}
|
||||
</a>
|
||||
<button class="btn btn-xs btn-default" ng-click="refreshStatus()" ng-disabled="refreshBusy" ng-show="!domain.mailStatus.relay.status"><i class="fa fa-sync-alt" ng-class="{ 'fa-pulse': refreshBusy }"></i></button>
|
||||
</p>
|
||||
<div id="collapse_outbound_smtp" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
<p><b> {{ domain.mailStatus.relay.value }} </b> </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="domain.mailConfig.relay.provider === 'cloudron-smtp'">
|
||||
<div class="col-xs-12">
|
||||
<p class="text-muted">
|
||||
<i ng-hide="refreshBusy" ng-class="domain.mailStatus.rbl.status ? 'fa fa-check-circle text-success' : 'fa fa-exclamation-triangle text-danger'"></i>
|
||||
<a href="" data-toggle="collapse" data-parent="#accordion" data-target="#collapse_rbl">{{ 'email.smtpStatus.blacklistCheck' | tr }}</a>
|
||||
<button class="btn btn-xs btn-default" ng-click="refreshStatus()" ng-disabled="refreshBusy" ng-show="!domain.mailStatus.rbl.status"><i class="fa fa-sync-alt" ng-class="{ 'fa-pulse': refreshBusy }"></i></button>
|
||||
</p>
|
||||
<div id="collapse_rbl" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
<div ng-show="domain.mailStatus.rbl.servers.length" ng-bind-html="'email.smtpStatus.blacklisted' | tr:{ ip: domain.mailStatus.rbl.ip }"></div>
|
||||
<div ng-hide="domain.mailStatus.rbl.servers.length" ng-bind-html="'email.smtpStatus.notBlacklisted' | tr:{ ip: domain.mailStatus.rbl.ip }"></div>
|
||||
<div ng-repeat="server in domain.mailStatus.rbl.servers">
|
||||
<a ng-href="{{server.site}}" target="_blank">{{ server.name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</uib-tab>
|
||||
</uib-tabset>
|
||||
</div>
|
||||
@@ -1,100 +0,0 @@
|
||||
<div>
|
||||
<a href="/#/email" class="back-to-view-link"><i class="fas fa-arrow-left"></i> {{ 'email.backAction' | tr }}</a>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
<h1>
|
||||
{{ 'emails.eventlog.title' | tr }}
|
||||
|
||||
<a class="btn btn-default btn-outline pull-right" href="/logs.html?id=mail" target="_blank">{{ 'main.action.logs' | tr }}</a>
|
||||
<a class="btn btn-default btn-outline pull-right" href="#/emails-queue">{{ 'emails.action.queue' | tr }}</a>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
<div class="maillog-filter">
|
||||
<input class="form-control" style="width: 200px;" placeholder="{{ 'main.searchPlaceholder' | tr }}" type="text" ng-model="activity.search" ng-model-options="{ debounce: 1000 }" ng-change="activity.updateFilter(true)" />
|
||||
<multiselect ng-model="activity.selectedTypes" ms-header="{{ 'emails.typeFilterHeader' | tr }}" options="a.name for a in activityTypes" data-multiple="true" ng-change="activity.updateFilter(true)" filter-after-rows="5" scroll-after-rows="10"></multiselect>
|
||||
<select class="form-control" ng-model="activity.pageItems" ng-options="a.name for a in pageItemCount" ng-change="activity.updateFilter(true)"></select>
|
||||
</div>
|
||||
<div class="pagination pull-right">
|
||||
<button class="btn btn-default btn-outline" ng-click="activity.refresh()"><i class="fas fa-sync-alt" ng-class="{ 'fa-spin': busyRefresh }"></i></button>
|
||||
<button class="btn btn-default btn-outline" ng-click="activity.showPrevPage()" ng-disabled="activity.busy || activity.currentPage <= 1"><i class="fa fa-angle-double-left"></i> {{ 'main.pagination.prev' | tr }}</button>
|
||||
<button class="btn btn-default btn-outline" ng-click="activity.showNextPage()" ng-disabled="activity.busy || activity.perPage > activity.eventLogs.length">{{ 'main.pagination.next' | tr }} <i class="fa fa-angle-double-right"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
<div class="card card-block" style="max-width: 100%">
|
||||
<div>
|
||||
<center ng-show="activity.busy"><h2><i class="fa fa-circle-notch fa-spin"></i></h2></center>
|
||||
<table ng-hide="activity.busy" class="table table-hover" style="margin: 0; table-layout:fixed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 5%"><!-- Icon --></th>
|
||||
<th style="width: 15%">{{ 'emails.eventlog.time' | tr }}</th>
|
||||
<th style="width: 25%">{{ 'emails.eventlog.mailFrom' | tr }}</th>
|
||||
<th style="width: 25%">{{ 'emails.eventlog.rcptTo' | tr }}</th>
|
||||
<th style="width: 30%">{{ 'emails.eventlog.details' | tr }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody ng-hide="activity.eventLogs.length">
|
||||
<tr>
|
||||
<td colspan="4" class="text-center">
|
||||
<br>
|
||||
<br>
|
||||
{{ 'emails.eventlog.empty' | tr }}
|
||||
<br>
|
||||
<br>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody ng-show="activity.eventLogs.length" ng-repeat="eventlog in activity.eventLogs">
|
||||
<tr ng-click="activity.showEventLogDetails(eventlog)" class="hand">
|
||||
<td class="no-wrap">
|
||||
<i class="fas fa-arrow-circle-left" ng-show="eventlog.type === 'delivered'" uib-tooltip="{{ 'emails.eventlog.type.outgoing' | tr }}"></i>
|
||||
<i class="fas fa-history" ng-show="eventlog.type === 'deferred'" uib-tooltip="{{ 'emails.eventlog.type.deferred' | tr }}"></i>
|
||||
<i class="fas fa-arrow-circle-right" ng-show="eventlog.type === 'received'" uib-tooltip="{{ 'emails.eventlog.type.incoming' | tr }}"></i>
|
||||
<i class="fas fa-align-justify" ng-show="eventlog.type === 'queued' && eventlog.spamStatus.indexOf('Yes,') !== 0" uib-tooltip="{{ 'emails.eventlog.type.queued' | tr }}"></i>
|
||||
<i class="fas fa-trash" ng-show="eventlog.type === 'queued' && eventlog.spamStatus.indexOf('Yes,') === 0" uib-tooltip="{{ 'emails.eventlog.type.queued' | tr }}"></i>
|
||||
<i class="fas fa-minus-circle" ng-show="eventlog.type === 'denied'" uib-tooltip="{{ 'emails.eventlog.type.denied' | tr }}"></i>
|
||||
<i class="fas fa-hand-paper" ng-show="eventlog.type === 'bounce'" uib-tooltip="{{ 'emails.eventlog.type.bounce' | tr }}"></i>
|
||||
<i class="fas fa-filter" ng-show="eventlog.type === 'spam-learn'" uib-tooltip="{{ 'emails.eventlog.type.spamFilterTrained' | tr }}"></i>
|
||||
<i class="fas fa-fill-drip" ng-show="eventlog.type === 'quota'" uib-tooltip="{{ 'emails.eventlog.type.quota' | tr }}"></i>
|
||||
</td>
|
||||
<td class="no-wrap"><span uib-tooltip="{{ eventlog.ts | prettyLongDate }}" class="arrow">{{ eventlog.ts | prettyDate }}</span></td>
|
||||
<td class="elide-table-cell">{{ (eventlog.mailFrom | prettyEmailAddresses) || '-' }}</td>
|
||||
<td class="elide-table-cell">{{ (eventlog.rcptTo | prettyEmailAddresses) || eventlog.mailbox || '-' }}</td>
|
||||
<td>
|
||||
<span ng-show="eventlog.type === 'bounce'">{{ 'emails.eventlog.type.bounceInfo' | tr }}. {{ eventlog.message || eventlog.reason }}</span>
|
||||
<span ng-show="eventlog.type === 'deferred'">{{ 'emails.eventlog.type.deferredInfo' | tr: { delay:eventlog.delay } }}. {{ eventlog.message || eventlog.reason }} </span>
|
||||
<span ng-show="eventlog.type === 'queued'">
|
||||
<span ng-show="eventlog.direction === 'inbound'">{{ 'emails.eventlog.type.inboundInfo' | tr }}</span>
|
||||
<span ng-show="eventlog.direction === 'outbound'">{{ 'emails.eventlog.type.outboundInfo' | tr }}</span>
|
||||
</span>
|
||||
<span ng-show="eventlog.type === 'received'">{{ 'emails.eventlog.type.receivedInfo' | tr }}</span>
|
||||
<span ng-show="eventlog.type === 'delivered'">{{ 'emails.eventlog.type.deliveredInfo' | tr }}</span>
|
||||
<span ng-show="eventlog.type === 'denied'">{{ 'emails.eventlog.type.deniedInfo' | tr }}. {{ eventlog.message || eventlog.reason }} </span>
|
||||
<span ng-show="eventlog.type === 'spam-learn'">{{ 'emails.eventlog.type.spamFilterTrainedInfo' | tr }}</span>
|
||||
<span ng-show="eventlog.type === 'quota'">
|
||||
<span ng-show="eventlog.quotaPercent > 0">{{ 'emails.eventlog.type.overQuotaInfo' | tr: { mailbox: eventlog.mailbox, quotaPercent: eventlog.quotaPercent } }}</span>
|
||||
<span ng-show="eventlog.quotaPercent < 0">{{ 'emails.eventlog.type.underQuotaInfo' | tr: { mailbox: eventlog.mailbox, quotaPercent: -eventlog.quotaPercent } }}</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-show="activity.activeEventLog === eventlog">
|
||||
<td colspan="6">
|
||||
<pre class="eventlog-details">{{ eventlog | json }}</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,83 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/* global $ */
|
||||
/* global angular */
|
||||
|
||||
angular.module('Application').controller('EmailsEventlogController', ['$scope', '$location', '$translate', '$timeout', 'Client', function ($scope, $location, $translate, $timeout, Client) {
|
||||
Client.onReady(function () { if (!Client.getUserInfo().isAtLeastOwner) $location.path('/'); });
|
||||
|
||||
$scope.ready = false;
|
||||
$scope.config = Client.getConfig();
|
||||
$scope.user = Client.getUserInfo();
|
||||
|
||||
$scope.pageItemCount = [
|
||||
{ name: $translate.instant('main.pagination.perPageSelector', { n: 20 }), value: 20 },
|
||||
{ name: $translate.instant('main.pagination.perPageSelector', { n: 50 }), value: 50 },
|
||||
{ name: $translate.instant('main.pagination.perPageSelector', { n: 100 }), value: 100 }
|
||||
];
|
||||
|
||||
$scope.activityTypes = [
|
||||
{ name: 'Bounce', value: 'bounce' },
|
||||
{ name: 'Deferred', value: 'deferred' },
|
||||
{ name: 'Delivered', value: 'delivered' },
|
||||
{ name: 'Denied', value: 'denied' },
|
||||
{ name: 'Queued', value: 'queued' },
|
||||
{ name: 'Quota', value: 'quota' },
|
||||
{ name: 'Received', value: 'received' },
|
||||
{ name: 'Spam', value: 'spam' },
|
||||
];
|
||||
|
||||
$scope.activity = {
|
||||
busy: true,
|
||||
eventLogs: [],
|
||||
activeEventLog: null,
|
||||
currentPage: 1,
|
||||
perPage: 20,
|
||||
pageItems: $scope.pageItemCount[0],
|
||||
selectedTypes: [],
|
||||
search: '',
|
||||
|
||||
refresh: function () {
|
||||
$scope.activity.busy = true;
|
||||
|
||||
var types = $scope.activity.selectedTypes.map(function (a) { return a.value; }).join(',');
|
||||
|
||||
Client.getMailEventLogs($scope.activity.search, types, $scope.activity.currentPage, $scope.activity.pageItems.value, function (error, result) {
|
||||
if (error) return console.error('Failed to fetch mail eventlogs.', error);
|
||||
|
||||
$scope.activity.busy = false;
|
||||
|
||||
$scope.activity.eventLogs = result;
|
||||
});
|
||||
},
|
||||
|
||||
showNextPage: function () {
|
||||
$scope.activity.currentPage++;
|
||||
$scope.activity.refresh();
|
||||
},
|
||||
|
||||
showPrevPage: function () {
|
||||
if ($scope.activity.currentPage > 1) $scope.activity.currentPage--;
|
||||
else $scope.activity.currentPage = 1;
|
||||
$scope.activity.refresh();
|
||||
},
|
||||
|
||||
showEventLogDetails: function (eventLog) {
|
||||
if ($scope.activity.activeEventLog === eventLog) $scope.activity.activeEventLog = null;
|
||||
else $scope.activity.activeEventLog = eventLog;
|
||||
},
|
||||
|
||||
updateFilter: function (fresh) {
|
||||
if (fresh) $scope.activity.currentPage = 1;
|
||||
$scope.activity.refresh();
|
||||
}
|
||||
};
|
||||
|
||||
Client.onReady(function () {
|
||||
$scope.ready = true;
|
||||
|
||||
$scope.activity.refresh();
|
||||
});
|
||||
|
||||
$('.modal-backdrop').remove();
|
||||
}]);
|
||||
@@ -1,81 +0,0 @@
|
||||
<div>
|
||||
<a href="/#/email" class="back-to-view-link"><i class="fas fa-arrow-left"></i> {{ 'email.backAction' | tr }}</a>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
<h1>
|
||||
{{ 'emails.queue.title' | tr }}
|
||||
|
||||
<a class="btn btn-default btn-outline pull-right" href="/logs.html?id=mail" target="_blank">{{ 'main.action.logs' | tr }}</a>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
<div class="maillog-filter">
|
||||
<input class="form-control" style="width: 200px;" placeholder="{{ 'main.searchPlaceholder' | tr }}" type="text" ng-model="queue.search" ng-model-options="{ debounce: 1000 }" ng-change="queue.updateFilter(true)" />
|
||||
<select class="form-control" ng-model="queue.pageItems" ng-options="a.name for a in pageItemCount" ng-change="queue.updateFilter(true)"></select>
|
||||
</div>
|
||||
<div class="pagination pull-right">
|
||||
<button class="btn btn-default btn-outline" ng-click="queue.reload()"><i class="fas fa-sync-alt" ng-class="{ 'fa-spin': queue.busyRefresh }"></i></button>
|
||||
<button class="btn btn-default btn-outline" ng-click="queue.showPrevPage()" ng-disabled="queue.busy || queue.currentPage <= 1"><i class="fa fa-angle-double-left"></i> {{ 'main.pagination.prev' | tr }}</button>
|
||||
<button class="btn btn-default btn-outline" ng-click="queue.showNextPage()" ng-disabled="queue.busy || queue.perPage > queue.items.length">{{ 'main.pagination.next' | tr }} <i class="fa fa-angle-double-right"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
<div class="card card-block" style="max-width: 100%">
|
||||
<div>
|
||||
<center ng-show="queue.busy"><h2><i class="fa fa-circle-notch fa-spin"></i></h2></center>
|
||||
<table ng-hide="queue.busy" class="table table-hover" style="margin: 0; table-layout:fixed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 15%">{{ 'emails.queue.queueTime' | tr }}</th>
|
||||
<th style="width: 25%">{{ 'emails.queue.mailFrom' | tr }}</th>
|
||||
<th style="width: 25%">{{ 'emails.queue.rcptTo' | tr }}</th>
|
||||
<th style="width: 30%">{{ 'emails.queue.details' | tr }}</th>
|
||||
<th class="text-right" style="width: 5%">{{ 'main.actions' | tr }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody ng-hide="queue.items.length">
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">
|
||||
<br>
|
||||
<br>
|
||||
{{ 'emails.queue.empty' | tr }}
|
||||
<br>
|
||||
<br>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody ng-show="queue.items.length" ng-repeat="item in queue.items">
|
||||
<tr ng-click="queue.showItemDetails(item)" class="hand">
|
||||
<td class="no-wrap"><span uib-tooltip="{{ item.queueTime | prettyLongDate }}" class="arrow">{{ item.queueTime | prettyDate }}</span></td>
|
||||
<td class="elide-table-cell">{{ (item.mailFrom | prettyEmailAddresses) || '-' }}</td>
|
||||
<td class="elide-table-cell">{{ (item.rcptTo | prettyEmailAddresses) || '-' }}</td>
|
||||
<td class="elide-table-cell">
|
||||
<span ng-show="item.queueType === 'delivery'">Delivering</span>
|
||||
<span ng-show="item.queueType === 'tempfail'">Retrying in {{ item.nextAttemptTime | prettyFutureDate }}. {{ item.attempts+1 }} attempt(s) so far.</span>
|
||||
<span ng-show="item.queueType === 'load'">Loading</span>
|
||||
</td>
|
||||
<td class="text-right no-wrap">
|
||||
<!-- resend is broken in haraka -->
|
||||
<!-- <button class="btn btn-xs btn-default" ng-click="queue.resend(item)" uib-tooltip="{{ 'emails.queue.resendTooltip' | tr }}"><i class="fa fa-retweet"></i></button> -->
|
||||
<button class="btn btn-xs btn-default" ng-show="item.queueType === 'tempfail'" ng-click="$event.stopPropagation(); queue.discard(item)" uib-tooltip="{{ 'emails.queue.discardTooltip' | tr }}"><i class="fa fa-trash-alt"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-show="queue.activeItem === item">
|
||||
<td colspan="6">
|
||||
<pre class="item-details">{{ item | json }}</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,95 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/* global $ */
|
||||
/* global angular */
|
||||
|
||||
angular.module('Application').controller('EmailsQueueController', ['$scope', '$location', '$translate', '$timeout', 'Client', function ($scope, $location, $translate, $timeout, Client) {
|
||||
Client.onReady(function () { if (!Client.getUserInfo().isAtLeastOwner) $location.path('/'); });
|
||||
|
||||
$scope.ready = false;
|
||||
$scope.config = Client.getConfig();
|
||||
$scope.user = Client.getUserInfo();
|
||||
|
||||
$scope.pageItemCount = [
|
||||
{ name: $translate.instant('main.pagination.perPageSelector', { n: 20 }), value: 20 },
|
||||
{ name: $translate.instant('main.pagination.perPageSelector', { n: 50 }), value: 50 },
|
||||
{ name: $translate.instant('main.pagination.perPageSelector', { n: 100 }), value: 100 }
|
||||
];
|
||||
|
||||
$scope.queue = {
|
||||
busy: true,
|
||||
busyRefresh: false,
|
||||
items: [],
|
||||
activeItem: null,
|
||||
currentPage: 1,
|
||||
perPage: 20,
|
||||
pageItems: $scope.pageItemCount[0],
|
||||
search: '',
|
||||
|
||||
refresh: function (showBusy, callback) {
|
||||
if (showBusy) $scope.queue.busy = true;
|
||||
|
||||
Client.listMailQueue($scope.queue.search, $scope.queue.currentPage, $scope.queue.pageItems.value, function (error, result) {
|
||||
if (showBusy) $scope.queue.busy = false;
|
||||
|
||||
if (error) {
|
||||
console.error('Failed to fetch mail eventlogs.', error);
|
||||
} else {
|
||||
$scope.queue.items = result;
|
||||
}
|
||||
|
||||
if (callback) callback();
|
||||
});
|
||||
},
|
||||
|
||||
reload: function () {
|
||||
$scope.queue.busyRefresh = true;
|
||||
$scope.queue.refresh(true, function () {
|
||||
$scope.queue.busyRefresh = false;
|
||||
});
|
||||
},
|
||||
|
||||
resend: function (item) {
|
||||
Client.resendMailQueueItem(item.file, function (error) {
|
||||
if (error) return console.error('Failed to retry item.', error);
|
||||
$scope.queue.refresh(false);
|
||||
});
|
||||
},
|
||||
|
||||
discard: function (item) {
|
||||
Client.delMailQueueItem(item.file, function (error) {
|
||||
if (error) return console.error('Failed to discard item.', error);
|
||||
$scope.queue.refresh(false);
|
||||
});
|
||||
},
|
||||
|
||||
showNextPage: function () {
|
||||
$scope.queue.currentPage++;
|
||||
$scope.queue.refresh(true);
|
||||
},
|
||||
|
||||
showPrevPage: function () {
|
||||
if ($scope.queue.currentPage > 1) $scope.queue.currentPage--;
|
||||
else $scope.queue.currentPage = 1;
|
||||
$scope.queue.refresh(true);
|
||||
},
|
||||
|
||||
showItemDetails: function (item) {
|
||||
if ($scope.queue.activeItem === item) $scope.queue.activeItem = null;
|
||||
else $scope.queue.activeItem = item;
|
||||
},
|
||||
|
||||
updateFilter: function (fresh) {
|
||||
if (fresh) $scope.queue.currentPage = 1;
|
||||
$scope.queue.refresh(false);
|
||||
}
|
||||
};
|
||||
|
||||
Client.onReady(function () {
|
||||
$scope.ready = true;
|
||||
|
||||
$scope.queue.refresh(true);
|
||||
});
|
||||
|
||||
$('.modal-backdrop').remove();
|
||||
}]);
|
||||
@@ -1,149 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/* global angular */
|
||||
/* global $ */
|
||||
|
||||
angular.module('Application').controller('EventLogController', ['$scope', '$location', '$translate', 'Client', function ($scope, $location, $translate, Client) {
|
||||
Client.onReady(function () { if (!Client.getUserInfo().isAtLeastAdmin) $location.path('/'); });
|
||||
|
||||
$scope.config = Client.getConfig();
|
||||
|
||||
$scope.busy = false;
|
||||
$scope.busyRefresh = false;
|
||||
$scope.eventLogs = [];
|
||||
$scope.activeEventLog = null;
|
||||
|
||||
// TODO sync this with the eventlog filter
|
||||
$scope.actions = [
|
||||
{ name: '-- All app events --', value: 'app.' },
|
||||
{ name: '-- All user events --', value: 'user.' },
|
||||
{ name: 'app.backup', value: 'app.backup' },
|
||||
{ name: 'app.backup.finish', value: 'app.backup.finish' },
|
||||
{ name: 'app.configure', value: 'app.configure' },
|
||||
{ name: 'app.install', value: 'app.install' },
|
||||
{ name: 'app.restore', value: 'app.restore' },
|
||||
{ name: 'app.uninstall', value: 'app.uninstall' },
|
||||
{ name: 'app.update', value: 'app.update' },
|
||||
{ name: 'app.update.finish', value: 'app.update.finish' },
|
||||
{ name: 'app.login', value: 'app.login' },
|
||||
{ name: 'app.oom', value: 'app.oom' },
|
||||
{ name: 'app.down', value: 'app.down' },
|
||||
{ name: 'app.up', value: 'app.up' },
|
||||
{ name: 'app.start', value: 'app.start' },
|
||||
{ name: 'app.stop', value: 'app.stop' },
|
||||
{ name: 'app.restart', value: 'app.restart' },
|
||||
{ name: 'Apptask Crash', value: 'app.task.crash' },
|
||||
{ name: 'backup.cleanup', value: 'backup.cleanup.start' },
|
||||
{ name: 'backup.cleanup.finish', value: 'backup.cleanup.finish' },
|
||||
{ name: 'backup.finish', value: 'backup.finish' },
|
||||
{ name: 'backup.start', value: 'backup.start' },
|
||||
{ name: 'certificate.new', value: 'certificate.new' },
|
||||
{ name: 'certificate.renew', value: 'certificate.renew' },
|
||||
{ name: 'certificate.cleanup', value: 'certificate.cleanup' },
|
||||
{ name: 'cloudron.activate', value: 'cloudron.activate' },
|
||||
{ name: 'cloudron.provision', value: 'cloudron.provision' },
|
||||
{ name: 'cloudron.restore', value: 'cloudron.restore' },
|
||||
{ name: 'cloudron.start', value: 'cloudron.start' },
|
||||
{ name: 'cloudron.update', value: 'cloudron.update' },
|
||||
{ name: 'cloudron.update.finish', value: 'cloudron.update.finish' },
|
||||
{ name: 'dashboard.domain.update', value: 'dashboard.domain.update' },
|
||||
{ name: 'dyndns.update', value: 'dyndns.update' },
|
||||
{ name: 'domain.add', value: 'domain.add' },
|
||||
{ name: 'domain.update', value: 'domain.update' },
|
||||
{ name: 'domain.remove', value: 'domain.remove' },
|
||||
{ name: 'mail.location', value: 'mail.location' },
|
||||
{ name: 'mail.enabled', value: 'mail.enabled' },
|
||||
{ name: 'mail.box.add', value: 'mail.box.add' },
|
||||
{ name: 'mail.box.update', value: 'mail.box.update' },
|
||||
{ name: 'mail.box.remove', value: 'mail.box.remove' },
|
||||
{ name: 'mail.list.add', value: 'mail.list.add' },
|
||||
{ name: 'mail.list.update', value: 'mail.list.update' },
|
||||
{ name: 'mail.list.remove', value: 'mail.list.remove' },
|
||||
{ name: 'service.configure', value: 'service.configure' },
|
||||
{ name: 'service.rebuild', value: 'service.rebuild' },
|
||||
{ name: 'service.restart', value: 'service.restart' },
|
||||
{ name: 'support.ticket', value: 'support.ticket' },
|
||||
{ name: 'support.ssh', value: 'support.ssh' },
|
||||
{ name: 'user.add', value: 'user.add' },
|
||||
{ name: 'user.login', value: 'user.login' },
|
||||
{ name: 'user.login.ghost', value: 'user.login.ghost' },
|
||||
{ name: 'user.logout', value: 'user.logout' },
|
||||
{ name: 'user.remove', value: 'user.remove' },
|
||||
{ name: 'user.transfer', value: 'user.transfer' },
|
||||
{ name: 'user.update', value: 'user.update' },
|
||||
{ name: 'volume.add', value: 'volume.add' },
|
||||
{ name: 'volume.update', value: 'volume.update' },
|
||||
{ name: 'volume.remove', value: 'volume.update' },
|
||||
{ name: 'System Crash', value: 'system.crash' }
|
||||
];
|
||||
|
||||
$scope.pageItemCount = [
|
||||
{ name: $translate.instant('main.pagination.perPageSelector', { n: 20 }), value: 20 },
|
||||
{ name: $translate.instant('main.pagination.perPageSelector', { n: 50 }), value: 50 },
|
||||
{ name: $translate.instant('main.pagination.perPageSelector', { n: 100 }), value: 100 }
|
||||
];
|
||||
|
||||
$scope.currentPage = 1;
|
||||
$scope.pageItems = $scope.pageItemCount[0];
|
||||
$scope.action = '';
|
||||
$scope.selectedActions = [];
|
||||
$scope.search = '';
|
||||
|
||||
function fetchEventLogs(background, callback) {
|
||||
callback = callback || function (error) { if (error) console.error(error); };
|
||||
background = background || false;
|
||||
|
||||
if (!background) $scope.busy = true;
|
||||
|
||||
var actions = $scope.selectedActions.map(function (a) { return a.value; }).join(', ');
|
||||
|
||||
Client.getEventLogs(actions, $scope.search || null, $scope.currentPage, $scope.pageItems.value, function (error, result) {
|
||||
$scope.busy = false;
|
||||
|
||||
if (error) return callback(error);
|
||||
|
||||
$scope.eventLogs = [];
|
||||
result.forEach(function (e) {
|
||||
$scope.eventLogs.push({ raw: e, details: Client.eventLogDetails(e), source: Client.eventLogSource(e) });
|
||||
});
|
||||
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
$scope.refresh = function () {
|
||||
$scope.busyRefresh = true;
|
||||
|
||||
fetchEventLogs(true, function () {
|
||||
$scope.busyRefresh = false;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.showNextPage = function () {
|
||||
$scope.currentPage++;
|
||||
fetchEventLogs();
|
||||
};
|
||||
|
||||
$scope.showPrevPage = function () {
|
||||
if ($scope.currentPage > 1) $scope.currentPage--;
|
||||
else $scope.currentPage = 1;
|
||||
|
||||
fetchEventLogs();
|
||||
};
|
||||
|
||||
$scope.updateFilter = function (fresh) {
|
||||
if (fresh) $scope.currentPage = 1;
|
||||
fetchEventLogs();
|
||||
};
|
||||
|
||||
$scope.showEventLogDetails = function (eventLog) {
|
||||
if ($scope.activeEventLog === eventLog) $scope.activeEventLog = null;
|
||||
else $scope.activeEventLog = eventLog;
|
||||
};
|
||||
|
||||
Client.onReady(function () {
|
||||
fetchEventLogs();
|
||||
});
|
||||
|
||||
$('.modal-backdrop').remove();
|
||||
}]);
|
||||
@@ -1,265 +0,0 @@
|
||||
<!-- Modal sysinfo -->
|
||||
<div class="modal fade" id="sysinfoModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'network.configureIp.title' | tr }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form name="sysinfoForm" role="form" novalidate ng-submit="sysinfo.submit()" autocomplete="off">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<label class="control-label">{{ 'network.ip.provider' | tr }} <sup><a ng-href="https://docs.cloudron.io/networking/#ipv4" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
|
||||
<select class="form-control" ng-model="sysinfo.newProvider" ng-options="a.value as a.name for a in sysinfoProvider"></select>
|
||||
<p class="has-error" ng-show="sysinfo.error.generic">{{ sysinfo.error.generic }}</p>
|
||||
</div>
|
||||
|
||||
<div ng-show="sysinfo.newProvider === 'generic'">
|
||||
{{ 'network.configureIp.providerGenericDescription' | tr }} <sup><a ng-href="https://ipv4.api.cloudron.io/api/v1/helper/public_ip" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup>
|
||||
</div>
|
||||
|
||||
<!-- Fixed -->
|
||||
<div class="form-group" ng-show="sysinfo.newProvider === 'fixed'" ng-class="{ 'has-error': (!sysinfoForm.ipv4.$dirty && sysinfo.error.ipv4) }">
|
||||
<label class="control-label">{{ 'network.ipv4.address' | tr }}</label>
|
||||
<input type="text" class="form-control" ng-model="sysinfo.newIPv4" name="ipv4" ng-disabled="sysinfo.busy" ng-required="sysinfo.newProvider === 'fixed'">
|
||||
<p class="has-error" ng-show="sysinfo.error.ipv4">{{ sysinfo.error.ipv4 }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Network Interface -->
|
||||
<div class="form-group" ng-show="sysinfo.newProvider === 'network-interface'" ng-class="{ 'has-error': (!sysinfoForm.ifname.$dirty && sysinfo.error.ifname) }">
|
||||
<label class="control-label">{{ 'network.ip.interface' | tr }}</label>
|
||||
<p>{{ 'network.ip.interfaceDescription' | tr }} <code>ip -f inet -br addr</code></p>
|
||||
<input type="text" class="form-control" ng-model="sysinfo.newIfname" name="ifname" ng-disabled="sysinfo.busy" ng-required="sysinfo.newProvider === 'network-interface'">
|
||||
<p class="has-error" ng-show="sysinfo.error.ifname">{{ sysinfo.error.ifname }}</p>
|
||||
</div>
|
||||
|
||||
<input class="ng-hide" type="submit" ng-disabled="sysinfoForm.$invalid || sysinfo.busy"/>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
|
||||
<button type="button" class="btn btn-success" ng-click="sysinfo.submit()" ng-disabled="sysinfoForm.$invalid || sysinfo.busy"><i class="fa fa-circle-notch fa-spin" ng-show="sysinfo.busy"></i> {{ 'main.dialog.save' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal block list -->
|
||||
<div class="modal fade" id="blocklistModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'network.firewall.configure.title' | tr }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form name="blocklistChangeForm" role="form" novalidate ng-submit="blocklist.submit()" autocomplete="off">
|
||||
<div class="form-group">
|
||||
<label class="control-label">{{ 'network.firewall.blockedIpRanges' | tr }}</label>
|
||||
<p class="small">{{ 'network.firewall.configure.description' | tr }}</p>
|
||||
<div class="has-error" ng-show="blocklist.error.blocklist">{{ blocklist.error.blocklist }}</div>
|
||||
<textarea ng-model="blocklist.blocklist" placeholder="{{ 'network.firewall.configure.blocklistPlaceholder' | tr }}" name="blocklist" class="form-control" ng-class="{ 'has-error': !blocklistChangeForm.blocklist.$dirty && blocklist.error.blocklist }" rows="4"></textarea>
|
||||
</div>
|
||||
<input class="ng-hide" type="submit"/>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
|
||||
<button type="button" class="btn btn-success" ng-click="blocklist.submit()"><i class="fa fa-circle-notch fa-spin" ng-show="blocklist.busy"></i> {{ 'main.dialog.save' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal IPv6 -->
|
||||
<div class="modal fade" id="ipv6ConfigureModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'network.configureIpv6.title' | tr }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form name="ipv6ConfigureForm" role="form" novalidate ng-submit="ipv6Configure.submit()" autocomplete="off">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<label class="control-label">{{ 'network.ip.provider' | tr }} <sup><a ng-href="https://docs.cloudron.io/networking/#ipv6" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
|
||||
<select class="form-control" ng-model="ipv6Configure.newProvider" ng-options="a.value as a.name for a in ipv6ConfigureProvider"></select>
|
||||
<p class="has-error" ng-show="ipv6Configure.error.generic">{{ ipv6Configure.error.generic }}</p>
|
||||
</div>
|
||||
|
||||
<div ng-show="ipv6Configure.newProvider === 'generic'">
|
||||
{{ 'network.configureIp.providerGenericDescription' | tr }} <sup><a ng-href="https://ipv6.api.cloudron.io/api/v1/helper/public_ip" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup>
|
||||
</div>
|
||||
|
||||
<!-- Fixed -->
|
||||
<div class="form-group" ng-show="ipv6Configure.newProvider === 'fixed'" ng-class="{ 'has-error': (!ipv6ConfigureForm.ipv4.$dirty && ipv6Configure.error.ipv6) }">
|
||||
<label class="control-label">{{ 'network.ipv6.address' | tr }}</label>
|
||||
<input type="text" class="form-control" ng-model="ipv6Configure.newIPv6" name="ipv6" ng-disabled="ipv6Configure.busy" ng-required="ipv6Configure.newProvider === 'fixed'">
|
||||
<p class="has-error" ng-show="ipv6Configure.error.ipv6">{{ ipv6Configure.error.ipv6 }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Network Interface -->
|
||||
<div class="form-group" ng-show="ipv6Configure.newProvider === 'network-interface'" ng-class="{ 'has-error': (!ipv6ConfigureForm.ifname.$dirty && ipv6Configure.error.ifname) }">
|
||||
<label class="control-label">{{ 'network.ip.interface' | tr }}</label>
|
||||
<p>{{ 'network.ip.interfaceDescription' | tr }} <code>ip -f inet6 -br addr</code></p>
|
||||
<input type="text" class="form-control" ng-model="ipv6Configure.newIfname" name="ifname" ng-disabled="ipv6Configure.busy" ng-required="ipv6Configure.newProvider === 'network-interface'">
|
||||
<p class="has-error" ng-show="ipv6Configure.error.ifname">{{ ipv6Configure.error.ifname }}</p>
|
||||
</div>
|
||||
|
||||
<input class="ng-hide" type="submit" ng-disabled="ipv6ConfigureForm.$invalid || ipv6Configure.busy"/>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
|
||||
<button type="button" class="btn btn-success" ng-click="ipv6Configure.submit()" ng-disabled="ipv6ConfigureForm.$invalid || ipv6Configure.busy"><i class="fa fa-circle-notch fa-spin" ng-show="ipv6Configure.busy"></i> {{ 'main.dialog.save' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="text-left">
|
||||
<h1>{{ 'network.title' | tr }}</h1>
|
||||
</div>
|
||||
|
||||
<!-- IPv4 -->
|
||||
<div class="text-left">
|
||||
<h3>{{ 'network.ip.title' | tr }}</h3>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
{{ 'network.ip.description' | tr }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<span class="text-muted">{{ 'network.ip.provider' | tr }}</span>
|
||||
</div>
|
||||
<div class="col-xs-6 text-right">
|
||||
<span>{{ prettyIpProviderName(sysinfo.provider) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<span class="text-muted">{{ 'network.ip.address' | tr }}</span>
|
||||
</div>
|
||||
<div class="col-xs-6 text-right">
|
||||
<span ng-show="sysinfo.ipv4">{{ sysinfo.ipv4 }}</span>
|
||||
<span ng-show="!sysinfo.ipv4">{{ sysinfo.serverIPv4 }} ({{ 'network.ip.detected' | tr }})</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="sysinfo.ifname">
|
||||
<div class="col-xs-6">
|
||||
<span class="text-muted">{{ 'network.ip.interface' | tr }}</span>
|
||||
</div>
|
||||
<div class="col-xs-6 text-right">
|
||||
<span>{{ sysinfo.ifname }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-6 text-right">
|
||||
<button class="btn btn-outline btn-primary pull-right" ng-click="sysinfo.show()">{{ 'network.ip.configure' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Firewall -->
|
||||
<div class="text-left" ng-show="user.isAtLeastOwner">
|
||||
<h3>{{ 'network.firewall.title' | tr }}</h3>
|
||||
</div>
|
||||
|
||||
<div class="card" ng-show="user.isAtLeastOwner">
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<span class="text-muted">{{ 'network.firewall.blockedIpRanges' | tr }}</span>
|
||||
</div>
|
||||
<div class="col-xs-6 text-right">
|
||||
<span>{{ 'network.firewall.blocklist' | tr:{ blockCount: blocklist.currentBlocklistLength } }} <a href="" ng-click="blocklist.show()"><i class="fa fa-edit text-small"></i></a></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- IPv6 -->
|
||||
<div class="text-left">
|
||||
<h3>{{ 'network.ipv6.title' | tr }}</h3>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
{{ 'network.ipv6.description' | tr }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div class="row">
|
||||
<div class="col-xs-2">
|
||||
<span class="text-muted">{{ 'network.ip.provider' | tr }}</span>
|
||||
</div>
|
||||
<div class="col-xs-10 text-right">
|
||||
<span>{{ prettyIpProviderName(ipv6Configure.provider) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="ipv6Configure.provider !== 'noop'">
|
||||
<div class="col-xs-2">
|
||||
<span class="text-muted">{{ 'network.ip.address' | tr }}</span>
|
||||
</div>
|
||||
<div class="col-xs-10 text-right">
|
||||
<span ng-show="ipv6Configure.ipv6">{{ ipv6Configure.ipv6 }}</span>
|
||||
<span ng-show="!ipv6Configure.ipv6 && ipv6Configure.serverIPv6">{{ ipv6Configure.serverIPv6 }} ({{ 'network.ip.detected' | tr }})</span>
|
||||
<span ng-show="ipv6Configure.displayError" class="text-danger">{{ ipv6Configure.displayError }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="ipv6Configure.ifname">
|
||||
<div class="col-xs-6">
|
||||
<span class="text-muted">{{ 'network.ip.interface' | tr }}</span>
|
||||
</div>
|
||||
<div class="col-xs-6 text-right">
|
||||
<span>{{ ipv6Configure.ifname }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-6 text-right">
|
||||
<button class="btn btn-outline btn-primary pull-right" ng-click="ipv6Configure.show()">{{ 'network.ip.configure' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-left">
|
||||
<h3>{{ 'network.dyndns.title' | tr }}</h3>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p>{{ 'network.dyndns.description' | tr }}</p>
|
||||
<p class="text-danger" ng-show="dyndnsConfigure.error"><br/>{{ dyndnsConfigure.error }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-2" style="padding-top: 12px;">
|
||||
<i class="fa fa-circle" ng-class="{ 'status-active': dyndnsConfigure.isEnabled, 'status-inactive': !dyndnsConfigure.isEnabled }"></i> {{ dyndnsConfigure.isEnabled ? 'main.statusEnabled' : 'main.statusDisabled' | tr }}
|
||||
</div>
|
||||
<div class="col-md-10 text-right">
|
||||
<button class="btn btn-outline btn-primary" ng-hide="dyndnsConfigure.isEnabled" ng-click="dyndnsConfigure.setEnabled(true)" ng-disabled="dyndnsConfigure.busy"><i class="fa fa-circle-notch fa-spin" ng-show="dyndnsConfigure.busy"></i> {{ 'main.enableAction' | tr }}</button>
|
||||
<button class="btn btn-outline btn-danger" ng-show="dyndnsConfigure.isEnabled" ng-click="dyndnsConfigure.setEnabled(false)" ng-disabled="dyndnsConfigure.busy"><i class="fa fa-circle-notch fa-spin" ng-show="dyndnsConfigure.busy"></i> {{ 'main.disableAction' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,209 +0,0 @@
|
||||
|
||||
<!-- Modal client add -->
|
||||
<div class="modal fade" id="clientAddModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'oidc.newClientDialog.title' | tr }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{{ 'oidc.newClientDialog.description' | tr }}
|
||||
<br/>
|
||||
<br/>
|
||||
<form name="clientAddForm" role="form" novalidate ng-submit="clientAdd.submit()" autocomplete="off">
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="clientName">{{ 'oidc.client.name' | tr }}</label>
|
||||
<input type="text" id="clientName" class="form-control" name="clientName" ng-model="clientAdd.name" autofocus required/>
|
||||
</div>
|
||||
<div class="form-group" ng-class="{ 'has-error': clientAdd.error.id }">
|
||||
<label class="control-label" for="clientId">{{ 'oidc.client.id' | tr }}</label>
|
||||
<input type="text" id="clientId" class="form-control" name="clientId" ng-model="clientAdd.id" required/>
|
||||
<div class="control-label" ng-show="clientAdd.error.id">
|
||||
<small>{{ clientAdd.error.id }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="clientSecret">{{ 'oidc.client.secret' | tr }}</label>
|
||||
<input type="text" id="clientSecret" class="form-control" name="clientSecret" ng-model="clientAdd.secret" required/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="loginRedirectUri">{{ 'oidc.client.loginRedirectUri' | tr }}</label>
|
||||
<input type="text" id="loginRedirectUri" class="form-control" name="loginRedirectUri" ng-model="clientAdd.loginRedirectUri" required/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="logoutRedirectUri">{{ 'oidc.client.logoutRedirectUri' | tr }}</label>
|
||||
<input type="url" id="logoutRedirectUri" class="form-control" name="logoutRedirectUri" ng-model="clientAdd.logoutRedirectUri"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">{{ 'oidc.client.signingAlgorithm' | tr }}</label>
|
||||
<div class="control-label">
|
||||
<select class="form-control" ng-model="clientAdd.tokenSignatureAlgorithm">
|
||||
<option value="RS256">RS256</option>
|
||||
<option value="EdDSA">EdDSA</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<input class="ng-hide" type="submit" ng-disabled="clientAddForm.$invalid"/>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
|
||||
<button type="button" class="btn btn-success" ng-click="clientAdd.submit()" ng-disabled="clientAddForm.$invalid || clientAdd.busy">
|
||||
<i class="fa fa-circle-notch fa-spin" ng-show="clientAdd.busy"></i> {{ 'oidc.newClientDialog.createAction' | tr }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal client edit -->
|
||||
<div class="modal fade" id="clientEditModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'oidc.editClientDialog.title' | tr:{ client: clientEdit.id } }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form name="clientEditForm" role="form" novalidate ng-submit="clientEdit.submit()" autocomplete="off">
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="inputEditClientName">{{ 'oidc.client.name' | tr }}</label>
|
||||
<input type="text" id="inputEditClientName" class="form-control" name="clientName" ng-model="clientEdit.name" autofocus required/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="inputEditClientSecret">{{ 'oidc.client.secret' | tr }}</label>
|
||||
<input type="text" id="inputEditClientSecret" class="form-control" name="clientSecret" ng-model="clientEdit.secret" required/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="inputEditLoginRedirectUri">{{ 'oidc.client.loginRedirectUri' | tr }}</label>
|
||||
<input type="text" id="inputEditLoginRedirectUri" class="form-control" name="loginRedirectUri" ng-model="clientEdit.loginRedirectUri" required/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="inputEditLogoutRedirectUri">{{ 'oidc.client.logoutRedirectUri' | tr }}</label>
|
||||
<input type="url" id="inputEditLogoutRedirectUri" class="form-control" name="logoutRedirectUri" ng-model="clientEdit.logoutRedirectUri"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">{{ 'oidc.client.signingAlgorithm' | tr }}</label>
|
||||
<div class="control-label">
|
||||
<select class="form-control" ng-model="clientEdit.tokenSignatureAlgorithm">
|
||||
<option value="RS256">RS256</option>
|
||||
<option value="EdDSA">EdDSA</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<input class="ng-hide" type="submit" ng-disabled="clientEditForm.$invalid"/>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
|
||||
<button type="button" class="btn btn-success" ng-click="clientEdit.submit()" ng-disabled="clientEditForm.$invalid || clientEdit.busy">
|
||||
<i class="fa fa-circle-notch fa-spin" ng-show="clientEdit.busy"></i> {{ 'main.dialog.save' | tr }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal client delete -->
|
||||
<div class="modal fade" id="clientDeleteModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'oidc.deleteClientDialog.title' | tr:{ client: deleteClient.id } }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>{{ 'oidc.deleteClientDialog.description' | tr }}</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
|
||||
<button type="button" class="btn btn-danger" ng-click="deleteClient.submit()" ng-disabled="deleteClient.busy"><i class="fa fa-circle-notch fa-spin" ng-show="deleteClient.busy"></i> {{ 'main.dialog.delete' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<div class="text-left">
|
||||
<h1>{{ 'oidc.title' | tr }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="grid-item-top">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table width="100%">
|
||||
<tr>
|
||||
<td class="text-muted" style="vertical-align: top;">{{ 'oidc.env.discoveryUrl' | tr }}</td>
|
||||
<td class="text-right" style="vertical-align: top;" ng-click-select>https://{{ config.adminFqdn }}/.well-known/openid-configuration</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted" style="vertical-align: top;">{{ 'oidc.env.authEndpoint' | tr }}</td>
|
||||
<td class="text-right" style="vertical-align: top;" ng-click-select>https://{{ config.adminFqdn }}/openid/auth</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted" style="vertical-align: top;">{{ 'oidc.env.tokenEndpoint' | tr }}</td>
|
||||
<td class="text-right" style="vertical-align: top;" ng-click-select>https://{{ config.adminFqdn }}/openid/token</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted" style="vertical-align: top;">{{ 'oidc.env.keysEndpoint' | tr }}</td>
|
||||
<td class="text-right" style="vertical-align: top;" ng-click-select>https://{{ config.adminFqdn }}/openid/jwks</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted" style="vertical-align: top;">{{ 'oidc.env.profileEndpoint' | tr }}</td>
|
||||
<td class="text-right" style="vertical-align: top;" ng-click-select>https://{{ config.adminFqdn }}/openid/me</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted" style="vertical-align: top;">{{ 'oidc.env.logoutUrl' | tr }}</td>
|
||||
<td class="text-right" style="vertical-align: top;" ng-click-select>https://{{ config.adminFqdn }}/openid/session/end</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="text-left">
|
||||
<h3>{{ 'oidc.clients.title' | tr }} <button class="btn btn-primary btn-sm pull-right" ng-click="clientAdd.show()"><i class="fa fa-plus"></i> {{ 'oidc.clients.newClient' | tr }}</button></h3>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="grid-item-top">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 33%">{{ 'oidc.client.name' | tr }}</th>
|
||||
<th style="width: 33%">{{ 'oidc.client.id' | tr }}</th>
|
||||
<th style="width: 33%">{{ 'oidc.client.signingAlgorithm' | tr }}</th>
|
||||
<th style="width: 10%" class="text-right">{{ 'main.actions' | tr }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-show="clients.length === 0">
|
||||
<td colspan="3" class="text-center">{{ 'oidc.clients.empty' | tr }}</td>
|
||||
</tr>
|
||||
<tr ng-repeat="client in clients">
|
||||
<td class="text-left elide-table-cell hand" ng-click="clientEdit.show(client)">
|
||||
{{ client.name }}
|
||||
</td>
|
||||
<td class="text-left elide-table-cell hand" ng-click="clientEdit.show(client)">
|
||||
{{ client.id }}
|
||||
</td>
|
||||
<td class="text-left elide-table-cell hand" ng-click="clientEdit.show(client)">
|
||||
{{ client.tokenSignatureAlgorithm }}
|
||||
</td>
|
||||
<td class="text-right no-wrap" style="vertical-align: bottom">
|
||||
<button class="btn btn-xs btn-danger" ng-click="deleteClient.show(client)" uib-tooltip="Delete"><i class="far fa-trash-alt"></i></button>
|
||||
<button class="btn btn-xs btn-default" ng-click="clientEdit.show(client)" uib-tooltip="Edit"><i class="far fa fa-pencil-alt"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,153 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/* global angular */
|
||||
/* global $ */
|
||||
|
||||
angular.module('Application').controller('OidcController', ['$scope', '$location', 'Client', function ($scope, $location, Client) {
|
||||
Client.onReady(function () { if (!Client.getUserInfo().isAtLeastAdmin) $location.path('/'); });
|
||||
|
||||
$scope.user = Client.getUserInfo();
|
||||
$scope.config = Client.getConfig();
|
||||
$scope.clients = [];
|
||||
|
||||
$scope.refreshClients = function () {
|
||||
Client.getOidcClients(function (error, result) {
|
||||
if (error) return console.error('Failed to load oidc clients', error);
|
||||
|
||||
$scope.clients = result;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.clientAdd = {
|
||||
busy: false,
|
||||
error: {},
|
||||
id: '',
|
||||
name: '',
|
||||
secret: '',
|
||||
loginRedirectUri: '',
|
||||
logoutRedirectUri: '',
|
||||
tokenSignatureAlgorithm: '',
|
||||
|
||||
show: function () {
|
||||
$scope.clientAdd.id = '';
|
||||
$scope.clientAdd.secret = '';
|
||||
$scope.clientAdd.name = '';
|
||||
$scope.clientAdd.loginRedirectUri = '';
|
||||
$scope.clientAdd.logoutRedirectUri = '';
|
||||
$scope.clientAdd.tokenSignatureAlgorithm = 'RS256';
|
||||
$scope.clientAdd.busy = false;
|
||||
$scope.clientAdd.error = null;
|
||||
$scope.clientAddForm.$setPristine();
|
||||
|
||||
$('#clientAddModal').modal('show');
|
||||
},
|
||||
|
||||
submit: function () {
|
||||
$scope.clientAdd.busy = true;
|
||||
$scope.clientAdd.error = {};
|
||||
|
||||
Client.addOidcClient($scope.clientAdd.id, $scope.clientAdd.name, $scope.clientAdd.secret, $scope.clientAdd.loginRedirectUri, $scope.clientAdd.logoutRedirectUri, $scope.clientAdd.tokenSignatureAlgorithm, function (error) {
|
||||
if (error) {
|
||||
if (error.statusCode === 409) {
|
||||
$scope.clientAdd.error.id = 'Client ID already exists';
|
||||
$('#clientId').focus();
|
||||
} else {
|
||||
console.error('Unable to add openid client.', error);
|
||||
}
|
||||
|
||||
$scope.clientAdd.busy = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.refreshClients();
|
||||
$scope.clientAdd.busy = false;
|
||||
|
||||
$('#clientAddModal').modal('hide');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.clientEdit = {
|
||||
busy: false,
|
||||
error: {},
|
||||
id: '',
|
||||
name: '',
|
||||
secret: '',
|
||||
loginRedirectUri: '',
|
||||
logoutRedirectUri: '',
|
||||
tokenSignatureAlgorithm: '',
|
||||
|
||||
show: function (client) {
|
||||
$scope.clientEdit.id = client.id;
|
||||
$scope.clientEdit.name = client.name;
|
||||
$scope.clientEdit.secret = client.secret;
|
||||
$scope.clientEdit.loginRedirectUri = client.loginRedirectUri;
|
||||
$scope.clientEdit.logoutRedirectUri = client.logoutRedirectUri;
|
||||
$scope.clientEdit.tokenSignatureAlgorithm = client.tokenSignatureAlgorithm;
|
||||
$scope.clientEdit.busy = false;
|
||||
$scope.clientEdit.error = null;
|
||||
$scope.clientEditForm.$setPristine();
|
||||
|
||||
$('#clientEditModal').modal('show');
|
||||
},
|
||||
|
||||
submit: function () {
|
||||
$scope.clientEdit.busy = true;
|
||||
$scope.clientEdit.error = {};
|
||||
|
||||
Client.updateOidcClient($scope.clientEdit.id, $scope.clientEdit.name, $scope.clientEdit.secret, $scope.clientEdit.loginRedirectUri, $scope.clientEdit.logoutRedirectUri, $scope.clientEdit.tokenSignatureAlgorithm, function (error) {
|
||||
if (error) {
|
||||
console.error('Unable to edit openid client.', error);
|
||||
|
||||
$scope.clientEdit.busy = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.refreshClients();
|
||||
$scope.clientEdit.busy = false;
|
||||
|
||||
$('#clientEditModal').modal('hide');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.deleteClient = {
|
||||
busy: false,
|
||||
error: {},
|
||||
id: '',
|
||||
|
||||
show: function (client) {
|
||||
$scope.deleteClient.busy = false;
|
||||
$scope.deleteClient.id = client.id;
|
||||
|
||||
$('#clientDeleteModal').modal('show');
|
||||
},
|
||||
|
||||
submit: function () {
|
||||
Client.delOidcClient($scope.deleteClient.id, function (error) {
|
||||
$scope.deleteClient.busy = false;
|
||||
|
||||
if (error) return console.error('Failed to delete openid client', error);
|
||||
|
||||
$scope.refreshClients();
|
||||
|
||||
$('#clientDeleteModal').modal('hide');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Client.onReady(function () {
|
||||
$scope.refreshClients();
|
||||
});
|
||||
|
||||
// setup all the dialog focus handling
|
||||
['clientAddModal', 'clientEditmodal'].forEach(function (id) {
|
||||
$('#' + id).on('shown.bs.modal', function () {
|
||||
$(this).find('[autofocus]:first').focus();
|
||||
});
|
||||
});
|
||||
|
||||
$('.modal-backdrop').remove();
|
||||
}]);
|
||||
@@ -1,102 +0,0 @@
|
||||
<div class="container">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h1>
|
||||
{{ 'system.title' | tr }}
|
||||
<a class="btn btn-default pull-right" href="/logs.html?id=box" target="_blank">{{ 'main.action.logs' | tr }}</a>
|
||||
<button class="btn btn-default pull-right" ng-click="$parent.reboot.show()">{{ 'main.action.reboot' | tr }}</button>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
|
||||
<h3 class="graphs-toolbar">
|
||||
Graphs
|
||||
<div class="graphs-toolbar-actions">
|
||||
<button class="btn btn-sm btn-default" style="margin-right: 5px;" ng-click="graphs.refresh()" ng-disabled="graphs.busy"><i class="fas fa-sync-alt" ng-class="{ 'fa-spin': graphs.busy }"></i></button>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-sm btn-primary dropdown-toggle" type="button" data-toggle="dropdown">
|
||||
{{ graphs.period | trKeyFromPeriod | tr }}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="" ng-click="graphs.setPeriod(6)">{{ 6 | trKeyFromPeriod | tr }}</a></li>
|
||||
<li><a href="" ng-click="graphs.setPeriod(12)">{{ 12 | trKeyFromPeriod | tr }}</a></li>
|
||||
<li><a href="" ng-click="graphs.setPeriod(24)">{{ 24 | trKeyFromPeriod | tr }}</a></li>
|
||||
<li><a href="" ng-click="graphs.setPeriod(24*7)">{{ 24*7 | trKeyFromPeriod | tr }}</a></li>
|
||||
<li><a href="" ng-click="graphs.setPeriod(24*30)">{{ 24*30 | trKeyFromPeriod | tr }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</h3>
|
||||
|
||||
<div class="card" style="min-height: 300px;">
|
||||
<label>{{ 'system.cpuUsage.title' | tr }}</label>
|
||||
<canvas id="graphsCPUChart" style="width: 100%;"></canvas>
|
||||
<div class="text-muted text-center text-small">{{ 'system.cpuUsage.graphSubtext' | tr:{ threshold: '20 %'} }}</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<label>{{ 'system.systemMemory.title' | tr }}</label>
|
||||
<canvas id="graphsSystemMemoryChart" style="width: 100%;"></canvas>
|
||||
<div class="text-muted text-center text-small">{{ 'system.systemMemory.graphSubtext' | tr:{ threshold: '1 GiB'} }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<h3 class="graphs-toolbar">
|
||||
{{ 'system.diskUsage.title' | tr }}
|
||||
<span class="small disks-last-updated" ng-show="!disks.busy && disks.ts">Last updated: {{ disks.ts | prettyDate }}</span>
|
||||
<div class="graphs-toolbar-actions">
|
||||
<button class="btn btn-sm btn-default" style="margin-right: 5px;" ng-click="disks.refresh()" ng-disabled="disks.busy || disks.busyRefresh"><i class="fas fa-sync-alt" ng-class="{ 'fa-spin': disks.busyRefresh }"></i></button>
|
||||
</div>
|
||||
</h3>
|
||||
|
||||
<div class="card">
|
||||
<div class="row" ng-show="disks.busy">
|
||||
<div class="col-md-12 text-center">
|
||||
<h2 style="margin: 60px 0;"><i class="fa fa-circle-notch fa-spin"></i></h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" ng-show="!disks.busy && !disks.ts">
|
||||
<div class="col-md-12 text-center">
|
||||
<button class="btn btn-primary" style="margin: 60px 0;" ng-click="disks.refresh()" ng-disabled="disks.busyRefresh"><i class="fas fa-sync-alt fa-spin" ng-show="disks.busyRefresh"></i> Analyze Disk</button>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-hide="disks.busy" class="ng-hide">
|
||||
<div class="row" ng-repeat="disk in disks.disks" style="margin-bottom: 20px;">
|
||||
<div class="col-md-12">
|
||||
<div style="display: flex; align-items: baseline; justify-content: space-between;">
|
||||
<h3 class="no-wrap" style="font-size: 20px;" ng-bind-html="'system.diskUsage.mountedAt' | tr:{ filesystem: disk.filesystem, mountpoint: disk.mountpoint }"></h3>
|
||||
<div class="text-muted" style="white-space:nowrap;" ng-show="disk.available && disk.size" ng-bind-html="'system.diskUsage.usedInfo' | tr:{ used: (disk.used | prettyDiskSize), size: (disk.size | prettyDiskSize) }"></div>
|
||||
<div class="text-muted" style="white-space:nowrap;" ng-hide="disk.available && disk.size">{{ 'system.diskUsage.notAvailableYet' | tr }}</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" ng-repeat="content in disk.contents" style="width: {{ content.usage / disk.size * 100 }}%; background-color: {{ content.color }};" uib-tooltip="{{ content.label + ' ' + (content.usage | prettyDiskSize) }}"></div>
|
||||
<div class="text-center text-muted" style="font-size: 12px; line-height: 20px;">{{ disk.available | prettyDiskSize }}</div>
|
||||
</div>
|
||||
<div class="text-right text-muted" style="margin-top: 10px;">{{ 'system.diskUsage.diskSpeed' | tr:{ speed: disk.speed } }}</div>
|
||||
<p ng-hide="disk.volume">{{ 'system.diskUsage.diskContent' | tr }}:</p>
|
||||
<p ng-show="disk.volume" ng-bind-html="'system.diskUsage.volumeContent' | tr:{ name: disk.volume.name }"></p>
|
||||
<div ng-repeat="content in disk.contents" class="disk-content">
|
||||
<span class="color-indicator" style="background-color: {{ content.color }};"> </span>
|
||||
<span ng-show="content.type === 'standard'">{{ content.label || content.id }}</span>
|
||||
<span ng-show="content.type === 'swap'">{{ content.id }}</span>
|
||||
<span ng-show="content.type === 'app'">
|
||||
<a href="https://{{ content.app.fqdn }}" target="_blank" ng-hide="content.uninstalled">{{ content.app.label || content.app.fqdn }}</a>
|
||||
<span ng-show="content.uninstalled">{{ 'system.diskUsage.uninstalledApp' | tr }}</span>
|
||||
</span>
|
||||
<span ng-show="content.type === 'volume'"><a href="/#/volumes">{{ content.volume.name }}</a></span>
|
||||
<small class="text-muted">{{ content.usage | prettyDiskSize }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -1,342 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/* global angular */
|
||||
/* global $ */
|
||||
/* global Chart */
|
||||
|
||||
angular.module('Application').controller('SystemController', ['$scope', '$location', '$timeout', 'Client', function ($scope, $location, $timeout, Client) {
|
||||
Client.onReady(function () { if (!Client.getUserInfo().isAtLeastAdmin) $location.path('/'); });
|
||||
|
||||
$scope.config = Client.getConfig();
|
||||
$scope.memory = null;
|
||||
$scope.volumesById = {};
|
||||
|
||||
// https://stackoverflow.com/questions/1484506/random-color-generator
|
||||
function rainbow(numOfSteps, step) {
|
||||
// This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distinguishable vibrant markers in Google Maps and other apps.
|
||||
// Adam Cole, 2011-Sept-14
|
||||
// HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
|
||||
var r, g, b;
|
||||
var h = step / numOfSteps;
|
||||
var i = ~~(h * 6);
|
||||
var f = h * 6 - i;
|
||||
var q = 1 - f;
|
||||
switch(i % 6){
|
||||
case 0: r = 1; g = f; b = 0; break;
|
||||
case 1: r = q; g = 1; b = 0; break;
|
||||
case 2: r = 0; g = 1; b = f; break;
|
||||
case 3: r = 0; g = q; b = 1; break;
|
||||
case 4: r = f; g = 0; b = 1; break;
|
||||
case 5: r = 1; g = 0; b = q; break;
|
||||
}
|
||||
var c = '#' + ('00' + (~ ~(r * 255)).toString(16)).slice(-2) + ('00' + (~ ~(g * 255)).toString(16)).slice(-2) + ('00' + (~ ~(b * 255)).toString(16)).slice(-2);
|
||||
return (c);
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array
|
||||
function shuffle(a) {
|
||||
for (let i = a.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[a[i], a[j]] = [a[j], a[i]];
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
var colorIndex = 0;
|
||||
var colors = [];
|
||||
function resetColors(n) {
|
||||
colorIndex = 0;
|
||||
colors = [];
|
||||
for (var i = 0; i < n; i++) colors.push(rainbow(n, i));
|
||||
shuffle(colors);
|
||||
}
|
||||
|
||||
function getNextColor() {
|
||||
return colors[colorIndex++];
|
||||
}
|
||||
|
||||
$scope.disks = {
|
||||
busy: true,
|
||||
busyRefresh: false,
|
||||
ts: 0,
|
||||
taskId: '',
|
||||
disks: [],
|
||||
|
||||
show: function () {
|
||||
Client.diskUsage(function (error, result) {
|
||||
if (error) return console.error('Failed to refresh disk usage.', error);
|
||||
|
||||
if (!result.usage) {
|
||||
$scope.disks.busy = false;
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.disks.ts = result.usage.ts;
|
||||
|
||||
// [ { filesystem, type, size, used, available, capacity, mountpoint }]
|
||||
$scope.disks.disks = Object.keys(result.usage.disks).map(function (k) { return result.usage.disks[k]; });
|
||||
|
||||
$scope.disks.disks.forEach(function (disk) {
|
||||
var usageOther = disk.used;
|
||||
|
||||
resetColors(disk.contents.length);
|
||||
|
||||
// if this disk is a volume amend it and remove it from contents
|
||||
disk.contents.forEach(function (content) { if (content.path === disk.mountpoint) disk.volume = $scope.volumesById[content.id]; });
|
||||
disk.contents = disk.contents.filter(function (content) { return content.path !== disk.mountpoint; });
|
||||
|
||||
disk.contents.forEach(function (content) {
|
||||
content.color = getNextColor();
|
||||
|
||||
if (content.type === 'app') {
|
||||
content.app = Client.getInstalledAppsByAppId()[content.id];
|
||||
if (!content.app) content.uninstalled = true;
|
||||
}
|
||||
if (content.type === 'volume') content.volume = $scope.volumesById[content.id];
|
||||
|
||||
usageOther -= content.usage;
|
||||
});
|
||||
|
||||
disk.contents.sort(function (x, y) { return y.usage - x.usage; }); // sort by usage
|
||||
|
||||
if ($scope.disks.disks[0] === disk) { // the root mount point is the first disk. keep this 'contains' in the end
|
||||
disk.contents.push({
|
||||
type: 'standard',
|
||||
label: 'Everything else (Ubuntu, etc)',
|
||||
id: 'other',
|
||||
color: '#555555',
|
||||
usage: usageOther
|
||||
});
|
||||
} else {
|
||||
disk.contents.push({
|
||||
type: 'standard',
|
||||
label: 'Used',
|
||||
id: 'other',
|
||||
color: '#555555',
|
||||
usage: usageOther
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.disks.busy = false;
|
||||
});
|
||||
},
|
||||
|
||||
checkStatus: function () {
|
||||
Client.getLatestTaskByType(TASK_TYPES.TASK_UPDATE_DISK_USAGE, function (error, task) {
|
||||
if (error) return console.error(error);
|
||||
|
||||
if (!task) return;
|
||||
|
||||
$scope.disks.taskId = task.id;
|
||||
$scope.disks.busyRefresh = true;
|
||||
$scope.disks.updateStatus();
|
||||
});
|
||||
},
|
||||
|
||||
updateStatus: function () {
|
||||
Client.getTask($scope.disks.taskId, function (error, data) {
|
||||
if (error) return $timeout($scope.disks.updateStatus, 3000);
|
||||
|
||||
if (!data.active) {
|
||||
$scope.disks.busyRefresh = false;
|
||||
$scope.disks.taskId = '';
|
||||
$scope.disks.show();
|
||||
return;
|
||||
}
|
||||
|
||||
$timeout($scope.disks.updateStatus, 3000);
|
||||
});
|
||||
},
|
||||
|
||||
refresh: function () {
|
||||
$scope.disks.busyRefresh = true;
|
||||
|
||||
Client.refreshDiskUsage(function (error, taskId) {
|
||||
if (error) {
|
||||
$scope.disks.busyRefresh = false;
|
||||
return console.error('Failed to refresh disk usage.', error);
|
||||
}
|
||||
|
||||
$scope.disks.taskId = taskId;
|
||||
$timeout($scope.disks.updateStatus, 3000);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.graphs = {
|
||||
busy: false,
|
||||
period: 6,
|
||||
memoryChart: null,
|
||||
diskChart: null,
|
||||
|
||||
setPeriod: function (hours) {
|
||||
$scope.graphs.period = hours;
|
||||
$scope.graphs.refresh();
|
||||
},
|
||||
|
||||
refresh: function () {
|
||||
$scope.graphs.busy = true;
|
||||
|
||||
Client.getSystemGraphs($scope.graphs.period * 60, function (error, result) {
|
||||
if (error) return console.error('Failed to fetch system graphs:', error);
|
||||
|
||||
var cpuCount = result.cpuCount;
|
||||
|
||||
// in minutes
|
||||
var timePeriod = $scope.graphs.period * 60;
|
||||
|
||||
// keep in sync with graphs.js
|
||||
var timeBucketSizeMinutes = timePeriod > (24 * 60) ? (6*60) : 5;
|
||||
var steps = Math.floor(timePeriod/timeBucketSizeMinutes);
|
||||
|
||||
var labels = new Array(steps).fill(0);
|
||||
labels = labels.map(function (v, index) {
|
||||
var dateTime = new Date(Date.now() - ((timePeriod - (index * timeBucketSizeMinutes)) * 60 * 1000));
|
||||
|
||||
if ($scope.graphs.period > 24) {
|
||||
return dateTime.toLocaleDateString();
|
||||
} else {
|
||||
return dateTime.toLocaleTimeString();
|
||||
}
|
||||
});
|
||||
|
||||
function fillGraph(canvasId, contents, chartPropertyName, divisor, max, format, formatDivisor) {
|
||||
if (!contents || !contents[0]) return; // no data available yet
|
||||
|
||||
var datasets = [];
|
||||
|
||||
resetColors(contents.length);
|
||||
contents.forEach(function (content, index) {
|
||||
|
||||
// fill holes with previous value
|
||||
var cur = 0;
|
||||
content.data.forEach(function (d) {
|
||||
if (d[0] === null) d[0] = cur;
|
||||
else cur = d[0];
|
||||
});
|
||||
|
||||
var datapoints = Array(steps).map(function () { return '0'; });
|
||||
|
||||
// walk backwards and fill up the datapoints
|
||||
content.data.reverse().forEach(function (d, index) {
|
||||
datapoints[datapoints.length-1-index] = (d[0] / divisor).toFixed(2);
|
||||
});
|
||||
|
||||
var color = index === 0 ? '#2196F3' : getNextColor();
|
||||
datasets.push({
|
||||
label: content.label,
|
||||
backgroundColor: color + '4F',
|
||||
borderColor: color, // FIXME give real distinct colors
|
||||
borderWidth: 1,
|
||||
radius: 0,
|
||||
data: datapoints,
|
||||
cubicInterpolationMode: 'monotone',
|
||||
tension: 0.4
|
||||
});
|
||||
});
|
||||
|
||||
var graphData = {
|
||||
labels: labels,
|
||||
datasets: datasets
|
||||
};
|
||||
|
||||
var options = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
aspectRatio: 2.5,
|
||||
animation: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
intersect: false,
|
||||
mode: 'index',
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
ticks: { autoSkipPadding: 50, maxRotation: 0 }
|
||||
},
|
||||
y: {
|
||||
ticks: { maxTicksLimit: 6 },
|
||||
min: 0,
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (format) options.scales.y.ticks.callback = function (value) { return (formatDivisor ? (value/formatDivisor).toFixed(0) : value) + ' ' + format; };
|
||||
if (max) options.scales.y.max = max;
|
||||
|
||||
var ctx = $(canvasId).get(0).getContext('2d');
|
||||
|
||||
if ($scope.graphs[chartPropertyName]) $scope.graphs[chartPropertyName].destroy();
|
||||
$scope.graphs[chartPropertyName] = new Chart(ctx, { type: 'line', data: graphData, options: options });
|
||||
}
|
||||
|
||||
var cpuThreshold = 20;
|
||||
var appsWithHighCPU = Object.keys(result.apps).map(function (appId) {
|
||||
result.apps[appId].id = appId;
|
||||
|
||||
var app = Client.getInstalledAppsByAppId()[appId];
|
||||
if (!app) result.apps[appId].label = appId;
|
||||
else result.apps[appId].label = app.label || app.fqdn;
|
||||
|
||||
return result.apps[appId];
|
||||
}).filter(function (app) {
|
||||
if (!app.cpu) return false; // not sure why we get empty objects
|
||||
return app.cpu.some(function (d) { return d[0] > cpuThreshold; });
|
||||
}).map(function (app) {
|
||||
return { data: app.cpu, label: app.label };
|
||||
});
|
||||
|
||||
var memoryThreshold = 1024 * 1024 * 1024;
|
||||
var appsWithHighMemory = Object.keys(result.apps).map(function (appId) {
|
||||
result.apps[appId].id = appId;
|
||||
|
||||
var app = Client.getInstalledAppsByAppId()[appId];
|
||||
if (!app) result.apps[appId].label = appId;
|
||||
else result.apps[appId].label = app.label || app.fqdn;
|
||||
|
||||
return result.apps[appId];
|
||||
}).filter(function (app) {
|
||||
if (!app.memory) return false; // not sure why we get empty objects
|
||||
return app.memory.some(function (d) { return d[0] > memoryThreshold; });
|
||||
}).map(function (app) {
|
||||
return { data: app.memory, label: app.label };
|
||||
});
|
||||
|
||||
fillGraph('#graphsCPUChart', [{ data: result.cpu, label: 'CPU' }].concat(appsWithHighCPU), 'cpuChart', 1, cpuCount * 100, '%');
|
||||
fillGraph('#graphsSystemMemoryChart', [{ data: result.memory, label: 'Memory' }].concat(appsWithHighMemory), 'memoryChart', 1024 * 1024, Number.parseInt($scope.memory.memory / 1024 / 1024), 'GiB', 1024);
|
||||
|
||||
$scope.graphs.busy = false;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Client.onReady(function () {
|
||||
Client.memory(function (error, memory) {
|
||||
if (error) console.error(error);
|
||||
|
||||
$scope.memory = memory;
|
||||
|
||||
Client.getVolumes(function (error, volumes) {
|
||||
if (error) return console.error(error);
|
||||
|
||||
$scope.volumesById = {};
|
||||
volumes.forEach(function (v) { $scope.volumesById[v.id] = v; });
|
||||
|
||||
$scope.graphs.refresh();
|
||||
|
||||
$scope.disks.show();
|
||||
$scope.disks.checkStatus();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Client.onReconnect(function () {
|
||||
$scope.reboot.busy = false;
|
||||
});
|
||||
}]);
|
||||
@@ -1,165 +0,0 @@
|
||||
<!-- modal volume add -->
|
||||
<div class="modal fade" id="volumeAddModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'volumes.addVolumeDialog.title' | tr }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form name="volumeAddForm" role="form" novalidate ng-submit="volumeAdd.submit()" autocomplete="off">
|
||||
<fieldset>
|
||||
<p class="has-error text-center" ng-show="volumeAdd.error">{{ volumeAdd.error }}</p>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label">{{ 'volumes.name' | tr }}</label>
|
||||
<input type="text" class="form-control" ng-model="volumeAdd.name" name="name" ng-disabled="volumeAdd.busy" ng-required="true" autofocus>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="mountType">{{ 'volumes.mountType' | tr }}</label>
|
||||
<select class="form-control" id="mountType" ng-model="volumeAdd.mountType" ng-options="a.value as a.name for a in mountTypes"></select>
|
||||
<p class="small">
|
||||
<span class="text-warning" ng-show="volumeAdd.mountType === 'mountpoint'" ng-bind-html="'volumes.addVolumeDialog.mountpointWarning' | tr"></span>
|
||||
<span class="text-info" ng-hide="volumeAdd.mountType === 'mountpoint' || volumeAdd.mountType === 'filesystem'" ng-bind-html="'volumes.addVolumeDialog.mountTypeInfo' | tr"></span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="volumeAdd.mountType === 'filesystem'">
|
||||
<label class="control-label">{{ 'volumes.localDirectory' | tr }}</label>
|
||||
<input type="text" class="form-control" ng-model="volumeAdd.hostPath" name="hostPath" ng-disabled="volumeAdd.busy" placeholder="/srv/shared" ng-required="volumeAdd.mountType === 'filesystem'" autofocus>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="volumeAdd.mountType === 'mountpoint'">
|
||||
<label class="control-label">{{ 'volumes.hostPath' | tr }}</label>
|
||||
<input type="text" class="form-control" ng-model="volumeAdd.hostPath" name="hostPath" ng-disabled="volumeAdd.busy" placeholder="/mnt/data" ng-required="volumeAdd.mountType === 'mountpoint'" autofocus>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="volumeAdd.mountType === 'ext4' || volumeAdd.mountType === 'xfs'">
|
||||
<label class="control-label">{{ 'volumes.addVolumeDialog.diskPath' | tr }}</label>
|
||||
<select class="form-control" ng-model="volumeAdd.diskPath" ng-options="item.path as item.label for item in blockDevices track by item.path"></select>
|
||||
<input type="text" class="form-control" style="margin-top: 5px;" ng-show="volumeAdd.diskPath.path === 'custom'" ng-model="volumeAdd.customDiskPath" ng-disabled="volumeAdd.busy" placeholder="/dev/disk/by-uuid/uuid" ng-required="(volumeAdd.mountType === 'ext4' || volumeAdd.mountType === 'xfs') && volumeAdd.diskPath.path === 'custom'">
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="volumeAdd.mountType === 'cifs' || volumeAdd.mountType === 'nfs' || volumeAdd.mountType === 'sshfs'">
|
||||
<label class="control-label" for="volumeAddHost">{{ 'volumes.addVolumeDialog.server' | tr }}</label>
|
||||
<input type="text" class="form-control" ng-model="volumeAdd.host" id="volumeAddHost" name="host" ng-disabled="volumeAdd.busy" placeholder="Server IP or hostname" ng-required="volumeAdd.mountType === 'cifs' || volumeAdd.mountType === 'nfs' || volumeAdd.mountType === 'sshfs'">
|
||||
</div>
|
||||
|
||||
<div class="checkbox" ng-show="volumeAdd.mountType === 'cifs'">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="volumeAdd.seal">{{ 'backups.configureBackupStorage.cifsSealSupport' | tr }}</input>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="volumeAdd.mountType === 'sshfs'">
|
||||
<label class="control-label" for="volumeAddPort">{{ 'volumes.addVolumeDialog.port' | tr }}</label>
|
||||
<input type="number" class="form-control" ng-model="volumeAdd.port" id="volumeAddPort" name="port" ng-disabled="volumeAdd.busy" ng-required="volumeAdd.mountType === 'sshfs'">
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="volumeAdd.mountType === 'cifs' || volumeAdd.mountType === 'nfs' || volumeAdd.mountType === 'sshfs'">
|
||||
<label class="control-label" for="volumeAddRemoteDir">{{ 'volumes.addVolumeDialog.remoteDirectory' | tr }}</label>
|
||||
<input type="text" class="form-control" ng-model="volumeAdd.remoteDir" id="volumeAddRemoteDir" name="remoteDir" ng-disabled="volumeAdd.busy" placeholder="/share" ng-required="volumeAdd.mountType === 'cifs' || volumeAdd.mountType === 'nfs' || volumeAdd.mountType === 'sshfs'">
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="volumeAdd.mountType === 'cifs'">
|
||||
<label class="control-label" for="volumeAddUsername">{{ 'volumes.addVolumeDialog.username' | tr }}</label>
|
||||
<input type="text" class="form-control" ng-model="volumeAdd.username" id="volumeAddUsername" name="cifsUsername" ng-disabled="volumeAdd.busy" ng-required="volumeAdd.mountType === 'cifs'">
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="volumeAdd.mountType === 'cifs'">
|
||||
<label class="control-label" for="volumeAddPassword">{{ 'volumes.addVolumeDialog.password' | tr }}</label>
|
||||
<input type="password" class="form-control" ng-model="volumeAdd.password" id="volumeAddPassword" name="cifsPassword" ng-disabled="volumeAdd.busy" password-reveal ng-required="volumeAdd.mountType === 'cifs'">
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="volumeAdd.mountType === 'sshfs'">
|
||||
<label class="control-label" for="volumeAddUser">{{ 'volumes.addVolumeDialog.user' | tr }}</label>
|
||||
<input type="text" class="form-control" ng-model="volumeAdd.user" id="volumeAddUser" name="user" ng-disabled="volumeAdd.busy" ng-required="volumeAdd.mountType === 'sshfs'">
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="volumeAdd.mountType === 'sshfs'">
|
||||
<label class="control-label" for="volumeAddPrivateKey">{{ 'volumes.addVolumeDialog.privateKey' | tr }}</label>
|
||||
<textarea class="form-control" ng-model="volumeAdd.privateKey" id="volumeAddPrivateKey" name="privateKey" ng-disabled="volumeAdd.busy" ng-required="volumeAdd.mountType === 'sshfs'"></textarea>
|
||||
</div>
|
||||
|
||||
<input class="ng-hide" type="submit" ng-disabled="volumeAddForm.$invalid || volumeAdd.busy"/>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer ">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
|
||||
<button type="submit" class="btn btn-outline btn-success pull-right" ng-click="volumeAdd.submit()" ng-disabled="volumeAddForm.$invalid || volumeAdd.busy"><i class="fa fa-circle-notch fa-spin" ng-show="volumeAdd.busy"></i> {{ 'main.dialog.save' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal volume remove -->
|
||||
<div class="modal fade" id="volumeRemoveModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{ 'volumes.removeVolumeDialog.title' | tr:{ volume:volumeRemove.volume.name } }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p ng-bind-html="'volumes.removeVolumeDialog.description' | tr:{ volume:volumeRemove.volume.name }"></p>
|
||||
<p class="has-error" ng-show="volumeRemove.error">{{ volumeRemove.error }}</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
|
||||
<button type="button" class="btn btn-danger" ng-click="volumeRemove.submit()" ng-disabled="volumeRemove.busy"><i class="fa fa-circle-notch fa-spin" ng-show="volumeRemove.busy"></i> {{ 'volumes.removeVolumeDialog.removeAction' | tr }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="text-left">
|
||||
<h1>{{ 'volumes.title' | tr }} <button class="btn btn-primary btn-outline pull-right" ng-click="volumeAdd.show()"><i class="fa fa-plus"></i> {{ 'volumes.addVolumeAction' | tr }}</button></h1>
|
||||
</div>
|
||||
|
||||
<div class="card card-large">
|
||||
<p ng-bind-html="'volumes.description' | tr"></p>
|
||||
|
||||
<div class="row ng-hide" ng-show="!ready">
|
||||
<div class="col-lg-12 text-center">
|
||||
<h2><i class="fa fa-circle-notch fa-spin"></i></h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row animateMeOpacity ng-hide" ng-show="ready">
|
||||
<div class="col-lg-12">
|
||||
<table class="table table-hover" style="margin-top: 10px; table-layout: fixed;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 5%"></th>
|
||||
<th style="width: 20%" class="text-left">{{ 'volumes.name' | tr }}</th>
|
||||
<th style="width: 15%" class="text-left">{{ 'volumes.type' | tr }}</th>
|
||||
<th style="width: 45%" class="text-left">{{ 'volumes.hostPath' | tr }}</th>
|
||||
<th style="width: 15%" class="text-right">{{ 'main.actions' | tr }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="volume in volumes">
|
||||
<td>
|
||||
<i class="fa fa-circle" ng-style="{ color: volume.status.state === 'active' ? '#27CE65' : '#d9534f' }" ng-show="volume.status" uib-tooltip="{{ volume.status.message }}"></i>
|
||||
<i class="fa fa-circle-notch fa-spin" ng-hide="volume.status"></i>
|
||||
</td>
|
||||
<td class="wrap-table-cell">
|
||||
{{ volume.name }}
|
||||
</td>
|
||||
<td class="wrap-table-cell">
|
||||
{{ volume.mountType }}
|
||||
</td>
|
||||
<td class="text-left wrap-table-cell hidden-xs hidden-sm" ng-show="volume.mountType !== 'mountpoint' && volume.mountType !== 'filesystem'">{{ volume.mountOptions.host || volume.mountOptions.diskPath || volume.hostPath }}{{ volume.mountOptions.remoteDir }}</td>
|
||||
<td class="text-left wrap-table-cell hidden-xs hidden-sm" ng-show="volume.mountType === 'mountpoint' || volume.mountType === 'filesystem'">{{ volume.hostPath }}</td>
|
||||
<td class="text-right no-wrap" style="vertical-align: bottom">
|
||||
<button class="btn btn-xs btn-default" ng-click="remount(volume)" ng-show="isMountProvider(volume.mountType)" ng-disabled="volume.remounting" uib-tooltip="{{ 'volumes.remountActionTooltip' | tr }}"><i class="fa fa-sync-alt" ng-class="{ 'fa-spin': volume.remounting }"></i></button>
|
||||
<a class="btn btn-xs btn-default" ng-href="{{ '/filemanager.html?type=volume&id=' + volume.id }}" target="_blank" uib-tooltip="{{ 'volumes.openFileManagerActionTooltip' | tr }}"><i class="fas fa-folder"></i></a>
|
||||
<button class="btn btn-xs btn-danger" ng-click="volumeRemove.show(volume)" uib-tooltip="{{ 'volumes.removeVolumeActionTooltip' | tr }}"><i class="far fa-trash-alt"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,24 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
# Vue 3 + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
./node_modules/.bin/vite build --base=/filemanager/
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
echo "=> Set API origin"
|
||||
export VITE_API_ORIGIN="my.nebulon.space"
|
||||
|
||||
echo "=> Run vite locally"
|
||||
npm run dev
|
||||
@@ -1,13 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/logo.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>FileManager</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "my-vue-app",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"combokeys": "^3.0.1",
|
||||
"filesize": "^10.0.7",
|
||||
"pankow": "^0.1.2",
|
||||
"primeicons": "^6.0.1",
|
||||
"primevue": "^3.27.0",
|
||||
"superagent": "^8.0.9",
|
||||
"vue": "^3.2.47",
|
||||
"vue-router": "^4.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.2.1",
|
||||
"vite": "^4.3.3"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 14 KiB |
@@ -1,139 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<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="64"
|
||||
version="1.1"
|
||||
viewBox="0 0 64 64"
|
||||
height="64"
|
||||
id="svg2"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="android-package-archive.svg">
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1360"
|
||||
inkscape:window-height="708"
|
||||
id="namedview33"
|
||||
showgrid="true"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:zoom="5.6568542"
|
||||
inkscape:cx="50.861875"
|
||||
inkscape:cy="21.2677"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-nodes="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4162" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient4300-2">
|
||||
<stop
|
||||
id="stop4302-4"
|
||||
style="stop-color:#3a539b" />
|
||||
<stop
|
||||
id="stop4304-1"
|
||||
style="stop-color:#3f5aa9"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient6251">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0"
|
||||
offset="0"
|
||||
id="stop6253" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0.2"
|
||||
offset="1"
|
||||
id="stop6255" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6251"
|
||||
id="linearGradient7145-0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-58,-335.3622)"
|
||||
x1="58"
|
||||
y1="392.36221"
|
||||
x2="58"
|
||||
y2="336.36221" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6251"
|
||||
id="linearGradient5849"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.84587337,-47,-272.73372)"
|
||||
x1="58"
|
||||
y1="403.41098"
|
||||
x2="58"
|
||||
y2="323.82297" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata84">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<rect
|
||||
y="11.000853"
|
||||
x="7"
|
||||
height="49.999977"
|
||||
width="49.999977"
|
||||
id="rect5837"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#9bd916;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<rect
|
||||
width="50"
|
||||
x="7"
|
||||
y="60.000854"
|
||||
height="1.0000085"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.25;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect5839" />
|
||||
<rect
|
||||
width="50"
|
||||
x="7"
|
||||
y="11.000853"
|
||||
height="1.0000085"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.5;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect5841" />
|
||||
<path
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.75;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 22,39 c -2.98896,7.5e-4 -5.84891,1.21778 -7.92188,3.37109 l -2.7246,-2.72461 c -0.0942,-0.0974 -0.2239,-0.15234 -0.35938,-0.15234 -0.4494,9e-5 -0.67059,0.54683 -0.34766,0.85937 l 2.76954,2.76954 C 11.85252,45.07416 11.00038,47.49972 11,50 l 0,7 22,0 0,-7 c -0.004,-2.4983 -0.85795,-4.92089 -2.42188,-6.86914 l 2.77735,-2.77735 c 0.32293,-0.31254 0.10175,-0.85928 -0.34766,-0.85937 -0.13548,0 -0.26516,0.055 -0.35937,0.15234 L 29.91602,42.3789 C 27.8458,40.22417 24.98808,39.00438 22,39 Z m 0,1 c 5.52285,0 10,4.47715 10,10 l 0,6 -20,0 0,-6 c 0,-5.52285 4.47715,-10 10,-10 z m -4.5,5 C 16.67157,45 16,45.67157 16,46.5 16,47.32843 16.67157,48 17.5,48 18.32843,48 19,47.32843 19,46.5 19,45.67157 18.32843,45 17.5,45 Z m 9,0 C 25.67157,45 25,45.67157 25,46.5 25,47.32843 25.67157,48 26.5,48 27.32843,48 28,47.32843 28,46.5 28,45.67157 27.32843,45 26.5,45 Z"
|
||||
id="path5845"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccccccccccsccccsssssssssss" />
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.55199998;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient5849);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect5847"
|
||||
width="49.999977"
|
||||
height="49.999977"
|
||||
x="7"
|
||||
y="11.000853" />
|
||||
<path
|
||||
style="opacity:0.75;fill:#ffffff;fill-opacity:1"
|
||||
d="M 40 12 L 40 14 L 42 14 L 42 12 L 40 12 z M 42 14 L 42 16 L 44 16 L 44 14 L 42 14 z M 42 16 L 40 16 L 40 18 L 42 18 L 42 16 z M 42 18 L 42 20 L 44 20 L 44 18 L 42 18 z M 42 20 L 40 20 L 40 22 L 42 22 L 42 20 z M 42 22 L 42 24 L 44 24 L 44 22 L 42 22 z M 42 24 L 40 24 L 40 26 L 42 26 L 42 24 z M 40 27 L 40 31 L 41 31 L 41 35 L 43 35 L 43 31 L 44 31 L 44 27 L 40 27 z M 41 28 L 43 28 L 43 30 L 41 30 L 41 28 z "
|
||||
id="rect4173-3" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 7.4 KiB |
@@ -1,28 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="17" y2="31" x1="40" x2="54" gradientUnits="userSpaceOnUse" gradientTransform="translate(302 78.36)">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="392.36" y2="336.36" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="translate(254-254)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m312 139.36v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<g transform="translate(-302-78.36)">
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<use fill="#fc963a" xlink:href="#c"/>
|
||||
<g transform="scale(1-1)">
|
||||
<rect opacity=".5" x="312" y="-82.36" width="30" fill="#ffffff" height="1"/>
|
||||
<rect opacity=".25" x="312" y="-139.36" width="44" height="1"/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path opacity=".5" fill="#ffffff" d="m356 95.36l-14-14v14z"/>
|
||||
<path opacity=".1" fill="url(#a)" d="m342 95.36l14 14v-14z"/>
|
||||
</g>
|
||||
<use fill="url(#b)" xlink:href="#c"/>
|
||||
<path opacity=".75" color-interpolation-filters="linearRGB" color="#000000" image-rendering="auto" color-rendering="auto" d="m324.04 100.33v3a18.999996 18.999996 0 0 1 19 19h3a21.999996 21.999996 0 0 0 -22 -22m0 7v3a11.999996 11.999996 0 0 1 12 12h3a14.999996 14.999996 0 0 0 -15 -15m3.5 8c-1.939 0-3.5 1.561-3.5 3.5 0 1.939 1.561 3.5 3.5 3.5 1.939 0 3.5-1.561 3.5-3.5 0-1.939-1.561-3.5-3.5-3.5" color-interpolation="sRGB" text-rendering="auto" fill="#ffffff" shape-rendering="auto"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" inkscape:version="1.1-dev (d80adc983d, 2020-06-15)" sodipodi:docname="application-certificate.svg" id="svg35" version="1.1" height="64" viewBox="0 0 64 64" width="64">
|
||||
<sodipodi:namedview inkscape:current-layer="svg35" showgrid="false" id="namedview37" inkscape:window-height="480" inkscape:window-width="640" inkscape:pageshadow="2" inkscape:pageopacity="0" guidetolerance="10" gridtolerance="10" objecttolerance="10" borderopacity="1" bordercolor="#666666" pagecolor="#ffffff" />
|
||||
<defs id="defs17">
|
||||
<linearGradient gradientTransform="matrix(1 0 0-1 0 64)" gradientUnits="userSpaceOnUse" x2="0" y2="61" y1="3" id="a">
|
||||
<stop id="stop2" stop-color="#cf000f" />
|
||||
<stop id="stop4" stop-color="#d91e18" offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient gradientTransform="matrix(1 0 0-1 0 64)" gradientUnits="userSpaceOnUse" x2="0" y2="47" y1="61" id="b">
|
||||
<stop offset="0" stop-color="#fb9fa2" id="stop9" />
|
||||
<stop offset="1" stop-color="#fb7d80" id="stop7" />
|
||||
</linearGradient>
|
||||
<linearGradient gradientUnits="userSpaceOnUse" x2="54" y2="31" x1="40" y1="17" id="c">
|
||||
<stop id="stop12" stop-color="#383e51" />
|
||||
<stop id="stop14" stop-opacity="0" stop-color="#655c6f" offset="1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path id="path19" d="m10 61v-58h30l14 14v44h-14z" fill="url(#a)" />
|
||||
<g id="g25" transform="scale(1-1)">
|
||||
<rect id="rect21" fill-opacity=".412" height="1" fill="#ffffff" y="-4" x="10" width="30" />
|
||||
<rect id="rect23" fill-opacity=".294" height="1" fill="#2e3132" y="-61" x="10" width="44" />
|
||||
</g>
|
||||
<g id="g31" fill-rule="evenodd">
|
||||
<path id="path27" d="m54 17l-14-14v14z" fill="url(#b)" />
|
||||
<path id="path29" d="m40 17l14 14v-14z" fill="url(#c)" opacity=".2" />
|
||||
</g>
|
||||
<path id="path33" d="m32 19.333c-4.432 0-8 3.568-8 8 0 2.585 1.22 4.869 3.111 6.33v12.337l4.889-3.556 4.889 3.556v-12.337c1.891-1.461 3.111-3.745 3.111-6.33 0-4.432-3.568-8-8-8" fill="#fcbcbe" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB |
@@ -1,26 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="3" y2="61" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0-1 0 64)">
|
||||
<stop stop-color="#5e6b78"/>
|
||||
<stop offset="1" stop-color="#768492"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="61" y2="47" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0-1 0 64)">
|
||||
<stop stop-color="#dedede"/>
|
||||
<stop offset="1" stop-color="#fbfbfb"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c" y1="17" x1="40" y2="31" x2="54" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#383e51"/>
|
||||
<stop offset="1" stop-color="#655c6f" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="url(#a)" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
<g transform="scale(1-1)">
|
||||
<rect width="30" x="10" y="-4" fill="#ffffff" height="1" fill-opacity=".412"/>
|
||||
<rect width="44" x="10" y="-61" fill="#2e3132" height="1" fill-opacity=".294"/>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path fill="url(#b)" d="m54 17l-14-14v14z"/>
|
||||
<path opacity=".2" fill="url(#c)" d="m40 17l14 14v-14z"/>
|
||||
</g>
|
||||
<path opacity=".75" fill="#fbfbfb" d="m30.3 20c-1.254 0-2.889.693-4.384 1.857-.897.377-1.527 1.249-1.527 2.263 0 .318.061.628.179.917-1.55.294-2.724 1.629-2.724 3.23 0 .333.051.661.152.978-.639.617-1 1.454-1 2.339 0 .692.22 1.334.595 1.865-.389.55-.595 1.193-.595 1.868 0 1.197.667 2.293 1.724 2.871-.018.148-.027.297-.027.446 0 1.614 1.078 3.041 2.64 3.527.68 1.137 1.916 1.838 3.27 1.838 1.484 0 2.771-.839 3.393-2.057.622 1.218 1.909 2.057 3.393 2.057 1.354 0 2.592-.701 3.272-1.838 1.563-.486 2.638-1.913 2.638-3.527 0-.149-.009-.298-.027-.446 1.057-.578 1.724-1.675 1.724-2.871 0-.676-.204-1.318-.593-1.868.374-.53.593-1.173.593-1.865 0-.885-.361-1.723-1-2.339.101-.317.152-.645.152-.978 0-1.6-1.174-2.936-2.724-3.23.118-.289.179-.599.179-.917 0-1.014-.63-1.886-1.527-2.263-1.495-1.164-3.129-1.857-4.384-1.857-.697 0-1.316.336-1.697.85-.381-.514-.999-.85-1.697-.85m0 .778c.716 0 1.299.57 1.299 1.27v5.03c-.357-.277-.808-.444-1.299-.444-.22 0-.398.174-.398.389 0 .215.178.389.398.389.716 0 1.299.57 1.299 1.27v9.344c-.494-.624-1.191-1.094-2.01-1.31-.212-.056-.431.067-.488.275-.057.207.069.421.281.477 1.306.343 2.217 1.505 2.217 2.826 0 1.615-1.344 2.929-2.995 2.929-1.115 0-2.13-.602-2.65-1.569-.052-.096-.143-.166-.25-.194-1.305-.343-2.215-1.504-2.215-2.824 0-.197.02-.396.06-.589.037-.179-.058-.359-.228-.433-.929-.404-1.529-1.304-1.529-2.296 0-.451.119-.882.347-1.264.594.512 1.371.824 2.223.824.22 0 .398-.174.398-.389 0-.215-.178-.389-.398-.389-1.418 0-2.57-1.129-2.57-2.515 0-.746.337-1.45.924-1.929.133-.108.178-.288.113-.444-.126-.302-.189-.62-.189-.944 0-1.386 1.152-2.513 2.57-2.513 1.418 0 2.572 1.127 2.572 2.513 0 .215.178.389.398.389.22 0 .398-.174.398-.389 0-1.744-1.395-3.174-3.151-3.283-.159-.26-.242-.557-.242-.864 0-.693.43-1.288 1.043-1.546.024-.007.046-.017.068-.029.19-.071.395-.109.61-.109.666 0 1.261.364 1.552.949.096.193.333.273.53.179.198-.094.279-.325.183-.519-.332-.669-.961-1.149-1.685-1.319 1.015-.605 2.01-.949 2.812-.949m3.393 0c.798 0 1.797.344 2.812.949-.724.17-1.353.651-1.685 1.319-.096.193-.014.425.183.519.198.094.436.014.532-.179.291-.586.885-.949 1.55-.949.215 0 .421.038.61.109.022.011.047.022.07.029.613.258 1.043.853 1.043 1.546 0 .307-.085.604-.244.864-1.756.109-3.149 1.539-3.149 3.283 0 .215.178.389.398.389.22 0 .398-.174.398-.389 0-1.386 1.152-2.513 2.57-2.513 1.418 0 2.572 1.127 2.572 2.513 0 .324-.064.641-.189.944-.065.157-.019.336.113.444.587.48.924 1.183.924 1.929 0 1.386-1.154 2.515-2.572 2.515-.22 0-.398.174-.398.389 0 .215.178.389.398.389.852 0 1.629-.312 2.223-.824.228.382.349.813.349 1.264 0 .991-.6 1.892-1.529 2.296-.17.074-.265.254-.228.433.04.193.06.392.06.589 0 1.32-.912 2.48-2.217 2.824-.107.028-.196.099-.248.194-.52.967-1.537 1.569-2.652 1.569-1.652 0-2.995-1.314-2.995-2.929 0-.109-.006-.219-.016-.326.01-.033.016-.067.016-.103v-12.841c0-.7.583-1.27 1.299-1.27.22 0 .398-.174.398-.389 0-.215-.178-.389-.398-.389-.49 0-.941.167-1.299.444v-3.373c0-.7.583-1.27 1.299-1.27m5.938 6.686c-.22 0-.398.174-.398.389 0 1.386-1.152 2.513-2.57 2.513-.697 0-1.349-.266-1.837-.753-.154-.153-.407-.156-.564-.006-.157.15-.16.396-.006.549.551.55 1.264.887 2.036.969.183 1.183 1.23 2.093 2.49 2.093.22 0 .398-.174.398-.389 0-.215-.178-.389-.398-.389-.824 0-1.514-.569-1.683-1.325 1.65-.211 2.929-1.592 2.929-3.262 0-.215-.178-.389-.398-.389m-14.42.416c-.22 0-.398.172-.398.387 0 1.67 1.279 3.054 2.929 3.264-.169.756-.859 1.323-1.683 1.323-.22 0-.398.174-.398.389 0 .215.178.389.398.389 1.26 0 2.308-.91 2.49-2.093.772-.082 1.484-.416 2.036-.967.154-.153.151-.401-.006-.551-.157-.15-.408-.148-.562.006-.488.487-1.14.755-1.837.755-1.418 0-2.572-1.129-2.572-2.515 0-.215-.178-.387-.398-.387m8.483 5.804c-.22 0-.398.174-.398.389 0 .215.178.389.398.389 1.418 0 2.572 1.129 2.572 2.515 0 .089-.006.177-.016.265-1.234.489-2.106 1.673-2.106 3.052 0 .215.178.389.398.389.22 0 .398-.174.398-.389 0-1.386 1.154-2.515 2.572-2.515.22 0 .398-.174.398-.389 0-.215-.178-.389-.398-.389-.153 0-.305.011-.452.031 0-.019.002-.037.002-.055 0-1.815-1.511-3.293-3.368-3.293m-4.664.829c-1.856 0-3.368 1.478-3.368 3.293 0 .019.002.036.002.055-.148-.019-.299-.031-.452-.031-.22 0-.398.174-.398.389 0 .215.178.389.398.389 1.418 0 2.572 1.129 2.572 2.515 0 .215.178.389.398.389.22 0 .398-.174.398-.389 0-1.379-.872-2.564-2.106-3.052-.009-.088-.016-.176-.016-.265 0-1.386 1.154-2.515 2.572-2.515.22 0 .398-.174.398-.389 0-.215-.178-.389-.398-.389"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.4 KiB |
@@ -1,22 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="392.36" y2="336.36" gradientUnits="userSpaceOnUse" x2="0" gradientTransform="translate(518 82)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<path color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" id="b" color-interpolation="sRGB" color="#000000" d="m542 417.36v58h44v-58h-14z"/>
|
||||
</defs>
|
||||
<g transform="translate(-532-414.36)">
|
||||
<use fill="#f9d24c" xlink:href="#b"/>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<rect opacity=".25" x="542" y="474.36" width="44" height="1"/>
|
||||
<rect opacity=".5" x="542" y="417.36" width="44" fill="#ffffff" height="1"/>
|
||||
</g>
|
||||
<rect width="1" x="548" y="418.36" fill="#ffffff" height="56" fill-opacity=".321"/>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<rect x="549" y="418.36" fill-opacity=".057" width="1" height="56"/>
|
||||
<path opacity=".75" fill="#ffffff" d="m566 453.28l-6.916-6.917 6.916-6.911 2.307 2.302-4.614 4.609 2.307 2.308 6.916-6.917-6.03-6.02c-.489-.489-1.29-.489-1.779 0l-9.739 9.74c-.495.489-.495 1.29 0 1.786l9.739 9.74c.489.489 1.29.489 1.779 0l9.739-9.74c.495-.495.495-1.296 0-1.786l-1.412-1.412zm0 0"/>
|
||||
</g>
|
||||
<use fill="url(#a)" xlink:href="#b"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,113 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<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="64"
|
||||
version="1.1"
|
||||
viewBox="0 0 64 64"
|
||||
height="64"
|
||||
id="svg74"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="application-x-gzip.svg">
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1360"
|
||||
inkscape:window-height="708"
|
||||
id="namedview96"
|
||||
showgrid="false"
|
||||
inkscape:zoom="5.6568542"
|
||||
inkscape:cx="-5.9847467"
|
||||
inkscape:cy="40.374841"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg74"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-nodes="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4177" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient6251">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0"
|
||||
offset="0"
|
||||
id="stop6253" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0.2"
|
||||
offset="1"
|
||||
id="stop6255" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6251"
|
||||
id="linearGradient4850"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.84587337,-46.999997,-272.73456)"
|
||||
x1="58"
|
||||
y1="393.95328"
|
||||
x2="58"
|
||||
y2="324.65894" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata84">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#febf10;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect4838"
|
||||
width="49.999977"
|
||||
height="49.999977"
|
||||
x="7"
|
||||
y="10.999992" />
|
||||
<rect
|
||||
id="rect4840"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.25;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
height="1.0000085"
|
||||
y="59.999992"
|
||||
x="7"
|
||||
width="50" />
|
||||
<rect
|
||||
id="rect4842"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.5;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
height="1.0000085"
|
||||
y="10.999992"
|
||||
x="7"
|
||||
width="50" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:0.75;fill:#ffffff;fill-opacity:1"
|
||||
d="m 30,12 0,2 2,0 0,-2 -2,0 z m 2,2 0,2 2,0 0,-2 -2,0 z m 0,2 -2,0 0,2 2,0 0,-2 z m 0,2 0,2 2,0 0,-2 -2,0 z m 0,2 -2,0 0,2 2,0 0,-2 z m 0,2 0,2 2,0 0,-2 -2,0 z m 0,2 -2,0 0,2 2,0 0,-2 z m -2,3 0,4 1,0 0,4 2,0 0,-4 1,0 0,-4 -4,0 z m 1,1 2,0 0,2 -2,0 0,-2 z"
|
||||
id="rect4173-3-3" />
|
||||
<rect
|
||||
y="10.999992"
|
||||
x="7"
|
||||
height="49.999977"
|
||||
width="49.999977"
|
||||
id="rect4848"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4850);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.2 KiB |
@@ -1,27 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="17" y2="31" x1="40" x2="54" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="392.36" y2="336.36" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="translate(-48-332.36)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<use fill="#ffad37" xlink:href="#c"/>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<g fill="#ffffff">
|
||||
<path opacity=".15" d="m10 4v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2l-1-1h-1v-1l-1-1v2h-2v-2h2l-1-1h-1v-1l-1-1v2h-2v-2h2l-1-1h-1v-1l-1-1v2h-2v-2h2l-1-1h-1v-1l-1-1v2h-2v-2h2l-1-1h-29zm3 1h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-24 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-27 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-30 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-33 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2z"/>
|
||||
<rect opacity=".5" x="10" y="-4" width="30" height="1" transform="scale(1-1)"/>
|
||||
</g>
|
||||
<rect opacity=".25" x="10" y="-61" width="44" height="1" transform="scale(1-1)"/>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path fill="#a46022" d="m54 17l-14-14v14z"/>
|
||||
<path opacity=".2" fill="url(#a)" d="m40 17l14 14v-14z"/>
|
||||
</g>
|
||||
<path color-interpolation-filters="linearRGB" color="#000000" image-rendering="auto" color-rendering="auto" d="m24 23v3h1v5.057a2.5 2.5 0 0 0 -2 2.44336 2.5 2.5 0 0 0 2 2.44531v5.05h-1v3h3v-3h-1v-3.529a13 10.500004 0 0 0 12 6.5293 13 10.500004 0 0 0 1 -.041v-.996a12 9.500009 0 0 1 -1 .0371 12 9.500009 0 0 1 -11.61914 -7.16406 2.5 2.5 0 0 0 1.61914 -2.33595 2.5 2.5 0 0 0 -1.61719 -2.33789 12 9.500009 0 0 1 11.61719 -7.16211 12 9.500009 0 0 1 1 .0391v-1.01a13 10.500004 0 0 0 -1 -.0332 13 10.500004 0 0 0 -12 6.51172v-3.512h1v-3zm1 1h1v1h-1zm.5 8a1.5 1.5 0 0 1 1.5 1.5 1.5 1.5 0 0 1 -1.5 1.5 1.5 1.5 0 0 1 -1.5 -1.5 1.5 1.5 0 0 1 1.5 -1.5m-.5 10h1v1h-1z" color-interpolation="sRGB" text-rendering="auto" fill="#99581d" shape-rendering="auto"/>
|
||||
<use fill="url(#b)" xlink:href="#c"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.2 KiB |
@@ -1,26 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="17" x1="40" y2="31" gradientUnits="userSpaceOnUse" x2="54" gradientTransform="translate(372 1234.36)">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="392.36" y2="336.36" gradientUnits="userSpaceOnUse" x2="0" gradientTransform="translate(324 902)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m382 1295.36v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<g transform="translate(-372-1234.36)">
|
||||
<use fill="#ffa555" xlink:href="#c"/>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000" transform="scale(1-1)">
|
||||
<rect opacity=".5" x="382" y="-1238.36" width="30" fill="#ffffff" height="1"/>
|
||||
<rect opacity=".25" x="382" y="-1295.36" width="44" height="1"/>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path opacity=".5" fill="#ffffff" d="m426 1251.36l-14-14v14z"/>
|
||||
<path opacity=".1" fill="url(#a)" d="m412 1251.36l14 14v-14z"/>
|
||||
</g>
|
||||
<path opacity=".75" color-interpolation-filters="linearRGB" color="#000000" image-rendering="auto" color-rendering="auto" d="m401 1256.36c-2.216 0-4 1.784-4 4v3c0 1.662-1.338 3-3 3v1c2.216 0 4-1.784 4-4v-3c0-1.662 1.338-3 3-3zm-7 11v1c1.662 0 3 1.338 3 3v3c0 2.216 1.784 4 4 4v-1c-1.662 0-3-1.338-3-3v-3c0-2.216-1.784-4-4-4m13-11v1c1.662 0 3 1.338 3 3v3c0 2.216 1.784 4 4 4v-1c-1.662 0-3-1.338-3-3v-3c0-2.216-1.784-4-4-4m7 11c-2.216 0-4 1.784-4 4v3c0 1.662-1.338 3-3 3v1c2.216 0 4-1.784 4-4v-3c0-1.662 1.338-3 3-3z" color-interpolation="sRGB" text-rendering="auto" fill="#ffffff" shape-rendering="auto"/>
|
||||
<use fill="url(#b)" xlink:href="#c"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -1,137 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<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="64"
|
||||
viewBox="0 0 64 64"
|
||||
height="64"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="application-json.svg">
|
||||
<metadata
|
||||
id="metadata39">
|
||||
<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>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1382"
|
||||
id="namedview37"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.6875"
|
||||
inkscape:cx="32"
|
||||
inkscape:cy="32"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="36"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" />
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="a"
|
||||
y1="17"
|
||||
y2="31"
|
||||
x1="40"
|
||||
x2="54"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop
|
||||
stop-color="#060606"
|
||||
id="stop7" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-opacity="0"
|
||||
id="stop9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="b"
|
||||
y1="392.36"
|
||||
y2="336.36"
|
||||
x2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(384,822)">
|
||||
<stop
|
||||
stop-color="#ffffff"
|
||||
stop-opacity="0"
|
||||
id="stop12" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#ffffff"
|
||||
stop-opacity=".2"
|
||||
id="stop14" />
|
||||
</linearGradient>
|
||||
<path
|
||||
id="c"
|
||||
d="m442 1215.36v-58h30l14 14v44h-14z" />
|
||||
</defs>
|
||||
<use
|
||||
height="100%"
|
||||
width="100%"
|
||||
y="0"
|
||||
x="0"
|
||||
style="fill:#cf74e0"
|
||||
id="use19"
|
||||
xlink:href="#c"
|
||||
transform="translate(-432,-1154.36)" />
|
||||
<rect
|
||||
x="10"
|
||||
y="-3.9999855"
|
||||
width="30"
|
||||
height="1"
|
||||
id="rect23"
|
||||
style="color:#000000;opacity:0.5;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#ffffff;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto"
|
||||
transform="scale(1,-1)" />
|
||||
<rect
|
||||
x="10"
|
||||
y="-60.999985"
|
||||
width="44"
|
||||
height="1"
|
||||
id="rect25"
|
||||
style="color:#000000;opacity:0.25;color-interpolation:sRGB;color-interpolation-filters:linearRGB;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto"
|
||||
transform="scale(1,-1)" />
|
||||
<path
|
||||
d="M 54,17 40,3 40,17 Z"
|
||||
id="path29"
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:0.5;fill:#ffffff;fill-rule:evenodd" />
|
||||
<path
|
||||
d="M 40,17 54,31 54,17 Z"
|
||||
id="path31"
|
||||
style="opacity:0.1;fill:url(#a);fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="color:#000000;opacity:0.75;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#ffffff;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path33-6"
|
||||
d="m 29,22 c -2.216,0 -4,1.784 -4,4 l 0,3 c 0,1.662 -1.338,3 -3,3 l 0,1 c 2.216,0 4,-1.784 4,-4 l 0,-3 c 0,-1.662 1.338,-3 3,-3 z m -7,11 0,1 c 1.662,0 3,1.338 3,3 l 0,3 c 0,2.216 1.784,4 4,4 l 0,-1 c -1.662,0 -3,-1.338 -3,-3 l 0,-3 c 0,-2.216 -1.784,-4 -4,-4 m 13,-11 0,1 c 1.662,0 3,1.338 3,3 l 0,3 c 0,2.216 1.784,4 4,4 l 0,-1 c -1.662,0 -3,-1.338 -3,-3 l 0,-3 c 0,-2.216 -1.784,-4 -4,-4 m 7,11 c -2.216,0 -4,1.784 -4,4 l 0,3 c 0,1.662 -1.338,3 -3,3 l 0,1 c 2.216,0 4,-1.784 4,-4 l 0,-3 c 0,-1.662 1.338,-3 3,-3 z" />
|
||||
<use
|
||||
height="100%"
|
||||
width="100%"
|
||||
y="0"
|
||||
x="0"
|
||||
style="fill:url(#b)"
|
||||
id="use35"
|
||||
xlink:href="#c"
|
||||
transform="translate(-432,-1154.36)" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -1,27 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="17" x1="40" y2="31" gradientUnits="userSpaceOnUse" x2="54">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="392.36" y2="336.36" gradientUnits="userSpaceOnUse" x2="0" gradientTransform="translate(-48-332.36)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<use fill="#555555" xlink:href="#c"/>
|
||||
<g transform="scale(1-1)">
|
||||
<rect opacity=".4" x="10" y="-4" width="30" fill="#ffffff" height="1"/>
|
||||
<rect opacity=".35" x="10" y="-61" width="44" height="1"/>
|
||||
</g>
|
||||
<path opacity=".5" fill="#ffffff" fill-rule="evenodd" d="m54 17l-14-14v14z"/>
|
||||
</g>
|
||||
<path opacity=".4" fill="url(#a)" fill-rule="evenodd" d="m40 17l14 14v-14z"/>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<path fill="#343434" d="m20 24v22h26v-22zm18 1c.552 0 1 .448 1 1 0 .552-.448 1-1 1-.552 0-1-.448-1-1 0-.552.448-1 1-1m3 0c.552 0 1 .448 1 1 0 .552-.448 1-1 1-.552 0-1-.448-1-1 0-.552.448-1 1-1m3 0c.552 0 1 .448 1 1 0 .552-.448 1-1 1-.552 0-1-.448-1-1 0-.552.448-1 1-1m-23 3h24v17h-24zm8.799 4l2.713 4.721-3.094 5.279h1.184l2.502-4.416 2.5 4.416h1.131c.013-.01.027-.019.039-.029l-3.057-5.25 2.697-4.721h-1.211l-2.1 3.857-2.1-3.857z"/>
|
||||
<path fill="#e0e0e0" d="m19 23v22h26v-22zm18 1c.552 0 1 .448 1 1 0 .552-.448 1-1 1-.552 0-1-.448-1-1 0-.552.448-1 1-1m3 0c.552 0 1 .448 1 1 0 .552-.448 1-1 1-.552 0-1-.448-1-1 0-.552.448-1 1-1m3 0c.552 0 1 .448 1 1 0 .552-.448 1-1 1-.552 0-1-.448-1-1 0-.552.448-1 1-1m-23 3h24v17h-24zm8.799 4l2.713 4.721-3.094 5.279h1.184l2.502-4.416 2.5 4.416h1.131c.013-.01.027-.019.039-.029l-3.057-5.25 2.697-4.721h-1.211l-2.1 3.857-2.1-3.857z"/>
|
||||
</g>
|
||||
<use fill="url(#b)" xlink:href="#c"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.2 KiB |
@@ -1,24 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="392.36" y2="336.36" gradientUnits="userSpaceOnUse" x2="0" gradientTransform="translate(-48-332.36)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="17" x1="40" y2="31" gradientUnits="userSpaceOnUse" x2="54">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<use fill="#555555" xlink:href="#c"/>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000" transform="scale(1-1)">
|
||||
<rect opacity=".4" x="10" y="-4" width="30" fill="#ffffff" height="1"/>
|
||||
<rect opacity=".35" x="10" y="-61" width="44" height="1"/>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path opacity=".5" fill="#ffffff" d="m54 17l-14-14v14z"/>
|
||||
<path opacity=".4" fill="url(#b)" d="m40 17l14 14v-14z"/>
|
||||
</g>
|
||||
<path opacity=".75" color-interpolation-filters="linearRGB" color="#000000" image-rendering="auto" color-rendering="auto" d="m40 26.01l-8 .004-4.441 7.691-1.555-2.695h-2v1h1.428l2.133 3.693 5.02-8.693h6.42v1h1zm-5 3c-1.662 0-3 1.338-3 3 0 1.662 1.338 3 3 3 .773 0 1.469-.298 2-.775v.775h1v-6h-1v.775c-.531-.477-1.227-.775-2-.775m0 1c1.108 0 2 .892 2 2 0 1.108-.892 2-2 2-1.108 0-2-.892-2-2 0-1.108.892-2 2-2m-11.99 6.99c-.006 0-.01.223-.01.5 0 .277.004.5.01.5h17.98c.006 0 .01-.223.01-.5 0-.277-.004-.5-.01-.5zm8 3c-.006 0-.01.223-.01.5 0 .277.004.5.01.5h1.98c.006 0 .01-.223.01-.5 0-.277-.004-.5-.01-.5z" color-interpolation="sRGB" text-rendering="auto" fill="#ffffff" shape-rendering="auto"/>
|
||||
<use fill="url(#a)" xlink:href="#c"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -1,222 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<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="64"
|
||||
viewBox="0 0 64 64"
|
||||
height="64"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="application-msonenote.svg">
|
||||
<metadata
|
||||
id="metadata39">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="640"
|
||||
inkscape:window-height="480"
|
||||
id="namedview37"
|
||||
showgrid="false"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="36.561651"
|
||||
inkscape:cy="28.512949"
|
||||
inkscape:current-layer="svg2">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4219" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="a"
|
||||
y1="61"
|
||||
y2="3"
|
||||
x2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1 0 0-1 0 64)">
|
||||
<stop
|
||||
stop-color="#913d88"
|
||||
id="stop7" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#9b4792"
|
||||
id="stop9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="b"
|
||||
y1="61"
|
||||
y2="47"
|
||||
x2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1 0 0-1 0 64)">
|
||||
<stop
|
||||
stop-color="#d5a5d0"
|
||||
id="stop12" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#e7cbe4"
|
||||
id="stop14" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="c"
|
||||
y1="17"
|
||||
x1="40"
|
||||
y2="31"
|
||||
x2="54"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop
|
||||
stop-color="#383e51"
|
||||
id="stop17" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#655c6f"
|
||||
stop-opacity="0"
|
||||
id="stop19" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="matrix(1 0 0-1 0 64)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="0"
|
||||
y2="3"
|
||||
y1="61"
|
||||
id="a-7">
|
||||
<stop
|
||||
id="stop7-5"
|
||||
stop-color="#3a539b" />
|
||||
<stop
|
||||
id="stop9-3"
|
||||
stop-color="#3f5aa9"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="matrix(1 0 0-1 0 64)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="0"
|
||||
y2="47"
|
||||
y1="61"
|
||||
id="b-5">
|
||||
<stop
|
||||
id="stop12-6"
|
||||
stop-color="#97aad8" />
|
||||
<stop
|
||||
id="stop14-2"
|
||||
stop-color="#c1cae7"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<path
|
||||
d="m40 17l14 14v-14z"
|
||||
id="d"
|
||||
fill-rule="evenodd"
|
||||
fill="url(#c)"
|
||||
opacity=".2" />
|
||||
<linearGradient
|
||||
id="a-3"
|
||||
y1="392.36"
|
||||
y2="336.36"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="0"
|
||||
gradientTransform="translate(-48,-332.36)">
|
||||
<stop
|
||||
stop-color="#ffffff"
|
||||
stop-opacity="0"
|
||||
id="stop4173" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#ffffff"
|
||||
stop-opacity=".2"
|
||||
id="stop4175" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="b-6"
|
||||
y1="17"
|
||||
x1="40"
|
||||
y2="31"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="54">
|
||||
<stop
|
||||
stop-color="#060606"
|
||||
id="stop4178" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-opacity="0"
|
||||
id="stop4180" />
|
||||
</linearGradient>
|
||||
<path
|
||||
id="c-7"
|
||||
d="m10 61v-58h30l14 14v44h-14z" />
|
||||
</defs>
|
||||
<g
|
||||
id="g4242">
|
||||
<use
|
||||
height="100%"
|
||||
width="100%"
|
||||
y="0"
|
||||
x="0"
|
||||
style="fill:#913d88;fill-opacity:1"
|
||||
xlink:href="#c-7"
|
||||
id="use4183" />
|
||||
<rect
|
||||
id="rect4187"
|
||||
height="1"
|
||||
width="30"
|
||||
y="-4"
|
||||
x="10"
|
||||
style="color:#000000;opacity:0.5;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#ffffff;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto"
|
||||
transform="scale(1,-1)" />
|
||||
<rect
|
||||
id="rect4189"
|
||||
height="1"
|
||||
width="44"
|
||||
y="-61"
|
||||
x="10"
|
||||
style="color:#000000;opacity:0.25;color-interpolation:sRGB;color-interpolation-filters:linearRGB;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto"
|
||||
transform="scale(1,-1)" />
|
||||
<path
|
||||
id="path4193"
|
||||
d="M 54,17 40,3 40,17 Z"
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:0.5;fill:#ffffff;fill-rule:evenodd" />
|
||||
<path
|
||||
style="opacity:0.2;fill:url(#b-6);fill-rule:evenodd"
|
||||
id="path4195"
|
||||
d="M 40,17 54,31 54,17 Z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.75;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="path35"
|
||||
d="m 22,22 c -0.554,0 -1,0.446 -1,1 l 0,20 c 0,0.554 0.446,1 1,1 l 18,0 c 0.554,0 1,-0.446 1,-1 l 0,-1 1,0 c 0.554,0 1,-0.446 1,-1 l 0,-4 C 43,36.814 42.936,36.649 42.848,36.5 42.936,36.351 43,36.186 43,36 l 0,-4 C 43,31.814 42.936,31.649 42.848,31.5 42.936,31.351 43,31.186 43,31 l 0,-4 c 0,-0.554 -0.446,-1 -1,-1 l -1,0 0,-3 c 0,-0.554 -0.446,-1 -1,-1 z m 0,1 18,0 0,20 -18,0 z m 1,2 0,1 7,0 0,-1 z m 9,0 0,1 7,0 0,-1 z m 9,2 1,0 0,4 -1,0 z m -18,1 0,1 7,0 0,-1 z m 9,0 0,1 7,0 0,-1 z m -9,3 0,1 7,0 0,-1 z m 9,0 0,1 7,0 0,-1 z m 9,1 1,0 0,4 -1,0 z m -18,2 0,1 7,0 0,-1 z m 9,0 0,1 7,0 0,-1 z m -9,3 0,1 7,0 0,-1 z m 9,0 0,1 7,0 0,-1 z m 9,0 1,0 0,4 -1,0 z m -18,3 0,1 7,0 0,-1 z m 9,0 0,1 7,0 0,-1 z" />
|
||||
<use
|
||||
height="100%"
|
||||
width="100%"
|
||||
y="0"
|
||||
x="0"
|
||||
xlink:href="#c-7"
|
||||
id="use4199"
|
||||
style="fill:url(#a-3)" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.6 KiB |
@@ -1,26 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="61" y2="3" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0-1 0 64)">
|
||||
<stop stop-color="#3a539b"/>
|
||||
<stop offset="1" stop-color="#3f5aa9"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="61" y2="47" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0-1 0 64)">
|
||||
<stop stop-color="#97aad8"/>
|
||||
<stop offset="1" stop-color="#c1cae7"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c" y1="17" x1="40" y2="31" x2="54" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#383e51"/>
|
||||
<stop offset="1" stop-color="#655c6f" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<path opacity=".2" fill="url(#c)" fill-rule="evenodd" id="d" d="m40 17l14 14v-14z"/>
|
||||
</defs>
|
||||
<path fill="url(#a)" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
<g transform="scale(1-1)">
|
||||
<rect width="30" x="10" y="-4" fill="#ffffff" height="1" fill-opacity=".412"/>
|
||||
<rect width="44" x="10" y="-61" fill="#2e3132" height="1" fill-opacity=".294"/>
|
||||
</g>
|
||||
<path fill="url(#b)" fill-rule="evenodd" d="m54 17l-14-14v14z"/>
|
||||
<use xlink:href="#d"/>
|
||||
<use xlink:href="#d"/>
|
||||
<path opacity=".75" fill="#ffffff" color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000" d="m21 24c-.554 0-1 .446-1 1v16c0 .554.446 1 1 1h22c.554 0 1-.446 1-1v-16c0-.554-.446-1-1-1zm0 1h.047l7.525 7.182-7.572 7.227zm1.258 0h19.484l-9.742 9.297zm20.695 0h.047v14.408l-7.572-7.227zm-13.775 7.76l2.822 2.695 2.822-2.695 8.178 7.807v.434h-22v-.434z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,25 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="392.36" y2="336.36" gradientUnits="userSpaceOnUse" x2="0" gradientTransform="translate(-48-332.36)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="17" x1="40" y2="31" gradientUnits="userSpaceOnUse" x2="54">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<use fill="#0b0b64" xlink:href="#c"/>
|
||||
<g fill="#ffffff">
|
||||
<path opacity=".1" d="m10 4v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2l-1-1h-1v-1l-1-1v2h-2v-2h2l-1-1h-1v-1l-1-1v2h-2v-2h2l-1-1h-1v-1l-1-1v2h-2v-2h2l-1-1h-1v-1l-1-1v2h-2v-2h2l-1-1h-29zm3 1h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-24 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-27 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-30 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-33 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2z"/>
|
||||
<rect opacity=".5" x="10" y="-4" width="30" height="1" transform="scale(1-1)"/>
|
||||
</g>
|
||||
<rect opacity=".25" x="10" y="-61" width="44" height="1" transform="scale(1-1)"/>
|
||||
<path fill="#8282dd" d="m54 17l-14-14v14z"/>
|
||||
</g>
|
||||
<path opacity=".2" fill="url(#b)" fill-rule="evenodd" d="m40 17l14 14v-14z"/>
|
||||
<use opacity=".7" fill="url(#a)" xlink:href="#c"/>
|
||||
<path color-interpolation-filters="linearRGB" color="#000000" image-rendering="auto" color-rendering="auto" d="m22 22c-.554 0-1 .446-1 1v20c0 .554.446 1 1 1h20c.554 0 1-.446 1-1v-20c0-.554-.446-1-1-1zm0 1h20v20h-20v-19zm1 2v1h8v-1zm10 0v1h8v-1zm-9.949 3v1h7.949v-1zm9.949 0v1h8v-1zm-10 3v1h8v-1zm10 0v1h8v-1zm-9.949 3v1h7.949v-1zm9.949 0v1h8v-1zm-10 3v1h8v-1zm10 0v1h8v-1zm-9.949 3v1h7.949v-1zm9.949 0v1h8v-1z" color-interpolation="sRGB" text-rendering="auto" fill="#8282dd" shape-rendering="auto"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.0 KiB |
@@ -1,24 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="392.36" y2="336.36" gradientUnits="userSpaceOnUse" x2="0" gradientTransform="translate(-48-332.36)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="17" x1="40" y2="31" gradientUnits="userSpaceOnUse" x2="54">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<use fill="#4040bf" xlink:href="#c"/>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000" transform="scale(1-1)">
|
||||
<rect opacity=".5" x="10" y="-4" width="30" fill="#ffffff" height="1"/>
|
||||
<rect opacity=".25" x="10" y="-61" width="44" height="1"/>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path opacity=".5" fill="#ffffff" d="m54 17l-14-14v14z"/>
|
||||
<path opacity=".2" fill="url(#b)" d="m40 17l14 14v-14z"/>
|
||||
</g>
|
||||
<path opacity=".75" color-interpolation-filters="linearRGB" color="#000000" image-rendering="auto" color-rendering="auto" d="m22 22c-.554 0-1 .446-1 1v20c0 .554.446 1 1 1h20c.554 0 1-.446 1-1v-20c0-.554-.446-1-1-1zm0 1h20v20h-20v-19zm1 2v1h8v-1zm10 0v1h8v-1zm-9.949 3v1h7.949v-1zm9.949 0v1h8v-1zm-10 3v1h8v-1zm10 0v1h8v-1zm-9.949 3v1h7.949v-1zm9.949 0v1h8v-1zm-10 3v1h8v-1zm10 0v1h8v-1zm-9.949 3v1h7.949v-1zm9.949 0v1h8v-1z" color-interpolation="sRGB" text-rendering="auto" fill="#ffffff" shape-rendering="auto"/>
|
||||
<use fill="url(#a)" xlink:href="#c"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,26 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="61" y2="3" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0-1 0 64)">
|
||||
<stop stop-color="#5e6b78"/>
|
||||
<stop offset="1" stop-color="#768492"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="61" y2="47" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0-1 0 64)">
|
||||
<stop stop-color="#dedede"/>
|
||||
<stop offset="1" stop-color="#fbfbfb"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c" y1="17" x1="40" y2="31" x2="54" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#383e51"/>
|
||||
<stop offset="1" stop-color="#655c6f" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="url(#a)" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
<g transform="scale(1-1)">
|
||||
<rect width="30" x="10" y="-4" fill="#ffffff" height="1" fill-opacity=".412"/>
|
||||
<rect width="44" x="10" y="-61" fill="#2e3132" height="1" fill-opacity=".294"/>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path opacity=".75" fill="url(#b)" d="m54 17l-14-14v14z"/>
|
||||
<path opacity=".2" fill="url(#c)" d="m40 17l14 14v-14z"/>
|
||||
<path opacity=".75" fill="#fbfbfb" d="m21 22v22h22v-22zm1 5h20v16h-20zm10.5 2c-.972 0-1.88.341-2.52.98-.64.64-.98 1.547-.98 2.52h2c0-.528.159-.87.395-1.105.235-.235.578-.395 1.105-.395.528 0 .87.159 1.105.395.235.235.395.578.395 1.105 0 .458-.09.653-.211.826-.521.526-1.152.976-1.658 1.406-.572.429-1.131.992-1.131 1.768v.5h2v-.5c.711-.728 1.77-1.362 2.43-2.029.348-.499.57-1.179.57-1.971 0-.972-.341-1.88-.98-2.52-.64-.64-1.547-.98-2.52-.98m-1.5 10v2h2v-2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1,18 +0,0 @@
|
||||
<svg version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="a" x2="0" y1="392.36" y2="336.36" gradientTransform="translate(-6,-332.36)" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#fff" stop-opacity="0" offset="0"/>
|
||||
<stop stop-color="#fff" stop-opacity=".2" offset="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g transform="translate(0,-4)" color="#000000" color-rendering="auto" image-rendering="auto" shape-rendering="auto">
|
||||
<circle cx="37" cy="36" r="24" fill="#fbeaab"/>
|
||||
<path d="m5 11v50h50v-20h-4.096c-2.717 0-4.904-2.187-4.904-4.904v-0.191c0-2.717 2.187-4.904 4.904-4.904h4.096v-20z" fill="#f9d24c"/>
|
||||
<rect x="5" y="60" width="50" height="1" opacity=".25"/>
|
||||
<g fill="#fff">
|
||||
<rect x="5" y="11" width="50" height="1" opacity=".5"/>
|
||||
<path d="m21 25v16.05c-0.635-0.648-1.517-1.053-2.5-1.053-1.939 0-3.5 1.561-3.5 3.5s1.561 3.5 3.5 3.5c1.905 0 3.437-1.509 3.49-3.4h0.01v-13.6h14v8.05c-0.635-0.648-1.517-1.053-2.5-1.053-1.939 0-3.5 1.561-3.5 3.5s1.561 3.5 3.5 3.5 3.5-1.561 3.5-3.5v-15.5h-15zm1 2h14v2h-14z" opacity=".75" stroke-width="2"/>
|
||||
</g>
|
||||
<path d="m5 11v50h50v-9.158a24 24 0 0 0 6-15.841797 24 24 0 0 0-6-15.830078v-9.17z" fill="url(#a)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,20 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="392.36" y2="336.36" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="translate(-14-332.36)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".15"/>
|
||||
</linearGradient>
|
||||
<path color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" id="b" color-interpolation="sRGB" color="#000000" d="m10 3v58h44v-58h-14z"/>
|
||||
</defs>
|
||||
<use fill="#da2c2c" xlink:href="#b"/>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<rect opacity=".25" x="10" y="60" width="44" height="1"/>
|
||||
<g fill="#ffffff">
|
||||
<rect opacity=".5" x="10" y="3" width="44" height="1"/>
|
||||
<path opacity=".8" d="m26.301 17.981c-.363.013-.752.11-1.179.293-3.813 1.629-1 5.371 3.856 8.793.029-.656.047-1.298.04-1.913-4.147-2.838-5.763-5.656-3.768-6.321 2.139-.885 3.718 1.737 3.768 6.321.26.177.521.351.794.53-.101-4.172-1.016-7.444-3.151-7.685-.114-.013-.239-.02-.36-.016m3.511 7.701l-.497-.788-.631.544-.132.828.832 1.569.418-.201c2.797 1.864 6.138 3.581 9.273 4.76l.931.542 1.287.031c1.186-.703.479-.962 4.789-1.495l-4.856.369c-3.746-1.45-7.94-3.887-11.417-6.159m9.343 6.692c.562.045 4.52-.647 6.264-.815.667-.087 3.31-.277 3.444 1.67.456-1.061-.23-2.066-1.71-2.381-2.287-.512-6.693 1.103-7.998 1.526m2.526-.492c-.68.244-1.828.219-2.562.517 1.041.533 6.371 1.947 8.528 1.542.345-.054 1.289-.316 1.325-1.076-1.728 1.201-6.498.358-7.291-.982m1.421.022c-.559-.179-3.444.709-4.02.49-3.547 1.182-7.432 2.883-10.591 4.698l-.915.181-.707 1.262-.949 1.235 1.07 1.065.837-1.374c3.699-2.218 11.01-5.83 15.277-7.56m-15.277 7.56c-.452.271-.884.539-1.293.799-.474 1.345-1.84 4.302-2.778 4.211 0 0-.172.143-.446.371 1.441.828 3.335-1.76 4.518-5.381m-4.072 5.01c-2.488-1.468 1.599-3.621 2.778-4.211.266-.759.517-1.582.754-2.46-3.715 2.312-6.04 4.709-4.796 6.293.3.383.539.659.816.748m3.979-7.04c.385-.239.79-.474 1.204-.712.732-2.952 1.217-6.347 1.318-9.458-.282-.188-.557-.378-.826-.568-.157 3.502-.808 7.457-1.694 10.738"/>
|
||||
</g>
|
||||
</g>
|
||||
<rect width="1" x="16" y="4" fill="#ffffff" height="56" fill-opacity=".252"/>
|
||||
<rect color-interpolation-filters="linearRGB" x="17" y="4" fill-opacity=".083" color="#000000" image-rendering="auto" color-rendering="auto" width="1" color-interpolation="sRGB" text-rendering="auto" height="56" shape-rendering="auto"/>
|
||||
<use fill="url(#a)" xlink:href="#b"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 192 KiB |
@@ -1,27 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="17" y2="31" x1="40" x2="54" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="392.36" y2="336.36" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="translate(-48-332.36)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<use fill="#51db51" xlink:href="#c"/>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<g transform="scale(1-1)">
|
||||
<rect opacity=".5" x="10" y="-4" width="30" fill="#ffffff" height="1"/>
|
||||
<rect opacity=".25" x="10" y="-61" width="44" height="1"/>
|
||||
</g>
|
||||
<path opacity=".5" fill="#ffffff" d="m54 17l-14-14v14z"/>
|
||||
</g>
|
||||
<path opacity=".2" fill="url(#a)" fill-rule="evenodd" d="m40 17l14 14v-14z"/>
|
||||
<g fill="#ffffff" color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<path opacity=".75" d="m35.734 22.01c-3.983 0-7.211 3.228-7.211 7.207 0 1.082.245 2.103.672 3.02l-1.234 1.461-.107 1.369c-.011.01-1.344.049-1.344.049l-.686.496c0 0 .006 1.495 0 1.475l-.002.002-1.467.023-.621.709-.014 1.34c0 0-1.266-.083-1.395.035-.81.746-1.355 1.277-1.355 1.277l.006 3.506c0 0 .469-.002.688-.002l9.338-9.336c.495.431 1.049.794 1.648 1.078l.002-.002c.936.444 1.979.701 3.084.701 3.983 0 7.211-3.227 7.211-7.207 0-3.979-3.228-7.206-7.213-7.205m1.377 3.434c1.327 0 2.402 1.077 2.402 2.404 0 1.327-1.075 2.402-2.402 2.402-1.328 0-2.404-1.075-2.404-2.402 0-1.327 1.076-2.404 2.404-2.404"/>
|
||||
<path opacity=".5" d="m37.11 25.445c-1.328 0-2.404 1.077-2.404 2.404 0 1.327 1.076 2.402 2.404 2.402 1.327 0 2.402-1.075 2.402-2.402 0-1.327-1.075-2.404-2.402-2.404m0 1.373c.569 0 1.029.462 1.029 1.031 0 .569-.461 1.029-1.029 1.029-.569 0-1.031-.461-1.031-1.029 0-.569.463-1.031 1.031-1.031m-6.109 7.828l-9.342 9.34h3.434l8.154-8.01c-.832-.307-1.59-.757-2.246-1.328"/>
|
||||
</g>
|
||||
<use fill="url(#b)" xlink:href="#c"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" inkscape:version="1.1-dev (d80adc983d, 2020-06-15)" sodipodi:docname="application-certificate.svg" id="svg35" version="1.1" height="64" viewBox="0 0 64 64" width="64">
|
||||
<sodipodi:namedview inkscape:current-layer="svg35" showgrid="false" id="namedview37" inkscape:window-height="480" inkscape:window-width="640" inkscape:pageshadow="2" inkscape:pageopacity="0" guidetolerance="10" gridtolerance="10" objecttolerance="10" borderopacity="1" bordercolor="#666666" pagecolor="#ffffff" />
|
||||
<defs id="defs17">
|
||||
<linearGradient gradientTransform="matrix(1 0 0-1 0 64)" gradientUnits="userSpaceOnUse" x2="0" y2="61" y1="3" id="a">
|
||||
<stop id="stop2" stop-color="#cf000f" />
|
||||
<stop id="stop4" stop-color="#d91e18" offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient gradientTransform="matrix(1 0 0-1 0 64)" gradientUnits="userSpaceOnUse" x2="0" y2="47" y1="61" id="b">
|
||||
<stop offset="0" stop-color="#fb9fa2" id="stop9" />
|
||||
<stop offset="1" stop-color="#fb7d80" id="stop7" />
|
||||
</linearGradient>
|
||||
<linearGradient gradientUnits="userSpaceOnUse" x2="54" y2="31" x1="40" y1="17" id="c">
|
||||
<stop id="stop12" stop-color="#383e51" />
|
||||
<stop id="stop14" stop-opacity="0" stop-color="#655c6f" offset="1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path id="path19" d="m10 61v-58h30l14 14v44h-14z" fill="url(#a)" />
|
||||
<g id="g25" transform="scale(1-1)">
|
||||
<rect id="rect21" fill-opacity=".412" height="1" fill="#ffffff" y="-4" x="10" width="30" />
|
||||
<rect id="rect23" fill-opacity=".294" height="1" fill="#2e3132" y="-61" x="10" width="44" />
|
||||
</g>
|
||||
<g id="g31" fill-rule="evenodd">
|
||||
<path id="path27" d="m54 17l-14-14v14z" fill="url(#b)" />
|
||||
<path id="path29" d="m40 17l14 14v-14z" fill="url(#c)" opacity=".2" />
|
||||
</g>
|
||||
<path id="path33" d="m32 19.333c-4.432 0-8 3.568-8 8 0 2.585 1.22 4.869 3.111 6.33v12.337l4.889-3.556 4.889 3.556v-12.337c1.891-1.461 3.111-3.745 3.111-6.33 0-4.432-3.568-8-8-8" fill="#fcbcbe" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB |
@@ -1,27 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="17" y2="31" x1="40" x2="54" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="392.36" y2="336.36" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="translate(-48-332.36)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<use fill="#51db51" xlink:href="#c"/>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<g transform="scale(1-1)">
|
||||
<rect opacity=".5" x="10" y="-4" width="30" fill="#ffffff" height="1"/>
|
||||
<rect opacity=".25" x="10" y="-61" width="44" height="1"/>
|
||||
</g>
|
||||
<path opacity=".5" fill="#ffffff" d="m54 17l-14-14v14z"/>
|
||||
</g>
|
||||
<path opacity=".2" fill="url(#a)" fill-rule="evenodd" d="m40 17l14 14v-14z"/>
|
||||
<g fill="#ffffff" color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<path opacity=".75" d="m35.734 22.01c-3.983 0-7.211 3.228-7.211 7.207 0 1.082.245 2.103.672 3.02l-1.234 1.461-.107 1.369c-.011.01-1.344.049-1.344.049l-.686.496c0 0 .006 1.495 0 1.475l-.002.002-1.467.023-.621.709-.014 1.34c0 0-1.266-.083-1.395.035-.81.746-1.355 1.277-1.355 1.277l.006 3.506c0 0 .469-.002.688-.002l9.338-9.336c.495.431 1.049.794 1.648 1.078l.002-.002c.936.444 1.979.701 3.084.701 3.983 0 7.211-3.227 7.211-7.207 0-3.979-3.228-7.206-7.213-7.205m1.377 3.434c1.327 0 2.402 1.077 2.402 2.404 0 1.327-1.075 2.402-2.402 2.402-1.328 0-2.404-1.075-2.404-2.402 0-1.327 1.076-2.404 2.404-2.404"/>
|
||||
<path opacity=".5" d="m37.11 25.445c-1.328 0-2.404 1.077-2.404 2.404 0 1.327 1.076 2.402 2.404 2.402 1.327 0 2.402-1.075 2.402-2.402 0-1.327-1.075-2.404-2.402-2.404m0 1.373c.569 0 1.029.462 1.029 1.031 0 .569-.461 1.029-1.029 1.029-.569 0-1.031-.461-1.031-1.029 0-.569.463-1.031 1.031-1.031m-6.109 7.828l-9.342 9.34h3.434l8.154-8.01c-.832-.307-1.59-.757-2.246-1.328"/>
|
||||
</g>
|
||||
<use fill="url(#b)" xlink:href="#c"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 13 KiB |
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" inkscape:version="1.1-dev (d80adc983d, 2020-06-15)" sodipodi:docname="application-certificate.svg" id="svg35" version="1.1" height="64" viewBox="0 0 64 64" width="64">
|
||||
<sodipodi:namedview inkscape:current-layer="svg35" showgrid="false" id="namedview37" inkscape:window-height="480" inkscape:window-width="640" inkscape:pageshadow="2" inkscape:pageopacity="0" guidetolerance="10" gridtolerance="10" objecttolerance="10" borderopacity="1" bordercolor="#666666" pagecolor="#ffffff" />
|
||||
<defs id="defs17">
|
||||
<linearGradient gradientTransform="matrix(1 0 0-1 0 64)" gradientUnits="userSpaceOnUse" x2="0" y2="61" y1="3" id="a">
|
||||
<stop id="stop2" stop-color="#cf000f" />
|
||||
<stop id="stop4" stop-color="#d91e18" offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient gradientTransform="matrix(1 0 0-1 0 64)" gradientUnits="userSpaceOnUse" x2="0" y2="47" y1="61" id="b">
|
||||
<stop offset="0" stop-color="#fb9fa2" id="stop9" />
|
||||
<stop offset="1" stop-color="#fb7d80" id="stop7" />
|
||||
</linearGradient>
|
||||
<linearGradient gradientUnits="userSpaceOnUse" x2="54" y2="31" x1="40" y1="17" id="c">
|
||||
<stop id="stop12" stop-color="#383e51" />
|
||||
<stop id="stop14" stop-opacity="0" stop-color="#655c6f" offset="1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path id="path19" d="m10 61v-58h30l14 14v44h-14z" fill="url(#a)" />
|
||||
<g id="g25" transform="scale(1-1)">
|
||||
<rect id="rect21" fill-opacity=".412" height="1" fill="#ffffff" y="-4" x="10" width="30" />
|
||||
<rect id="rect23" fill-opacity=".294" height="1" fill="#2e3132" y="-61" x="10" width="44" />
|
||||
</g>
|
||||
<g id="g31" fill-rule="evenodd">
|
||||
<path id="path27" d="m54 17l-14-14v14z" fill="url(#b)" />
|
||||
<path id="path29" d="m40 17l14 14v-14z" fill="url(#c)" opacity=".2" />
|
||||
</g>
|
||||
<path id="path33" d="m32 19.333c-4.432 0-8 3.568-8 8 0 2.585 1.22 4.869 3.111 6.33v12.337l4.889-3.556 4.889 3.556v-12.337c1.891-1.461 3.111-3.745 3.111-6.33 0-4.432-3.568-8-8-8" fill="#fcbcbe" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB |
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" inkscape:version="1.1-dev (d80adc983d, 2020-06-15)" sodipodi:docname="application-certificate.svg" id="svg35" version="1.1" height="64" viewBox="0 0 64 64" width="64">
|
||||
<sodipodi:namedview inkscape:current-layer="svg35" showgrid="false" id="namedview37" inkscape:window-height="480" inkscape:window-width="640" inkscape:pageshadow="2" inkscape:pageopacity="0" guidetolerance="10" gridtolerance="10" objecttolerance="10" borderopacity="1" bordercolor="#666666" pagecolor="#ffffff" />
|
||||
<defs id="defs17">
|
||||
<linearGradient gradientTransform="matrix(1 0 0-1 0 64)" gradientUnits="userSpaceOnUse" x2="0" y2="61" y1="3" id="a">
|
||||
<stop id="stop2" stop-color="#cf000f" />
|
||||
<stop id="stop4" stop-color="#d91e18" offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient gradientTransform="matrix(1 0 0-1 0 64)" gradientUnits="userSpaceOnUse" x2="0" y2="47" y1="61" id="b">
|
||||
<stop offset="0" stop-color="#fb9fa2" id="stop9" />
|
||||
<stop offset="1" stop-color="#fb7d80" id="stop7" />
|
||||
</linearGradient>
|
||||
<linearGradient gradientUnits="userSpaceOnUse" x2="54" y2="31" x1="40" y1="17" id="c">
|
||||
<stop id="stop12" stop-color="#383e51" />
|
||||
<stop id="stop14" stop-opacity="0" stop-color="#655c6f" offset="1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path id="path19" d="m10 61v-58h30l14 14v44h-14z" fill="url(#a)" />
|
||||
<g id="g25" transform="scale(1-1)">
|
||||
<rect id="rect21" fill-opacity=".412" height="1" fill="#ffffff" y="-4" x="10" width="30" />
|
||||
<rect id="rect23" fill-opacity=".294" height="1" fill="#2e3132" y="-61" x="10" width="44" />
|
||||
</g>
|
||||
<g id="g31" fill-rule="evenodd">
|
||||
<path id="path27" d="m54 17l-14-14v14z" fill="url(#b)" />
|
||||
<path id="path29" d="m40 17l14 14v-14z" fill="url(#c)" opacity=".2" />
|
||||
</g>
|
||||
<path id="path33" d="m32 19.333c-4.432 0-8 3.568-8 8 0 2.585 1.22 4.869 3.111 6.33v12.337l4.889-3.556 4.889 3.556v-12.337c1.891-1.461 3.111-3.745 3.111-6.33 0-4.432-3.568-8-8-8" fill="#fcbcbe" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB |
@@ -1,26 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="3" y2="61" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0-1 0 64)">
|
||||
<stop stop-color="#f62459"/>
|
||||
<stop offset="1" stop-color="#f73768"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="61" y2="47" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0-1 0 64)">
|
||||
<stop stop-color="#ffd6af"/>
|
||||
<stop offset="1" stop-color="#fffcf9"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c" y1="17" x1="40" y2="31" x2="54" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#383e51"/>
|
||||
<stop offset="1" stop-color="#655c6f" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="url(#a)" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
<g transform="scale(1-1)">
|
||||
<rect width="30" x="10" y="-4" fill="#ffffff" height="1" fill-opacity=".412"/>
|
||||
<rect width="44" x="10" y="-61" fill="#2e3132" height="1" fill-opacity=".294"/>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path fill="url(#b)" d="m54 17l-14-14v14z"/>
|
||||
<path opacity=".2" fill="url(#c)" d="m40 17l14 14v-14z"/>
|
||||
<path fill="#fffcf9" d="m24 22v7h-3v11h6v4h10v-4h6v-11h-3v-7zm1 1h14v6h-2v-1h-10v1h-2zm2 2v1h10v-1zm-5 5h20v9h-3v-3h-14v3h-3zm14 2v1h4v-1zm-8 8h8v3h-8z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 12 KiB |
@@ -1,28 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="17" y2="31" x1="40" x2="54" gradientUnits="userSpaceOnUse" gradientTransform="translate(302 78.36)">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="392.36" y2="336.36" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="translate(254-254)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m312 139.36v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<g transform="translate(-302-78.36)">
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<use fill="#fc963a" xlink:href="#c"/>
|
||||
<g transform="scale(1-1)">
|
||||
<rect opacity=".5" x="312" y="-82.36" width="30" fill="#ffffff" height="1"/>
|
||||
<rect opacity=".25" x="312" y="-139.36" width="44" height="1"/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path opacity=".5" fill="#ffffff" d="m356 95.36l-14-14v14z"/>
|
||||
<path opacity=".1" fill="url(#a)" d="m342 95.36l14 14v-14z"/>
|
||||
</g>
|
||||
<use fill="url(#b)" xlink:href="#c"/>
|
||||
<path opacity=".75" color-interpolation-filters="linearRGB" color="#000000" image-rendering="auto" color-rendering="auto" d="m324.04 100.33v3a18.999996 18.999996 0 0 1 19 19h3a21.999996 21.999996 0 0 0 -22 -22m0 7v3a11.999996 11.999996 0 0 1 12 12h3a14.999996 14.999996 0 0 0 -15 -15m3.5 8c-1.939 0-3.5 1.561-3.5 3.5 0 1.939 1.561 3.5 3.5 3.5 1.939 0 3.5-1.561 3.5-3.5 0-1.939-1.561-3.5-3.5-3.5" color-interpolation="sRGB" text-rendering="auto" fill="#ffffff" shape-rendering="auto"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1,24 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="17" x1="40" y2="31" gradientUnits="userSpaceOnUse" x2="54">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="392.36" y2="336.36" gradientUnits="userSpaceOnUse" x2="0" gradientTransform="translate(-48-332.36)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<use fill="#0c2d63" xlink:href="#c"/>
|
||||
<g transform="scale(1-1)">
|
||||
<rect opacity=".5" x="10" y="-4" width="30" fill="#ffffff" height="1"/>
|
||||
<rect opacity=".25" x="10" y="-61" width="44" height="1"/>
|
||||
</g>
|
||||
<path fill="#1d70f7" d="m54 17l-14-14v14z"/>
|
||||
</g>
|
||||
<path opacity=".2" fill="url(#a)" fill-rule="evenodd" d="m40 17l14 14v-14z"/>
|
||||
<path fill="#1d70f7" d="m21 22v1h22v-1zm0 3v1h22v-1zm0 3v1h11v-1zm0 4v1h22v-1zm0 3v1h19v-1zm0 2v1h11v-1zm0 3v1h22v-1zm0 3v1h16v-1z"/>
|
||||
<use fill="url(#b)" xlink:href="#c"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,24 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="17" x1="40" y2="31" x2="54" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#383e51"/>
|
||||
<stop offset="1" stop-color="#655c6f" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="2.978" y2="60.978" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0-1 0 63.978)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m10 60.978v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<use fill="#8542c2" xlink:href="#c"/>
|
||||
<g transform="scale(1-1)">
|
||||
<rect width="30" x="10" opacity=".5" y="-4" fill="#ffffff" height="1"/>
|
||||
<rect width="44" x="10" opacity=".25" y="-61" height="1"/>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path opacity=".5" fill="#ffffff" d="m54 17l-14-14v14z"/>
|
||||
<path opacity=".2" fill="url(#a)" d="m40 17l14 14v-14z"/>
|
||||
</g>
|
||||
<path opacity=".75" color-interpolation-filters="linearRGB" color="#4d4d4d" image-rendering="auto" color-rendering="auto" d="m32 21.99999a11 3 0 0 0 -11 3v16a11 3 0 0 0 11 3 11 3 0 0 0 11 -3v-16a11 3 0 0 0 -11 -3m0 1a10 2 0 0 1 10 2 10 2 0 0 1 -10 2 10 2 0 0 1 -10 -2 10 2 0 0 1 10 -2m-10 3.24219a11 3 0 0 0 10 1.75781 11 3 0 0 0 10 -1.75781v3.758a10 2 0 0 1 -10 2 10 2 0 0 1 -10 -2zm0 5a11 3 0 0 0 10 1.75781 11 3 0 0 0 10 -1.75781v4.758a10 2 0 0 1 -10 2 10 2 0 0 1 -10 -2zm0 6a11 3 0 0 0 10 1.75781 11 3 0 0 0 10 -1.75781v3.758a10 2 0 0 1 -10 2 10 2 0 0 1 -10 -2v-1z" color-interpolation="sRGB" text-rendering="auto" fill="#ffffff" shape-rendering="auto"/>
|
||||
<use fill="url(#b)" xlink:href="#c"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,26 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="17" x1="40" y2="31" gradientUnits="userSpaceOnUse" x2="54" gradientTransform="translate(46 296.36)">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="392.36" y2="336.36" gradientUnits="userSpaceOnUse" x2="0" gradientTransform="translate(-2-36)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m56 357.36v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<g transform="translate(-46-296.36)">
|
||||
<use fill="#1bb2fb" xlink:href="#c"/>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000" transform="scale(1-1)">
|
||||
<rect opacity=".5" x="56" y="-300.36" width="30" fill="#ffffff" height="1"/>
|
||||
<rect opacity=".25" x="56" y="-357.36" width="44" height="1"/>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path opacity=".5" fill="#ffffff" d="m100 313.36l-14-14v14z"/>
|
||||
<path opacity=".2" fill="url(#a)" d="m86 313.36l14 14v-14z"/>
|
||||
</g>
|
||||
<path opacity=".9" color-interpolation-filters="linearRGB" color="#000000" image-rendering="auto" color-rendering="auto" d="m67 318.36v1h22v-1zm0 3v1h22v-1zm0 3v1h11v-1zm0 4v1h22v-1zm0 3v1h9v-1zm13 0v9h9v-9zm1 1h7v7h-7zm-14 1v1h5v-1zm0 3v1h10v-1zm0 3v1h6v-1z" color-interpolation="sRGB" text-rendering="auto" fill="#ffffff" shape-rendering="auto"/>
|
||||
<use fill="url(#b)" xlink:href="#c"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1,139 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<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="64"
|
||||
version="1.1"
|
||||
viewBox="0 0 64 64"
|
||||
height="64"
|
||||
id="svg2"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="android-package-archive.svg">
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1360"
|
||||
inkscape:window-height="708"
|
||||
id="namedview33"
|
||||
showgrid="true"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:zoom="5.6568542"
|
||||
inkscape:cx="50.861875"
|
||||
inkscape:cy="21.2677"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-nodes="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4162" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient4300-2">
|
||||
<stop
|
||||
id="stop4302-4"
|
||||
style="stop-color:#3a539b" />
|
||||
<stop
|
||||
id="stop4304-1"
|
||||
style="stop-color:#3f5aa9"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient6251">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0"
|
||||
offset="0"
|
||||
id="stop6253" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0.2"
|
||||
offset="1"
|
||||
id="stop6255" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6251"
|
||||
id="linearGradient7145-0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-58,-335.3622)"
|
||||
x1="58"
|
||||
y1="392.36221"
|
||||
x2="58"
|
||||
y2="336.36221" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6251"
|
||||
id="linearGradient5849"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.84587337,-47,-272.73372)"
|
||||
x1="58"
|
||||
y1="403.41098"
|
||||
x2="58"
|
||||
y2="323.82297" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata84">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<rect
|
||||
y="11.000853"
|
||||
x="7"
|
||||
height="49.999977"
|
||||
width="49.999977"
|
||||
id="rect5837"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#9bd916;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<rect
|
||||
width="50"
|
||||
x="7"
|
||||
y="60.000854"
|
||||
height="1.0000085"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.25;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect5839" />
|
||||
<rect
|
||||
width="50"
|
||||
x="7"
|
||||
y="11.000853"
|
||||
height="1.0000085"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.5;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect5841" />
|
||||
<path
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.75;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 22,39 c -2.98896,7.5e-4 -5.84891,1.21778 -7.92188,3.37109 l -2.7246,-2.72461 c -0.0942,-0.0974 -0.2239,-0.15234 -0.35938,-0.15234 -0.4494,9e-5 -0.67059,0.54683 -0.34766,0.85937 l 2.76954,2.76954 C 11.85252,45.07416 11.00038,47.49972 11,50 l 0,7 22,0 0,-7 c -0.004,-2.4983 -0.85795,-4.92089 -2.42188,-6.86914 l 2.77735,-2.77735 c 0.32293,-0.31254 0.10175,-0.85928 -0.34766,-0.85937 -0.13548,0 -0.26516,0.055 -0.35937,0.15234 L 29.91602,42.3789 C 27.8458,40.22417 24.98808,39.00438 22,39 Z m 0,1 c 5.52285,0 10,4.47715 10,10 l 0,6 -20,0 0,-6 c 0,-5.52285 4.47715,-10 10,-10 z m -4.5,5 C 16.67157,45 16,45.67157 16,46.5 16,47.32843 16.67157,48 17.5,48 18.32843,48 19,47.32843 19,46.5 19,45.67157 18.32843,45 17.5,45 Z m 9,0 C 25.67157,45 25,45.67157 25,46.5 25,47.32843 25.67157,48 26.5,48 27.32843,48 28,47.32843 28,46.5 28,45.67157 27.32843,45 26.5,45 Z"
|
||||
id="path5845"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccccccccccsccccsssssssssss" />
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.55199998;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient5849);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect5847"
|
||||
width="49.999977"
|
||||
height="49.999977"
|
||||
x="7"
|
||||
y="11.000853" />
|
||||
<path
|
||||
style="opacity:0.75;fill:#ffffff;fill-opacity:1"
|
||||
d="M 40 12 L 40 14 L 42 14 L 42 12 L 40 12 z M 42 14 L 42 16 L 44 16 L 44 14 L 42 14 z M 42 16 L 40 16 L 40 18 L 42 18 L 42 16 z M 42 18 L 42 20 L 44 20 L 44 18 L 42 18 z M 42 20 L 40 20 L 40 22 L 42 22 L 42 20 z M 42 22 L 42 24 L 44 24 L 44 22 L 42 22 z M 42 24 L 40 24 L 40 26 L 42 26 L 42 24 z M 40 27 L 40 31 L 41 31 L 41 35 L 43 35 L 43 31 L 44 31 L 44 27 L 40 27 z M 41 28 L 43 28 L 43 30 L 41 30 L 41 28 z "
|
||||
id="rect4173-3" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 7.4 KiB |
@@ -1,106 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="a"
|
||||
y1="17"
|
||||
y2="31"
|
||||
x1="40"
|
||||
x2="54"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(238 78.36)">
|
||||
<stop
|
||||
stop-color="#060606"
|
||||
id="stop2" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-opacity="0"
|
||||
id="stop4" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="b"
|
||||
y1="392.36"
|
||||
y2="336.36"
|
||||
x2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(190-254)">
|
||||
<stop
|
||||
stop-color="#ffffff"
|
||||
stop-opacity="0"
|
||||
id="stop7" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#ffffff"
|
||||
stop-opacity=".2"
|
||||
id="stop9" />
|
||||
</linearGradient>
|
||||
<path
|
||||
id="c"
|
||||
d="m248 139.36v-58h30l14 14v44h-14z" />
|
||||
</defs>
|
||||
<g
|
||||
transform="translate(-238-78.36)">
|
||||
<use
|
||||
fill="#209ae7"
|
||||
xlink:href="#c" />
|
||||
<g
|
||||
color-rendering="auto"
|
||||
color-interpolation-filters="linearRGB"
|
||||
shape-rendering="auto"
|
||||
image-rendering="auto"
|
||||
text-rendering="auto"
|
||||
color-interpolation="sRGB"
|
||||
color="#000000"
|
||||
transform="scale(1-1)">
|
||||
<rect
|
||||
opacity=".5"
|
||||
x="248"
|
||||
y="-82.36"
|
||||
width="30"
|
||||
fill="#ffffff"
|
||||
height="1" />
|
||||
<rect
|
||||
opacity=".25"
|
||||
x="248"
|
||||
y="-139.36"
|
||||
width="44"
|
||||
height="1" />
|
||||
</g>
|
||||
<g
|
||||
fill-rule="evenodd">
|
||||
<path
|
||||
opacity=".5"
|
||||
fill="#ffffff"
|
||||
d="m292 95.36l-14-14v14z" />
|
||||
<path
|
||||
opacity=".2"
|
||||
fill="url(#a)"
|
||||
d="m278 95.36l14 14v-14z" />
|
||||
</g>
|
||||
<use
|
||||
fill="url(#b)"
|
||||
xlink:href="#c" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.8333382,0,0,1.8333306,2.6666187,3.6667279)">
|
||||
<rect
|
||||
width="11.69995"
|
||||
height="11.700049"
|
||||
rx="0.66966182"
|
||||
ry="0.66966754"
|
||||
x="10.15"
|
||||
y="10.14995"
|
||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.29999995;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:.75" />
|
||||
<path
|
||||
d="m 15.099978,11.209723 v 1.800007 h -0.956246 l 1.856242,2.100009 1.856243,-2.100009 h -0.956246 v -1.800007 z"
|
||||
style="display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:.75;fill-rule:nonzero;stroke:none;stroke-width:0.30053622;marker:none;enable-background:accumulate" />
|
||||
<path
|
||||
style="display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:.75;fill-rule:nonzero;stroke:none;stroke-width:0.30053622;marker:none;enable-background:accumulate"
|
||||
d="m 15.671876,15.409741 c -0.05187,0 -0.08437,0.08847 -0.08437,0.196875 v 0.68438 c -0.208817,0.04755 -0.405463,0.13271 -0.581249,0.243749 l -0.487497,-0.4875 c -0.07666,-0.07666 -0.1602,-0.111678 -0.196875,-0.07501 l -0.459373,0.459377 c -0.03668,0.03667 -0.0017,0.120227 0.075,0.196876 l 0.487498,0.487502 c -0.111038,0.175789 -0.196199,0.372439 -0.243748,0.581253 H 13.49689 c -0.108397,0 -0.196874,0.03252 -0.196874,0.08438 v 0.656253 c 0,0.05187 0.08848,0.08438 0.196874,0.08438 h 0.684373 c 0.04755,0.208816 0.13271,0.405466 0.243748,0.581254 l -0.487498,0.487501 c -0.07664,0.07666 -0.111676,0.160201 -0.075,0.196876 l 0.459373,0.459378 c 0.03667,0.03668 0.120224,0.0017 0.196875,-0.075 l 0.487498,-0.487504 c 0.175785,0.111041 0.372433,0.196202 0.581248,0.243751 v 0.684378 c 0,0.108398 0.03252,0.196876 0.08438,0.196876 h 0.656247 c 0.05186,0 0.08438,-0.08847 0.08438,-0.196876 v -0.684378 c 0.208815,-0.04755 0.405464,-0.132711 0.581248,-0.243751 l 0.487498,0.487503 c 0.07665,0.07666 0.160199,0.111676 0.196874,0.075 l 0.459374,-0.459375 c 0.03667,-0.03667 0.0017,-0.120226 -0.075,-0.196877 L 17.574991,19.10351 c 0.111038,-0.175788 0.1962,-0.372438 0.243748,-0.581254 h 0.684373 c 0.108397,0 0.196872,-0.03252 0.196872,-0.08438 v -0.656253 c 0,-0.05186 -0.08847,-0.08438 -0.196872,-0.08438 h -0.684375 c -0.04755,-0.208814 -0.132709,-0.405464 -0.243748,-0.581251 l 0.487498,-0.487503 c 0.07665,-0.07666 0.111677,-0.160198 0.07501,-0.196876 l -0.459372,-0.459377 c -0.03667,-0.03667 -0.120224,-0.0017 -0.196873,0.07501 l -0.487499,0.4875 C 16.81796,16.423704 16.621312,16.338541 16.412495,16.290994 v -0.68438 c 0,-0.108399 -0.03252,-0.196875 -0.08438,-0.196875 z M 16,17.275374 c 0.45858,0 0.834368,0.375792 0.834368,0.834377 0,0.458588 -0.375789,0.834379 -0.834368,0.834379 -0.458583,0 -0.834373,-0.375791 -0.834373,-0.834379 0,-0.458585 0.37579,-0.834377 0.834373,-0.834377 z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.7 KiB |
@@ -1,26 +0,0 @@
|
||||
<svg version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" x1="40" x2="54" y1="17" y2="31" gradientTransform="translate(238 78.36)" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#060606" offset="0"/>
|
||||
<stop stop-opacity="0" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" x2="0" y1="392.36" y2="336.36" gradientTransform="translate(190,-254)" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#fff" stop-opacity="0" offset="0"/>
|
||||
<stop stop-color="#fff" stop-opacity=".2" offset="1"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m248 139.36v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<g transform="translate(-238-78.36)">
|
||||
<use fill="#209ae7" xlink:href="#c"/>
|
||||
<g transform="scale(1-1)" color="#000000" color-rendering="auto" image-rendering="auto" shape-rendering="auto" text-rendering="auto">
|
||||
<rect x="248" y="-82.36" width="30" height="1" fill="#fff" opacity=".5"/>
|
||||
<rect x="248" y="-139.36" width="44" height="1" opacity=".25"/>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path d="m292 95.36-14-14v14z" fill="#fff" opacity=".5"/>
|
||||
<path d="m278 95.36 14 14v-14z" fill="url(#a)" opacity=".2"/>
|
||||
</g>
|
||||
<path transform="translate(238,78.36)" d="m22 22v6h-2v15c0 0.76175 0.26134 1 1 1h23v-15c0-0.76175-0.26135-1-1-1h-6v-6zm1 1h13v5h-4c-0.62732-0.86256-1.45448-1.49663-2.5-1.5-1.04829 8.5e-4 -1.87103 0.63517-2.5 1.5h-4zm-2 6h22v4h-9.666016c-1.47733 0-2.833984 1.4765-2.833984 3s1.360792 3.1105 2.833984 3h9.666016v4h-22zm14 5.5c0.73636 0 1.5 0.7406 1.5 1.5 0 0.75939-0.76364 1.5-1.5 1.5-0.7364 0-1.5-0.74061-1.5-1.5 0-0.7594 0.7636-1.5 1.5-1.5z" fill="#fff" opacity=".75"/>
|
||||
<use width="100%" height="100%" fill="url(#b)" xlink:href="#c"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 18 KiB |
@@ -1,26 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="17" y2="31" x1="40" x2="54" gradientUnits="userSpaceOnUse" gradientTransform="translate(432 1154.36)">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="392.36" y2="336.36" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="translate(384 822)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m442 1215.36v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<g transform="translate(-432-1154.36)">
|
||||
<use fill="#cf74e0" xlink:href="#c"/>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000" transform="scale(1-1)">
|
||||
<rect opacity=".5" x="442" y="-1158.36" width="30" fill="#ffffff" height="1"/>
|
||||
<rect opacity=".25" x="442" y="-1215.36" width="44" height="1"/>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path opacity=".5" fill="#ffffff" d="m486 1171.36l-14-14v14z"/>
|
||||
<path opacity=".1" fill="url(#a)" d="m472 1171.36l14 14v-14z"/>
|
||||
</g>
|
||||
<path opacity=".75" color-interpolation-filters="linearRGB" color="#000000" image-rendering="auto" color-rendering="auto" d="m466.05 1179.04l-5.318 17.59h1.045l5.318-17.59zm-9.05 3.318l-6 6 6 6 .707-.707-5.293-5.293 5.293-5.293zm12.707.25l-.707.707 5.293 5.293-5.293 5.293.707.707 6-6-.707-.707z" color-interpolation="sRGB" text-rendering="auto" fill="#ffffff" shape-rendering="auto"/>
|
||||
<use fill="url(#b)" xlink:href="#c"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,227 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<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="64"
|
||||
viewBox="0 0 64 64"
|
||||
height="64"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="application-vnd.iccprofile.svg">
|
||||
<metadata
|
||||
id="metadata39">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1025"
|
||||
id="namedview37"
|
||||
showgrid="false"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:zoom="8"
|
||||
inkscape:cx="14.009296"
|
||||
inkscape:cy="19.617126"
|
||||
inkscape:current-layer="svg2"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4226" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="a"
|
||||
y1="3"
|
||||
y2="61"
|
||||
x2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1 0 0-1 0 64)">
|
||||
<stop
|
||||
stop-color="#f39c12"
|
||||
id="stop7" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#f89406"
|
||||
id="stop9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="b"
|
||||
y1="61"
|
||||
y2="47"
|
||||
x2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1 0 0-1 0 64)">
|
||||
<stop
|
||||
stop-color="#fddda3"
|
||||
id="stop12" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#fde3a7"
|
||||
id="stop14" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="c"
|
||||
y1="17"
|
||||
x1="40"
|
||||
y2="31"
|
||||
x2="54"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop
|
||||
stop-color="#383e51"
|
||||
id="stop17" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#655c6f"
|
||||
stop-opacity="0"
|
||||
id="stop19" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(0,-1)"
|
||||
x2="54"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="31"
|
||||
x1="40"
|
||||
y1="17"
|
||||
id="a-3">
|
||||
<stop
|
||||
id="stop4172"
|
||||
stop-color="#060606" />
|
||||
<stop
|
||||
id="stop4174"
|
||||
stop-opacity="0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(254,-36)"
|
||||
x2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="336.36"
|
||||
y1="392.36"
|
||||
id="b-6">
|
||||
<stop
|
||||
id="stop4177"
|
||||
stop-opacity="0"
|
||||
stop-color="#ffffff" />
|
||||
<stop
|
||||
id="stop4179"
|
||||
stop-opacity=".2"
|
||||
stop-color="#ffffff"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<path
|
||||
d="m312 357.36v-58h30l14 14v44h-14z"
|
||||
id="c-7" />
|
||||
<linearGradient
|
||||
gradientTransform="translate(254,-36)"
|
||||
x2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="336.36"
|
||||
y1="392.36"
|
||||
id="b-6-3">
|
||||
<stop
|
||||
id="stop4177-6"
|
||||
stop-opacity="0"
|
||||
stop-color="#ffffff" />
|
||||
<stop
|
||||
id="stop4179-7"
|
||||
stop-opacity=".2"
|
||||
stop-color="#ffffff"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(254,-36)"
|
||||
x2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="336.36"
|
||||
y1="392.36"
|
||||
id="linearGradient4168">
|
||||
<stop
|
||||
id="stop4170"
|
||||
stop-opacity="0"
|
||||
stop-color="#ffffff" />
|
||||
<stop
|
||||
id="stop4172-5"
|
||||
stop-opacity=".2"
|
||||
stop-color="#ffffff"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<use
|
||||
id="use4186"
|
||||
xlink:href="#c-7"
|
||||
style="color:#000000;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#f9d24c;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;fill-opacity:1"
|
||||
x="0"
|
||||
y="0"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(-302,-297.36)" />
|
||||
<g
|
||||
style="color:#000000;color-interpolation:sRGB;color-interpolation-filters:linearRGB;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto"
|
||||
id="g4188"
|
||||
transform="matrix(1,0,0,-1,-302,-297.36)">
|
||||
<rect
|
||||
id="rect4190"
|
||||
height="1"
|
||||
width="30"
|
||||
y="-300.35999"
|
||||
x="312"
|
||||
style="opacity:0.5;fill:#ffffff" />
|
||||
<rect
|
||||
id="rect4192"
|
||||
height="1"
|
||||
width="44"
|
||||
y="-357.35999"
|
||||
x="312"
|
||||
style="opacity:0.25" />
|
||||
</g>
|
||||
<path
|
||||
id="path4196"
|
||||
d="M 54,16 40,2 40,16 Z"
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:0.5;fill:#ffffff;fill-rule:evenodd" />
|
||||
<path
|
||||
style="opacity:0.1;fill:url(#a-3);fill-rule:evenodd"
|
||||
id="path4198"
|
||||
d="M 40,16 54,30 54,16 Z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 32,21 c -1.876,7 -7,11.134 -7,15 0,3.866 3.134,7 7,7 3.866,0 7,-3.134 7,-7 0,-3.866 -5.124,-8 -7,-15 m 4.8125,11.427734 C 37.581303,33.460266 37.997636,34.712687 38,36 c 0,3.313708 -2.686292,6 -6,6 -1.284867,-0.0025 -2.535,-0.417376 -3.566406,-1.183594 C 28.947146,40.936416 29.472616,40.998005 30,41 c 3.865993,0 7,-3.134007 7,-7 -0.0028,-0.529493 -0.06574,-1.056956 -0.1875,-1.572266"
|
||||
id="path35"
|
||||
style="color:#4d4d4d;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.9;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
sodipodi:nodetypes="cssscccccccc" />
|
||||
<use
|
||||
height="100%"
|
||||
width="100%"
|
||||
y="0"
|
||||
x="0"
|
||||
xlink:href="#c-7"
|
||||
id="use4202"
|
||||
style="fill:url(#b-6)"
|
||||
transform="translate(-302,-297.36)" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.6 KiB |
@@ -1,26 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="61" y2="3" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0-1 0 64)">
|
||||
<stop stop-color="#e3200d"/>
|
||||
<stop offset="1" stop-color="#f22613"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="61" y2="47" x2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0-1 0 64)">
|
||||
<stop stop-color="#f87e72"/>
|
||||
<stop offset="1" stop-color="#fbaea7"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c" y1="17" x1="40" y2="31" x2="54" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#383e51"/>
|
||||
<stop offset="1" stop-color="#655c6f" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="url(#a)" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
<g transform="scale(1-1)">
|
||||
<rect width="30" x="10" y="-4" fill="#ffffff" height="1" fill-opacity=".412"/>
|
||||
<rect width="44" x="10" y="-61" fill="#2e3132" height="1" fill-opacity=".294"/>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path fill="url(#b)" d="m54 17l-14-14v14z"/>
|
||||
<path opacity=".2" fill="url(#c)" d="m40 17l14 14v-14z"/>
|
||||
</g>
|
||||
<path opacity=".75" fill="#ffffff" d="m32 22a11 3 0 0 0 -11 3v16a11 3 0 0 0 11 3 11 3 0 0 0 11 -3v-16A11 3 0 0 0 32 22m0 1A10 2 0 0 1 42 25 10 2 0 0 1 32 27 10 2 0 0 1 22 25 10 2 0 0 1 32 23M22 26.24219A11 3 0 0 0 32 28 11 3 0 0 0 42 26.24219V30A10 2 0 0 1 32 32 10 2 0 0 1 22 30zm0 5A11 3 0 0 0 32 33 11 3 0 0 0 42 31.24219V36A10 2 0 0 1 32 38 10 2 0 0 1 22 36zm0 6A11 3 0 0 0 32 39 11 3 0 0 0 42 37.24219V41A10 2 0 0 1 32 43 10 2 0 0 1 22 41v-1z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,26 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="392.36" y2="336.36" gradientUnits="userSpaceOnUse" x2="0" gradientTransform="translate(-48-332.36)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="17" x1="40" y2="31" gradientUnits="userSpaceOnUse" x2="54">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<use fill="#25bb70" xlink:href="#c"/>
|
||||
<g transform="scale(1-1)">
|
||||
<rect opacity=".5" x="10" y="-4" width="30" fill="#ffffff" height="1"/>
|
||||
<rect opacity=".25" x="10" y="-61" width="44" height="1"/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path opacity=".5" fill="#ffffff" d="m54 17l-14-14v14z"/>
|
||||
<path opacity=".2" fill="url(#b)" d="m40 17l14 14v-14z"/>
|
||||
</g>
|
||||
<path opacity=".75" color-interpolation-filters="linearRGB" color="#000000" image-rendering="auto" color-rendering="auto" stroke-width="2" d="m22 22c-.554 0-1 .446-1 1v20c0 .554.446 1 1 1h20c.554 0 1-.446 1-1v-20c0-.554-.446-1-1-1zm0 1h20v20h-20v-19zm1 1v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2z" color-interpolation="sRGB" text-rendering="auto" fill="#ffffff" shape-rendering="auto"/>
|
||||
<use fill="url(#a)" xlink:href="#c"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,26 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="392.36" y2="336.36" gradientUnits="userSpaceOnUse" x2="0" gradientTransform="translate(-48-332.36)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="17" x1="40" y2="31" gradientUnits="userSpaceOnUse" x2="54">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<use fill="#25bb70" xlink:href="#c"/>
|
||||
<g transform="scale(1-1)">
|
||||
<rect opacity=".5" x="10" y="-4" width="30" fill="#ffffff" height="1"/>
|
||||
<rect opacity=".25" x="10" y="-61" width="44" height="1"/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path opacity=".5" fill="#ffffff" d="m54 17l-14-14v14z"/>
|
||||
<path opacity=".2" fill="url(#b)" d="m40 17l14 14v-14z"/>
|
||||
</g>
|
||||
<path opacity=".75" color-interpolation-filters="linearRGB" color="#000000" image-rendering="auto" color-rendering="auto" stroke-width="2" d="m22 22c-.554 0-1 .446-1 1v20c0 .554.446 1 1 1h20c.554 0 1-.446 1-1v-20c0-.554-.446-1-1-1zm0 1h20v20h-20v-19zm1 1v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2z" color-interpolation="sRGB" text-rendering="auto" fill="#ffffff" shape-rendering="auto"/>
|
||||
<use fill="url(#a)" xlink:href="#c"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,26 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="392.36" y2="336.36" gradientUnits="userSpaceOnUse" x2="0" gradientTransform="translate(-48-332.36)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="17" x1="40" y2="31" gradientUnits="userSpaceOnUse" x2="54">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<use fill="#25bb70" xlink:href="#c"/>
|
||||
<g transform="scale(1-1)">
|
||||
<rect opacity=".5" x="10" y="-4" width="30" fill="#ffffff" height="1"/>
|
||||
<rect opacity=".25" x="10" y="-61" width="44" height="1"/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path opacity=".5" fill="#ffffff" d="m54 17l-14-14v14z"/>
|
||||
<path opacity=".2" fill="url(#b)" d="m40 17l14 14v-14z"/>
|
||||
</g>
|
||||
<path opacity=".75" color-interpolation-filters="linearRGB" color="#000000" image-rendering="auto" color-rendering="auto" stroke-width="2" d="m22 22c-.554 0-1 .446-1 1v20c0 .554.446 1 1 1h20c.554 0 1-.446 1-1v-20c0-.554-.446-1-1-1zm0 1h20v20h-20v-19zm1 1v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2z" color-interpolation="sRGB" text-rendering="auto" fill="#ffffff" shape-rendering="auto"/>
|
||||
<use fill="url(#a)" xlink:href="#c"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,26 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="392.36" y2="336.36" gradientUnits="userSpaceOnUse" x2="0" gradientTransform="translate(-48-332.36)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="17" x1="40" y2="31" gradientUnits="userSpaceOnUse" x2="54">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<use fill="#25bb70" xlink:href="#c"/>
|
||||
<g transform="scale(1-1)">
|
||||
<rect opacity=".5" x="10" y="-4" width="30" fill="#ffffff" height="1"/>
|
||||
<rect opacity=".25" x="10" y="-61" width="44" height="1"/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path opacity=".5" fill="#ffffff" d="m54 17l-14-14v14z"/>
|
||||
<path opacity=".2" fill="url(#b)" d="m40 17l14 14v-14z"/>
|
||||
</g>
|
||||
<path opacity=".75" color-interpolation-filters="linearRGB" color="#000000" image-rendering="auto" color-rendering="auto" stroke-width="2" d="m22 22c-.554 0-1 .446-1 1v20c0 .554.446 1 1 1h20c.554 0 1-.446 1-1v-20c0-.554-.446-1-1-1zm0 1h20v20h-20v-19zm1 1v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2z" color-interpolation="sRGB" text-rendering="auto" fill="#ffffff" shape-rendering="auto"/>
|
||||
<use fill="url(#a)" xlink:href="#c"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,25 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="392.36" y2="336.36" gradientUnits="userSpaceOnUse" x2="0" gradientTransform="translate(-48-332.36)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="17" x1="40" y2="31" gradientUnits="userSpaceOnUse" x2="54">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<use fill="#124c2f" xlink:href="#c"/>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000">
|
||||
<g fill="#ffffff">
|
||||
<path opacity=".1" d="m10 4v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h-2v1h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v2h1v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2v-1h-2v-2h2l-1-1h-1v-1l-1-1v2h-2v-2h2l-1-1h-1v-1l-1-1v2h-2v-2h2l-1-1h-1v-1l-1-1v2h-2v-2h2l-1-1h-1v-1l-1-1v2h-2v-2h2l-1-1h-29zm3 1h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-24 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-27 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-30 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-33 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm3 0h2v2h-2v-1zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-36 3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2z"/>
|
||||
<rect opacity=".5" x="10" y="-4" width="30" height="1" transform="scale(1-1)"/>
|
||||
</g>
|
||||
<rect opacity=".25" x="10" y="-61" width="44" height="1" transform="scale(1-1)"/>
|
||||
<path fill="#6ac798" d="m54 17l-14-14v14z"/>
|
||||
</g>
|
||||
<path opacity=".2" fill="url(#b)" fill-rule="evenodd" d="m40 17l14 14v-14z"/>
|
||||
<use opacity=".7" fill="url(#a)" xlink:href="#c"/>
|
||||
<path color-interpolation-filters="linearRGB" color="#000000" image-rendering="auto" color-rendering="auto" d="m22 22c-.554 0-1 .446-1 1v20c0 .554.446 1 1 1h20c.554 0 1-.446 1-1v-20c0-.554-.446-1-1-1zm0 1h20v20h-20v-19zm1 1v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2zm-13 4v2h5v-2zm6.5 0v2h5v-2zm6.5 0v2h5v-2z" color-interpolation="sRGB" text-rendering="auto" fill="#6ac798" shape-rendering="auto"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.0 KiB |
@@ -1,191 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<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="64"
|
||||
viewBox="0 0 64 64"
|
||||
height="64"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="application-vnd.ms-infopath.svg">
|
||||
<metadata
|
||||
id="metadata37">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="640"
|
||||
inkscape:window-height="480"
|
||||
id="namedview35"
|
||||
showgrid="false"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="41.123795"
|
||||
inkscape:cy="26.118387"
|
||||
inkscape:current-layer="svg2">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4208" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="a"
|
||||
y1="17"
|
||||
x1="40"
|
||||
y2="31"
|
||||
x2="54"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop
|
||||
stop-color="#383e51"
|
||||
id="stop7" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#655c6f"
|
||||
stop-opacity="0"
|
||||
id="stop9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="b"
|
||||
y1="2.978"
|
||||
y2="60.978"
|
||||
x2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,-1,0,63.978)">
|
||||
<stop
|
||||
stop-color="#ffffff"
|
||||
stop-opacity="0"
|
||||
id="stop12" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#ffffff"
|
||||
stop-opacity=".2"
|
||||
id="stop14" />
|
||||
</linearGradient>
|
||||
<path
|
||||
id="c"
|
||||
d="m10 60.978v-58h30l14 14v44h-14z" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6251"
|
||||
id="linearGradient7153"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-47.999999,-332.36221)"
|
||||
x1="58"
|
||||
y1="392.36221"
|
||||
x2="58"
|
||||
y2="336.36221" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient6251">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0"
|
||||
offset="0"
|
||||
id="stop6253" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0.2"
|
||||
offset="1"
|
||||
id="stop6255" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient9654"
|
||||
id="linearGradient9940"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="40"
|
||||
y1="17"
|
||||
x2="54"
|
||||
y2="31" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient9654">
|
||||
<stop
|
||||
style="stop-color:#060606;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop9656" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0"
|
||||
offset="1"
|
||||
id="stop9658" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g
|
||||
id="g4240">
|
||||
<path
|
||||
inkscape:export-ydpi="96"
|
||||
inkscape:export-xdpi="96"
|
||||
d="m 10.000001,61 0,-57.999999 30,0 L 54,17 l 0,30 0,14 -13.999999,0 -30,0 z"
|
||||
id="path6529"
|
||||
style="fill:#8542c2;fill-opacity:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
inkscape:export-ydpi="96"
|
||||
inkscape:export-xdpi="96"
|
||||
transform="scale(1,-1)"
|
||||
id="rect6531"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.5;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
height="1"
|
||||
y="-3.9999731"
|
||||
x="10"
|
||||
width="30" />
|
||||
<rect
|
||||
inkscape:export-ydpi="96"
|
||||
inkscape:export-xdpi="96"
|
||||
transform="scale(1,-1)"
|
||||
id="rect6533"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.25;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
height="1"
|
||||
y="-60.999973"
|
||||
x="10"
|
||||
width="44" />
|
||||
<path
|
||||
inkscape:export-ydpi="96"
|
||||
inkscape:export-xdpi="96"
|
||||
d="M 54,17 40.000001,3.000001 40.000001,17 Z"
|
||||
id="path6535"
|
||||
style="opacity:0.5;fill:#ffffff;fill-opacity:1;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:export-ydpi="96"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:0.2;fill:url(#linearGradient9940);fill-rule:evenodd"
|
||||
id="path9852"
|
||||
d="M 40.000001,17 54,30.99999 54,17 Z" />
|
||||
<path
|
||||
style="color:#4d4d4d;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.75;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;filter-blend-mode:normal;filter-gaussianBlur-deviation:0;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="path31"
|
||||
d="m36 20l-4 4.5-1.26 1.5h3.26v1h-8v11h-3.238l1.238 1.5 4 4.5 4-4.5 1.26-1.5h-3.26v-1h8v-11h3.238l-1.238-1.5zm0 1.504l3.107 3.496h-2.107v11h-7v-2h5v-9h-2.107zm-9 6.496h7v2h-5v9h2.107l-3.107 3.496-3.107-3.496h2.107v-1zm3 3h4v2h-4z"
|
||||
fill="#ffffff"
|
||||
opacity=".75" />
|
||||
<path
|
||||
inkscape:export-ydpi="96"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:url(#linearGradient7153);fill-opacity:1"
|
||||
id="path6977"
|
||||
d="m 10.000001,61 0,-57.999999 L 40,3.000001 54,17 l 0,30 0,14 -14,0 -29.999999,0 z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.9 KiB |
@@ -1,24 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="392.36" y2="336.36" gradientUnits="userSpaceOnUse" x2="0" gradientTransform="translate(-48-332.36)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="17" x1="40" y2="31" gradientUnits="userSpaceOnUse" x2="54">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<use fill="#fa752a" xlink:href="#c"/>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000" transform="scale(1-1)">
|
||||
<rect opacity=".5" x="10" y="-4" width="30" fill="#ffffff" height="1"/>
|
||||
<rect opacity=".25" x="10" y="-61" width="44" height="1"/>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path opacity=".5" fill="#ffffff" d="m54 17l-14-14v14z"/>
|
||||
<path opacity=".2" fill="url(#b)" d="m40 17l14 14v-14z"/>
|
||||
</g>
|
||||
<path opacity=".75" color-interpolation-filters="linearRGB" color="#000000" image-rendering="auto" color-rendering="auto" d="m22 22c-.554 0-1 .446-1 1v20c0 .554.446 1 1 1h20c.554 0 1-.446 1-1v-20c0-.554-.446-1-1-1zm0 1h20v20h-20zm12 2v4h4c0-2.209-1.791-4-4-4m-2 1a5 5 0 0 0 -5 5 5 5 0 0 0 5 5 5 5 0 0 0 5 -5 5 5 0 0 0 -.10156 -1h-3.898v-3.895a5 5 0 0 0 -1 -.105469m0 1v4h4a4 4 0 0 1 -4 4 4 4 0 0 1 -4 -4 4 4 0 0 1 4 -4m-8 11v1h16v-1zm0 2v1h16v-1z" color-interpolation="sRGB" text-rendering="auto" fill="#ffffff" shape-rendering="auto"/>
|
||||
<use fill="url(#a)" xlink:href="#c"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,24 +0,0 @@
|
||||
<svg width="64" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="a" y1="392.36" y2="336.36" gradientUnits="userSpaceOnUse" x2="0" gradientTransform="translate(-48-332.36)">
|
||||
<stop stop-color="#ffffff" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity=".2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" y1="17" x1="40" y2="31" gradientUnits="userSpaceOnUse" x2="54">
|
||||
<stop stop-color="#060606"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<path id="c" d="m10 61v-58h30l14 14v44h-14z"/>
|
||||
</defs>
|
||||
<use fill="#fa752a" xlink:href="#c"/>
|
||||
<g color-rendering="auto" color-interpolation-filters="linearRGB" shape-rendering="auto" image-rendering="auto" text-rendering="auto" color-interpolation="sRGB" color="#000000" transform="scale(1-1)">
|
||||
<rect opacity=".5" x="10" y="-4" width="30" fill="#ffffff" height="1"/>
|
||||
<rect opacity=".25" x="10" y="-61" width="44" height="1"/>
|
||||
</g>
|
||||
<g fill-rule="evenodd">
|
||||
<path opacity=".5" fill="#ffffff" d="m54 17l-14-14v14z"/>
|
||||
<path opacity=".2" fill="url(#b)" d="m40 17l14 14v-14z"/>
|
||||
</g>
|
||||
<path opacity=".75" color-interpolation-filters="linearRGB" color="#000000" image-rendering="auto" color-rendering="auto" d="m22 22c-.554 0-1 .446-1 1v20c0 .554.446 1 1 1h20c.554 0 1-.446 1-1v-20c0-.554-.446-1-1-1zm0 1h20v20h-20zm12 2v4h4c0-2.209-1.791-4-4-4m-2 1a5 5 0 0 0 -5 5 5 5 0 0 0 5 5 5 5 0 0 0 5 -5 5 5 0 0 0 -.10156 -1h-3.898v-3.895a5 5 0 0 0 -1 -.105469m0 1v4h4a4 4 0 0 1 -4 4 4 4 0 0 1 -4 -4 4 4 0 0 1 4 -4m-8 11v1h16v-1zm0 2v1h16v-1z" color-interpolation="sRGB" text-rendering="auto" fill="#ffffff" shape-rendering="auto"/>
|
||||
<use fill="url(#a)" xlink:href="#c"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |