Compare commits
302 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c2cea293d5 | |||
| ef2de71988 | |||
| f66af48d49 | |||
| 93f5745700 | |||
| 8e27fed9d3 | |||
| 8e8368ae0b | |||
| 8d165d63ed | |||
| d1e600773c | |||
| b0f0c26fe3 | |||
| 17f75d6797 | |||
| f17cabd63e | |||
| 43243380f1 | |||
| f22bae3546 | |||
| e9f5073677 | |||
| 4337de7559 | |||
| 05fd72937b | |||
| 7ebbd666a8 | |||
| 0709f4fd67 | |||
| da6743277b | |||
| 56a4c9d68e | |||
| 6176296c80 | |||
| 92793e9d92 | |||
| 0f1567d8fa | |||
| e2728aaf38 | |||
| 4fa0cac17c | |||
| ba7cc279e6 | |||
| a2f3f0a681 | |||
| a31439e89d | |||
| 932fcc9859 | |||
| 5b6590f1e9 | |||
| 85ba5adb33 | |||
| 2d7d8ca125 | |||
| 88e9542d12 | |||
| d4d3cecbf8 | |||
| b14e4ec6a6 | |||
| a098b57817 | |||
| c4539c34c9 | |||
| d1547ee3b0 | |||
| fc6c290a4f | |||
| f0cbeecf21 | |||
| b593fd1406 | |||
| 9342f97a28 | |||
| a95ccf4248 | |||
| d7a70aba84 | |||
| 41f8f68a5a | |||
| 7e651ffd54 | |||
| a637e0d686 | |||
| 56d5eb17fe | |||
| 173388dcd4 | |||
| 1bdf4f55cb | |||
| 47fa0988d9 | |||
| ed8e4cc785 | |||
| 16fa18ab49 | |||
| eefbcb32ed | |||
| 5f8c773b27 | |||
| 18968e84fc | |||
| d16aa27516 | |||
| 8f8d7cf9d3 | |||
| 5d8aaf9696 | |||
| 7c15b99a49 | |||
| f1e01dbbbc | |||
| 737af44b63 | |||
| 0911f3bde1 | |||
| e11dab4047 | |||
| 242f0c0b5a | |||
| 0667a32c83 | |||
| de1f223961 | |||
| 619ee56835 | |||
| 6171940717 | |||
| 02af9b2f6f | |||
| ff7237a098 | |||
| f4aae102f7 | |||
| 80842e1611 | |||
| d3d0d9e11d | |||
| 0045e823fe | |||
| 6e3c05f513 | |||
| 19ded76b77 | |||
| 34671d6ad4 | |||
| 3036fb95ae | |||
| 655e39ffcd | |||
| 07aba8cdac | |||
| 1b0416b1a1 | |||
| 9432c59db4 | |||
| 962b046ab5 | |||
| 6104a7043e | |||
| 479b9465c0 | |||
| d74bad2dc1 | |||
| 7717dbbe30 | |||
| e23f06561a | |||
| 46bb3fee81 | |||
| 9413f97b92 | |||
| 9f7e05e737 | |||
| a61608e8c7 | |||
| 364e7fe1be | |||
| cbba3e2432 | |||
| 59d6f45c41 | |||
| 369b3b28c7 | |||
| 7bd1aac209 | |||
| 22f9d0519a | |||
| 21551d7884 | |||
| 23559b2eda | |||
| a1af18fa35 | |||
| e33a9693f7 | |||
| 83b50690a8 | |||
| 89c0a0733f | |||
| d9405a2b02 | |||
| a93dac6a9c | |||
| bb85084c3d | |||
| 1da92fd5da | |||
| 2fd3fa20df | |||
| 61bd3631d9 | |||
| bc2032bed7 | |||
| b768040fca | |||
| a2672a2ad8 | |||
| 5a20f64300 | |||
| 03f127f162 | |||
| 14abe6a9e0 | |||
| d7e5a660ca | |||
| 2c6f3c88cf | |||
| 338795a03d | |||
| d33a93c6f9 | |||
| 30b9c139b6 | |||
| 6ff85e1dd6 | |||
| 018ff68f45 | |||
| 282b79c4b1 | |||
| 4c52fed779 | |||
| 4ecf31541b | |||
| 88ece3f5d7 | |||
| bf2f744bc7 | |||
| 8acec5309c | |||
| b02d3a27ba | |||
| fb2ebbd232 | |||
| f015d38a89 | |||
| 7e424e52a3 | |||
| 3c3d007068 | |||
| 1eb8aee0e6 | |||
| a4fbe80f73 | |||
| 5d2feda77c | |||
| fef3f23492 | |||
| 168f188596 | |||
| a5b73fe761 | |||
| 367770adab | |||
| 27aa0761bc | |||
| f1568b96ce | |||
| 86be2687fb | |||
| 58cc34b816 | |||
| d63caf5829 | |||
| a3de507b76 | |||
| 16a4e7192c | |||
| a4159378be | |||
| 535816a6d4 | |||
| b5029f8975 | |||
| 3d06d946b0 | |||
| 577a3cf145 | |||
| 09dbcd9647 | |||
| b631cc1286 | |||
| 964b511b39 | |||
| 5fd7de5275 | |||
| d9e66b2114 | |||
| f6737e43e9 | |||
| 38a03f3193 | |||
| de9865b9d9 | |||
| ef367f8bfe | |||
| adb9ad29fa | |||
| 7d1c9eef8d | |||
| 57e3900095 | |||
| 2e271313c9 | |||
| 2a6f8e65c2 | |||
| 3257bf2424 | |||
| 4c21fc802f | |||
| ad7c31914f | |||
| e47e1f6bdd | |||
| 3f91eaedfc | |||
| f248c7a583 | |||
| ef571d69f3 | |||
| 1b3e3dfada | |||
| e3fb91756a | |||
| 9c9c438c8b | |||
| 832c0c480b | |||
| 9586dc6c92 | |||
| 9bc0de9ab6 | |||
| 2845166e2a | |||
| 71cadf465b | |||
| 95ac2e31ae | |||
| a6bb42d3e8 | |||
| 197c97e242 | |||
| e2cb6a3b3c | |||
| 78e3b6400a | |||
| 55d5868da5 | |||
| f1cd334281 | |||
| 202dd62951 | |||
| 7701c7fab6 | |||
| 6296c498b2 | |||
| 5e3cb24efa | |||
| 36eb93deac | |||
| 38cb28db1f | |||
| f8740a1f0c | |||
| 7486d021b1 | |||
| 1ec43c8265 | |||
| b16824db31 | |||
| b55b1b5854 | |||
| c1423eec24 | |||
| b837d3f332 | |||
| 69b22c2d61 | |||
| bf24b4db24 | |||
| 31313178a5 | |||
| b2068704e7 | |||
| 0fc97b2104 | |||
| 9da8032b39 | |||
| 3b4285e13f | |||
| bc171d46e6 | |||
| de07c82a4d | |||
| b88aeb2166 | |||
| 976e5e35f7 | |||
| e268181b84 | |||
| 064ff027fd | |||
| 9e907901a0 | |||
| 7870cc2b67 | |||
| 9915ebb75f | |||
| 106af04355 | |||
| cee941a6a3 | |||
| 76c79ff18f | |||
| 73ae7b0c5f | |||
| d8e0a6ee32 | |||
| b2e3e29266 | |||
| 254be1dacd | |||
| 6989c65acf | |||
| 501685288b | |||
| d5e56b34c4 | |||
| 62f4454d99 | |||
| d9e229ab70 | |||
| f46573d836 | |||
| 2e6e969fbc | |||
| 88d5f6ec63 | |||
| 2cc1cdba6f | |||
| c39a40f15f | |||
| dabce9685b | |||
| 119de6467f | |||
| 22c083b58a | |||
| bb7a2b2329 | |||
| 10110e85eb | |||
| fb11969916 | |||
| f924a6d7d9 | |||
| c30e958dac | |||
| 7ab3a7e2c3 | |||
| 7b227d8712 | |||
| 5700467c29 | |||
| d61dd36fdd | |||
| d82d1a8a81 | |||
| fabf75a7d3 | |||
| 0aca61d73e | |||
| 2bed7a7f95 | |||
| 9dfd3544c2 | |||
| e79a755d08 | |||
| d76f08dfbd | |||
| 326af0c9c3 | |||
| 52149d2c54 | |||
| a0f62b71c8 | |||
| 1a21524f6f | |||
| 76636dea9c | |||
| 6b954e6cd6 | |||
| f259e1cb8c | |||
| 986623e2ac | |||
| 0209690d55 | |||
| 3b35c226ce | |||
| 3d10c3ace5 | |||
| 924298f740 | |||
| 823a14fae6 | |||
| 883817e62a | |||
| 4c431d39eb | |||
| 1ecf10e26d | |||
| 31b922d2f5 | |||
| bec72bf9ff | |||
| 6719c7723a | |||
| 0b3623a71d | |||
| eb03435377 | |||
| b7da6f13ff | |||
| 0a76d72a1d | |||
| 9cda3206ff | |||
| 809928c176 | |||
| 73a6717016 | |||
| 1703c25b26 | |||
| a0499e0258 | |||
| 92bc33dd1e | |||
| 266c64069f | |||
| 414d6e2184 | |||
| ed999066e5 | |||
| 9efdd684c4 | |||
| c042ae8d61 | |||
| 7ea3623cd9 | |||
| e643af16d3 | |||
| 4b2e98953b | |||
| c32a13fb5b | |||
| d639dfacb7 | |||
| 0fafa794da | |||
| 3a1390fdb7 | |||
| c25da25180 | |||
| 09908a737f | |||
| 43a53c0c8e | |||
| 3baa5fada8 | |||
| f2706cb572 | |||
| c7eaa26f73 |
@@ -1,15 +1,11 @@
|
||||
codecov:
|
||||
branch: master
|
||||
ci:
|
||||
- drone.nextcloud.com
|
||||
- !scrutinizer-ci.com
|
||||
|
||||
coverage:
|
||||
precision: 2
|
||||
round: down
|
||||
range: "70...100"
|
||||
status:
|
||||
project: off
|
||||
patch: off
|
||||
|
||||
comment: off
|
||||
comment:
|
||||
layout: "header, diff, changes, uncovered, tree"
|
||||
behavior: default
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# the default generated dir + db file
|
||||
/data
|
||||
/owncloud
|
||||
/config/config.php
|
||||
/config/*.config.php
|
||||
/config/mimetype*.json
|
||||
@@ -12,7 +13,6 @@
|
||||
|
||||
# ignore all apps except core ones
|
||||
/apps*/*
|
||||
!/apps/accessibility
|
||||
!/apps/comments
|
||||
!/apps/dav
|
||||
!/apps/files
|
||||
@@ -42,9 +42,6 @@
|
||||
/apps/files_external/3rdparty/irodsphp/prods/test*
|
||||
/apps/files_external/tests/config.*.php
|
||||
|
||||
# apps modules
|
||||
/apps/*/node_modules
|
||||
|
||||
|
||||
# ignore themes except the example and the README
|
||||
/themes/*
|
||||
@@ -121,7 +118,6 @@ nbproject
|
||||
/build/jsdocs/
|
||||
/npm-debug.log
|
||||
/PhantomJS_*
|
||||
/build/package-lock.json
|
||||
|
||||
# puphpet
|
||||
puphpet
|
||||
@@ -139,9 +135,6 @@ Vagrantfile
|
||||
/tests/autotest*
|
||||
/tests/data/lorem-copy.txt
|
||||
/tests/data/testimage-copy.png
|
||||
/tests/ui-regression/out/
|
||||
/tests/ui-regression/node_modules/
|
||||
/tests/ui-regression/package-lock.json
|
||||
/config/config-autotest-backup.php
|
||||
/config/autoconfig.php
|
||||
clover.xml
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
Header set X-Robots-Tag "none"
|
||||
Header set X-Download-Options "noopen"
|
||||
Header set X-Permitted-Cross-Domain-Policies "none"
|
||||
Header set Referrer-Policy "no-referrer"
|
||||
SetEnv modHeadersAvailable true
|
||||
</IfModule>
|
||||
|
||||
@@ -24,7 +23,7 @@
|
||||
<FilesMatch "\.(css|js|svg|gif)$">
|
||||
Header set Cache-Control "max-age=15778463"
|
||||
</FilesMatch>
|
||||
|
||||
|
||||
# Let browsers cache WOFF files for a week
|
||||
<FilesMatch "\.woff$">
|
||||
Header set Cache-Control "max-age=604800"
|
||||
@@ -64,7 +63,7 @@
|
||||
RewriteRule ^\.well-known/caldav /remote.php/dav/ [R=301,L]
|
||||
RewriteRule ^remote/(.*) remote.php [QSA,L]
|
||||
RewriteRule ^(?:build|tests|config|lib|3rdparty|templates)/.* - [R=404,L]
|
||||
RewriteCond %{REQUEST_URI} !^/\.well-known/(acme-challenge|pki-validation)/.*
|
||||
RewriteCond %{REQUEST_URI} !^/.well-known/(acme-challenge|pki-validation)/.*
|
||||
RewriteRule ^(?:\.|autotest|occ|issue|indie|db_|console).* - [R=404,L]
|
||||
</IfModule>
|
||||
<IfModule mod_mime.c>
|
||||
|
||||
@@ -127,9 +127,3 @@ file_filter = translationfiles/<lang>/workflowengine.po
|
||||
source_file = translationfiles/templates/workflowengine.pot
|
||||
source_lang = en
|
||||
type = PO
|
||||
|
||||
[nextcloud.accessibility]
|
||||
file_filter = translationfiles/<lang>/accessibility.po
|
||||
source_file = translationfiles/templates/accessibility.pot
|
||||
source_lang = en
|
||||
type = PO
|
||||
|
||||
@@ -58,7 +58,7 @@ like `git config --global alias.ci 'commit -s'`. Now you can commit with
|
||||
|
||||
In case you are not sure how to add or update the license header correctly please have a look at [contribute/HowToApplyALicense.md][applyalicense]
|
||||
|
||||
[devmanual]: https://docs.nextcloud.org/server/13/developer_manual/
|
||||
[devmanual]: https://docs.nextcloud.org/server/12/developer_manual/
|
||||
[dcofile]: https://github.com/nextcloud/server/blob/master/contribute/developer-certificate-of-origin
|
||||
[applyalicense]: https://github.com/nextcloud/server/blob/master/contribute/HowToApplyALicense.md
|
||||
|
||||
|
||||
@@ -36,16 +36,7 @@
|
||||
|
||||
[…learn more about how to get support for Nextcloud here!](https://nextcloud.com/support)
|
||||
|
||||
## Join the team :family:
|
||||
|
||||
### How to contribute
|
||||
|
||||
1. [Set up your local development environment](https://docs.nextcloud.com/server/14/developer_manual/general/devenv.html) :rocket:
|
||||
2. [Pick a good first issue](https://github.com/nextcloud/server/labels/good%20first%20issue) :notebook:
|
||||
3. Create a branch, a [Pull Request](https://opensource.guide/how-to-contribute/#opening-a-pull-request) and `@mention` the people from the issue :computer:
|
||||
4. Wait for it to get merged and :tada:
|
||||
|
||||
### Contribution Guidelines
|
||||
## Contribution Guidelines
|
||||
|
||||
All contributions to this repository from June, 16 2016 on are considered to be
|
||||
licensed under the AGPLv3 or any later version.
|
||||
@@ -65,7 +56,7 @@ Please review the [guidelines for contributing](https://github.com/nextcloud/ser
|
||||
|
||||
More information how to contribute: [https://nextcloud.com/contribute/](https://nextcloud.com/contribute/)
|
||||
|
||||
### Running master checkouts
|
||||
## Running master checkouts
|
||||
|
||||
Third-party components are handled as git submodules which have to be initialized first. So aside from the regular git checkout invoking `git submodule update --init` or a similar command is needed, for details see Git documentation.
|
||||
|
||||
@@ -74,7 +65,3 @@ Several apps that are included by default in regular releases such as [firstrunw
|
||||
That aside Git checkouts can be handled the same as release archives.
|
||||
|
||||
Note they should never be used on production systems.
|
||||
|
||||
## Tools we use
|
||||
|
||||
[](https://www.browserstack.com/)
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"browsers": ["last 2 versions", "not ie <= 11"]
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
@@ -1,16 +0,0 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es6: true
|
||||
},
|
||||
extends: 'eslint:recommended',
|
||||
parserOptions: {
|
||||
sourceType: 'module'
|
||||
},
|
||||
rules: {
|
||||
indent: ['error', 'tab'],
|
||||
'linebreak-style': ['error', 'unix'],
|
||||
quotes: ['error', 'single'],
|
||||
semi: ['error', 'always']
|
||||
}
|
||||
};
|
||||
@@ -1,12 +0,0 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
dist/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"esversion": 6
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
all: dev-setup build-js-production
|
||||
|
||||
dev-setup: clean clean-dev npm-init
|
||||
|
||||
npm-init:
|
||||
npm install
|
||||
|
||||
npm-update:
|
||||
npm update
|
||||
|
||||
build-js:
|
||||
npm run dev
|
||||
|
||||
build-js-production:
|
||||
npm run build
|
||||
|
||||
watch-js:
|
||||
npm run watch
|
||||
|
||||
clean:
|
||||
rm -f js/accessibility.js
|
||||
rm -f js/accessibility.js.map
|
||||
|
||||
clean-dev:
|
||||
rm -rf node_modules
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# Accessibility ♿
|
||||
|
||||
> This app provide multiple features to ease the use of nextcloud.
|
||||
|
||||
## Build Setup
|
||||
|
||||
``` bash
|
||||
# install dependencies
|
||||
make dev-setup
|
||||
|
||||
# build for development
|
||||
make build-js
|
||||
|
||||
# build for development and watch edits
|
||||
make watch-js
|
||||
|
||||
# build for production with minification
|
||||
make build-js-production
|
||||
|
||||
# clean output files
|
||||
make clean
|
||||
```
|
||||
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
$app = new \OCA\Accessibility\AppInfo\Application();
|
||||
|
||||
// Separate from the constructor since the route are not initialized before that
|
||||
// 1. create the app
|
||||
// 2. generate css route and inject
|
||||
$app->injectCss();
|
||||
@@ -1,23 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
|
||||
<id>accessibility</id>
|
||||
<name>Accessibility</name>
|
||||
<summary>Accessibility options for nextcloud</summary>
|
||||
<description><![CDATA[Provides multiple accessibilities options to ease your use of nextcloud]]></description>
|
||||
<version>1.0.1</version>
|
||||
<licence>agpl</licence>
|
||||
<author>John Molakvoæ</author>
|
||||
<namespace>Accessibility</namespace>
|
||||
<category>accessibility</category>
|
||||
<default_enable/>
|
||||
<dependencies>
|
||||
<nextcloud min-version="14" max-version="14"/>
|
||||
</dependencies>
|
||||
<default_enable/>
|
||||
<bugs>https://github.com/nextcloud/server/issues</bugs>
|
||||
<settings>
|
||||
<personal>OCA\Accessibility\Settings\Personal</personal>
|
||||
<personal-section>OCA\Accessibility\Settings\PersonalSection</personal-section>
|
||||
</settings>
|
||||
</info>
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
return [
|
||||
'routes' => [
|
||||
['name' => 'accessibility#getCss', 'url' => '/css/user-{md5}', 'verb' => 'GET'],
|
||||
],
|
||||
'ocs' => [
|
||||
[
|
||||
'name' => 'Config#getConfig',
|
||||
'url' => '/api/v1/config',
|
||||
'verb' => 'GET',
|
||||
],
|
||||
[
|
||||
'name' => 'Config#setConfig',
|
||||
'url' => '/api/v1/config/{key}',
|
||||
'verb' => 'POST',
|
||||
],
|
||||
]
|
||||
];
|
||||
@@ -1,22 +0,0 @@
|
||||
@font-face {
|
||||
font-family: 'OpenDyslexic';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url('../fonts/OpenDyslexic-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'OpenDyslexic';
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
src: url('../fonts/OpenDyslexic-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'OpenDyslexic';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: url('../fonts/OpenDyslexic-Bold.woff') format('woff');
|
||||
}
|
||||
|
||||
$font-face: OpenDyslexic, 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif;
|
||||
@@ -1,59 +0,0 @@
|
||||
.preview-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.preview {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 250px;
|
||||
max-width: 400px;
|
||||
flex: 1 1 300px;
|
||||
border: 1px solid var(--color-border);
|
||||
padding: 10px;
|
||||
border-radius: var(--border-radius);
|
||||
transition: all 200ms ease-in-out;
|
||||
filter: drop-shadow(0 1px 2px var(--color-box-shadow));
|
||||
background-color: var(--color-main-background);
|
||||
opacity: 0.9;
|
||||
margin: 10px;
|
||||
position: relative;
|
||||
&,
|
||||
* {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
&:hover,
|
||||
&:focus,
|
||||
&.selected {
|
||||
filter: drop-shadow(0 1px 4px var(--color-box-shadow));
|
||||
opacity: 1;
|
||||
}
|
||||
.preview-image {
|
||||
height: 200px;
|
||||
background-position: top left;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
h3 {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
line-height: 1em;
|
||||
align-items: center;
|
||||
}
|
||||
p {
|
||||
text-align: justify;
|
||||
}
|
||||
.icon-checkmark-color {
|
||||
transition: all 100ms ease-in-out;
|
||||
border-radius: 1em;
|
||||
padding: 4px 5px 4px 20px;
|
||||
background-position: 4px center;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
&.selected .icon-checkmark-color {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
box-shadow: 0 0 0 1px var(--color-success);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// SCSS variables
|
||||
$color-main-text: #d8d8d8;
|
||||
$color-main-background: #181818;
|
||||
|
||||
$color-background-dark: lighten($color-main-background, 4%);
|
||||
$color-background-darker: lighten($color-main-background, 8%);
|
||||
|
||||
$color-text-maxcontrast: darken($color-main-text, 30%);
|
||||
$color-text-light: darken($color-main-text, 10%);
|
||||
$color-text-lighter: darken($color-main-text, 20%);
|
||||
|
||||
$color-loading-light: #777;
|
||||
$color-loading-dark: #ccc;
|
||||
|
||||
$color-box-shadow: rgba(darken($color-main-background, 70%), 0.5);
|
||||
|
||||
$color-border: lighten($color-main-background, 7%);
|
||||
$color-border-dark: lighten($color-main-background, 14%);
|
||||
|
||||
#app-navigation > ul > li > a:first-child,
|
||||
#app-navigation > ul > li > ul > li > a:first-child,
|
||||
#expanddiv a {
|
||||
img {
|
||||
filter: invert(100%);
|
||||
}
|
||||
}
|
||||
.bubble,
|
||||
.app-navigation-entry-menu,
|
||||
.popovermenu {
|
||||
li {
|
||||
> button,
|
||||
> a,
|
||||
> .menuitem {
|
||||
> img {
|
||||
filter: invert(100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.bubble,
|
||||
.app-navigation-entry-menu,
|
||||
.popovermenu,
|
||||
#header .menu {
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
// since svg icons are inverted, revert to white for the header
|
||||
.header-right > * {
|
||||
[class^='icon-'], [class*=' icon-'] {
|
||||
filter: invert(100%);
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// Fonts
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
src: local('Open Sans'), local('OpenSans'),
|
||||
url('../../../core/fonts/OpenSans-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
/* overriding default light */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans'), local('OpenSans'),
|
||||
url('../../../core/fonts/OpenSans-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 500 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'),
|
||||
url('../../../core/fonts/OpenSans-Bold.woff') format('woff');
|
||||
}
|
||||
|
||||
// SCSS variables
|
||||
$color-main-text: #000;
|
||||
$color-main-background: #fff;
|
||||
|
||||
$color-background-dark: darken($color-main-background, 30%);
|
||||
$color-background-darker: darken($color-main-background, 30%);
|
||||
|
||||
$color-text-maxcontrast: $color-main-text;
|
||||
$color-text-light: $color-main-text;
|
||||
$color-text-lighter: $color-main-text;
|
||||
|
||||
$color-loading-light: #ddd;
|
||||
$color-loading-dark: #000;
|
||||
|
||||
$color-box-shadow: $color-main-text;
|
||||
|
||||
$color-border: darken($color-main-background, 50%);
|
||||
$color-border-dark: darken($color-main-background, 50%);
|
||||
|
||||
$font-face: 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif;
|
||||
|
||||
|
||||
// Font weight reset
|
||||
body {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
[class^='icon-'], [class*=' icon-'],
|
||||
.action {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
@@ -1,76 +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"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
sodipodi:docname="app-dark.svg"
|
||||
inkscape:version="0.92.2 5c3e80d, 2017-08-06">
|
||||
<metadata
|
||||
id="metadata8">
|
||||
<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>
|
||||
<defs
|
||||
id="defs6" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1880"
|
||||
inkscape:window-height="993"
|
||||
id="namedview4"
|
||||
showgrid="false"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:zoom="36.460193"
|
||||
inkscape:cx="8.4752826"
|
||||
inkscape:cy="18.273624"
|
||||
inkscape:window-x="20"
|
||||
inkscape:window-y="67"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="g848"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<g
|
||||
id="g848"
|
||||
transform="matrix(1.1307959,0,0,1.0801738,-1.0463882,-0.84269722)"
|
||||
style="stroke-width:0.90481776;fill:#000000">
|
||||
<circle
|
||||
r="1.9587879"
|
||||
cy="2.8315151"
|
||||
cx="7.990303"
|
||||
id="path844"
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.90481776;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke markers fill" />
|
||||
<path
|
||||
sodipodi:nodetypes="scsssscsccccczzcccccssssccsscs"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path843"
|
||||
d="m 2.3519709,4.2557871 c -0.3126291,0 -0.4819578,0.1287194 -0.5322266,0.4028321 -0.053173,0.2899914 0.1062609,0.5159057 0.4370117,0.6274414 1.420521,0.4790277 2.4256503,0.6675279 3.8410483,0.9800416 0.327839,0.072385 0.6900295,0.2893751 0.6520998,1.0948483 C 6.6985575,8.451345 6.6372144,9.7041167 6.2631031,11.323353 6.0397526,12.290062 5.6782112,13.622766 5.4305838,14.3922 5.3416728,14.668443 5.281658,14.922355 5.281658,15.031849 c 0,0.08257 0.053443,0.249642 0.097656,0.307617 0.023823,0.03125 0.075039,0.07397 0.1147462,0.09522 0.062879,0.03361 0.096262,0.03787 0.2612304,0.03663 0.3361373,-0.0026 0.4608113,-0.08245 0.6665037,-0.429687 0.4838657,-0.939315 0.7378785,-2.132191 0.9402993,-3.014266 0.092189,-0.517235 0.2624476,-1.66347 0.6710287,-1.66347 0.4085811,0 0.4979093,0.95008 0.6812062,1.687228 0.1832969,0.737148 0.6122738,2.295202 0.7738717,2.704862 0.2137476,0.54186 0.5742238,0.836985 0.8764648,0.717774 0.03316,-0.01301 0.116937,-0.04526 0.187989,-0.0708 0.147435,-0.053 0.186715,-0.07824 0.219726,-0.156249 0.0655,-0.154793 -0.01013,-0.454047 -0.349121,-1.41114 C 9.7721847,11.597192 9.2651806,9.3990255 9.3334308,7.2974743 9.3630343,6.3859259 9.6837054,6.305369 10.075725,6.2075087 c 1.17285,-0.2927798 2.00228,-0.379973 3.387085,-0.8579717 0.468513,-0.1617184 0.727539,-0.2608264 0.727539,-0.5590819 0,-0.2554049 -0.153084,-0.4346541 -0.437011,-0.5102538 -0.116851,-0.031101 -0.318383,-0.028905 -0.632326,0.00243 -1.085189,0.10834 -2.939683,0.5153868 -4.0234365,0.6710819 -0.529944,0.076133 -1.7229392,0.094015 -2.2729489,0 C 5.6763224,4.7574297 4.1363176,4.4147025 2.9061699,4.2997326 2.6560099,4.2763537 2.4068766,4.2557871 2.3519709,4.2557871 Z"
|
||||
style="fill:#000000;stroke-width:0.9048177" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.0 KiB |
@@ -1,76 +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"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
sodipodi:docname="app.svg"
|
||||
inkscape:version="0.92.2 5c3e80d, 2017-08-06">
|
||||
<metadata
|
||||
id="metadata8">
|
||||
<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>
|
||||
<defs
|
||||
id="defs6" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1880"
|
||||
inkscape:window-height="993"
|
||||
id="namedview4"
|
||||
showgrid="false"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:zoom="36.460193"
|
||||
inkscape:cx="8.4752826"
|
||||
inkscape:cy="18.273624"
|
||||
inkscape:window-x="20"
|
||||
inkscape:window-y="67"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="g848"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<g
|
||||
id="g848"
|
||||
transform="matrix(1.1307959,0,0,1.0801738,-1.0463882,-0.84269722)"
|
||||
style="stroke-width:0.90481776;fill:#000000">
|
||||
<circle
|
||||
r="1.9587879"
|
||||
cy="2.8315151"
|
||||
cx="7.990303"
|
||||
id="path844"
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.90481776;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke markers fill" />
|
||||
<path
|
||||
sodipodi:nodetypes="scsssscsccccczzcccccssssccsscs"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path843"
|
||||
d="m 2.3519709,4.2557871 c -0.3126291,0 -0.4819578,0.1287194 -0.5322266,0.4028321 -0.053173,0.2899914 0.1062609,0.5159057 0.4370117,0.6274414 1.420521,0.4790277 2.4256503,0.6675279 3.8410483,0.9800416 0.327839,0.072385 0.6900295,0.2893751 0.6520998,1.0948483 C 6.6985575,8.451345 6.6372144,9.7041167 6.2631031,11.323353 6.0397526,12.290062 5.6782112,13.622766 5.4305838,14.3922 5.3416728,14.668443 5.281658,14.922355 5.281658,15.031849 c 0,0.08257 0.053443,0.249642 0.097656,0.307617 0.023823,0.03125 0.075039,0.07397 0.1147462,0.09522 0.062879,0.03361 0.096262,0.03787 0.2612304,0.03663 0.3361373,-0.0026 0.4608113,-0.08245 0.6665037,-0.429687 0.4838657,-0.939315 0.7378785,-2.132191 0.9402993,-3.014266 0.092189,-0.517235 0.2624476,-1.66347 0.6710287,-1.66347 0.4085811,0 0.4979093,0.95008 0.6812062,1.687228 0.1832969,0.737148 0.6122738,2.295202 0.7738717,2.704862 0.2137476,0.54186 0.5742238,0.836985 0.8764648,0.717774 0.03316,-0.01301 0.116937,-0.04526 0.187989,-0.0708 0.147435,-0.053 0.186715,-0.07824 0.219726,-0.156249 0.0655,-0.154793 -0.01013,-0.454047 -0.349121,-1.41114 C 9.7721847,11.597192 9.2651806,9.3990255 9.3334308,7.2974743 9.3630343,6.3859259 9.6837054,6.305369 10.075725,6.2075087 c 1.17285,-0.2927798 2.00228,-0.379973 3.387085,-0.8579717 0.468513,-0.1617184 0.727539,-0.2608264 0.727539,-0.5590819 0,-0.2554049 -0.153084,-0.4346541 -0.437011,-0.5102538 -0.116851,-0.031101 -0.318383,-0.028905 -0.632326,0.00243 -1.085189,0.10834 -2.939683,0.5153868 -4.0234365,0.6710819 -0.529944,0.076133 -1.7229392,0.094015 -2.2729489,0 C 5.6763224,4.7574297 4.1363176,4.4147025 2.9061699,4.2997326 2.6560099,4.2763537 2.4068766,4.2557871 2.3519709,4.2557871 Z"
|
||||
style="fill:#ffffff;stroke-width:0.9048177" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 65 KiB |
@@ -1,83 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Accessibility;
|
||||
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
|
||||
class AccessibilityProvider {
|
||||
|
||||
/** @var string */
|
||||
protected $appName;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
/** @var IL10N */
|
||||
private $l;
|
||||
|
||||
/**
|
||||
* Account constructor.
|
||||
*
|
||||
* @param string $appName
|
||||
* @param IURLGenerator $urlGenerator
|
||||
* @param IL10N $l
|
||||
*/
|
||||
public function __construct(string $appName,
|
||||
IURLGenerator $urlGenerator,
|
||||
IL10N $l) {
|
||||
$this->appName = $appName;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->l = $l;
|
||||
}
|
||||
|
||||
public function getThemes() {
|
||||
return array(
|
||||
[
|
||||
'id' => 'themehighcontrast',
|
||||
'img' => $this->urlGenerator->imagePath($this->appName, 'theme-highcontrast.jpg'),
|
||||
'title' => $this->l->t('High contrast theme'),
|
||||
'text' => $this->l->t('A high contrast theme to ease your navigation. Visual quality will be reduced but clarity will be increased.')
|
||||
], [
|
||||
'id' => 'themedark',
|
||||
'img' => $this->urlGenerator->imagePath($this->appName, 'theme-dark.jpg'),
|
||||
'title' => $this->l->t('Dark theme (beta)'),
|
||||
'text' => $this->l->t('A dark theme to ease your eyes by reducing the overall luminosity and brightness. It is still under development, so please report any issues you may find.')
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getFonts() {
|
||||
return array(
|
||||
[
|
||||
'id' => 'fontdyslexic',
|
||||
'img' => $this->urlGenerator->imagePath($this->appName, 'font-opendyslexic.jpg'),
|
||||
'title' => $this->l->t('Dyslexia font'),
|
||||
'text' => $this->l->t('OpenDyslexic is a free typeface/font designed to mitigate some of the common reading errors caused by dyslexia. The typeface was created by Abelardo Gonzalez, who released it through an open-source license.')
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Accessibility\AppInfo;
|
||||
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\IConfig;
|
||||
use OCP\IUserSession;
|
||||
use OCP\IURLGenerator;
|
||||
|
||||
class Application extends App {
|
||||
|
||||
/** @var string */
|
||||
protected $appName = 'accessibility';
|
||||
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/** @var IUserSession */
|
||||
private $userSession;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct($this->appName);
|
||||
$this->config = \OC::$server->getConfig();
|
||||
$this->userSession = \OC::$server->getUserSession();
|
||||
$this->urlGenerator = \OC::$server->getURLGenerator();
|
||||
}
|
||||
|
||||
public function injectCss() {
|
||||
// Inject the fake css on all pages if enabled and user is logged
|
||||
$loggedUser = $this->userSession->getUser();
|
||||
if (!is_null($loggedUser)) {
|
||||
$userValues = $this->config->getUserKeys($loggedUser->getUID(), $this->appName);
|
||||
// we want to check if any theme or font is enabled.
|
||||
if (count($userValues) > 0) {
|
||||
$hash = $this->config->getUserValue($loggedUser->getUID(), $this->appName, 'icons-css', md5(implode('-', $userValues)));
|
||||
$linkToCSS = $this->urlGenerator->linkToRoute($this->appName . '.accessibility.getCss', ['md5' => $hash]);
|
||||
\OCP\Util::addHeader('link', ['rel' => 'stylesheet', 'href' => $linkToCSS]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,259 +0,0 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Accessibility\Controller;
|
||||
|
||||
use Leafo\ScssPhp\Compiler;
|
||||
use Leafo\ScssPhp\Exception\ParserException;
|
||||
use Leafo\ScssPhp\Formatter\Crunched;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataDisplayResponse;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\IConfig;
|
||||
use OCP\ILogger;
|
||||
use OCP\IRequest;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUserManager;
|
||||
use OCP\IUserSession;
|
||||
use OC\Template\IconsCacher;
|
||||
|
||||
class AccessibilityController extends Controller {
|
||||
|
||||
/** @var string */
|
||||
protected $appName;
|
||||
|
||||
/** @var string */
|
||||
protected $serverRoot;
|
||||
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
/** @var ITimeFactory */
|
||||
protected $timeFactory;
|
||||
|
||||
/** @var IUserSession */
|
||||
private $userSession;
|
||||
|
||||
/** @var IAppManager */
|
||||
private $appManager;
|
||||
|
||||
/** @var IconsCacher */
|
||||
protected $iconsCacher;
|
||||
|
||||
/** @var \OC_Defaults */
|
||||
private $defaults;
|
||||
|
||||
/** @var null|string */
|
||||
private $injectedVariables;
|
||||
|
||||
/**
|
||||
* Account constructor.
|
||||
*
|
||||
* @param string $appName
|
||||
* @param IRequest $request
|
||||
* @param IConfig $config
|
||||
* @param IUserManager $userManager
|
||||
* @param ILogger $logger
|
||||
* @param IURLGenerator $urlGenerator
|
||||
* @param ITimeFactory $timeFactory
|
||||
* @param IUserSession $userSession
|
||||
* @param IAppManager $appManager
|
||||
* @param \OC_Defaults $defaults
|
||||
*/
|
||||
public function __construct(string $appName,
|
||||
IRequest $request,
|
||||
IConfig $config,
|
||||
IUserManager $userManager,
|
||||
ILogger $logger,
|
||||
IURLGenerator $urlGenerator,
|
||||
ITimeFactory $timeFactory,
|
||||
IUserSession $userSession,
|
||||
IAppManager $appManager,
|
||||
IconsCacher $iconsCacher,
|
||||
\OC_Defaults $defaults) {
|
||||
parent::__construct($appName, $request);
|
||||
$this->appName = $appName;
|
||||
$this->config = $config;
|
||||
$this->userManager = $userManager;
|
||||
$this->logger = $logger;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->timeFactory = $timeFactory;
|
||||
$this->userSession = $userSession;
|
||||
$this->appManager = $appManager;
|
||||
$this->iconsCacher = $iconsCacher;
|
||||
$this->defaults = $defaults;
|
||||
|
||||
$this->serverRoot = \OC::$SERVERROOT;
|
||||
$this->appRoot = $this->appManager->getAppPath($this->appName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @NoCSRFRequired
|
||||
*
|
||||
* @return DataDisplayResponse
|
||||
*/
|
||||
public function getCss(): DataDisplayResponse {
|
||||
$css = '';
|
||||
$imports = '';
|
||||
$userValues = $this->getUserValues();
|
||||
|
||||
foreach ($userValues as $key => $scssFile) {
|
||||
if ($scssFile !== false) {
|
||||
$imports .= '@import "' . $scssFile . '";';
|
||||
}
|
||||
}
|
||||
|
||||
if ($imports !== '') {
|
||||
$scss = new Compiler();
|
||||
$scss->setImportPaths([
|
||||
$this->appRoot . '/css/',
|
||||
$this->serverRoot . '/core/css/'
|
||||
]);
|
||||
|
||||
// Continue after throw
|
||||
$scss->setIgnoreErrors(true);
|
||||
$scss->setFormatter(Crunched::class);
|
||||
|
||||
// Import theme, variables and compile css4 variables
|
||||
try {
|
||||
$css .= $scss->compile(
|
||||
$imports .
|
||||
$this->getInjectedVariables() .
|
||||
'@import "variables.scss";' .
|
||||
'@import "css-variables.scss";'
|
||||
);
|
||||
} catch (ParserException $e) {
|
||||
$this->logger->error($e->getMessage(), ['app' => 'core']);
|
||||
}
|
||||
}
|
||||
|
||||
// We don't want to override vars with url since path is different
|
||||
$css = $this->filterOutRule('/--[a-z-:]+url\([^;]+\)/mi', $css);
|
||||
|
||||
// Rebase all urls
|
||||
$appWebRoot = substr($this->appRoot, strlen($this->serverRoot) - strlen(\OC::$WEBROOT));
|
||||
$css = $this->rebaseUrls($css, $appWebRoot . '/css');
|
||||
|
||||
if (in_array('themedark', $userValues) && $this->iconsCacher->getCachedCSS() && $this->iconsCacher->getCachedCSS()->getSize() > 0) {
|
||||
$iconsCss = $this->invertSvgIconsColor($this->iconsCacher->getCachedCSS()->getContent());
|
||||
$css = $css . $iconsCss;
|
||||
}
|
||||
|
||||
$response = new DataDisplayResponse($css, Http::STATUS_OK, ['Content-Type' => 'text/css']);
|
||||
|
||||
// Set cache control
|
||||
$ttl = 31536000;
|
||||
$response->addHeader('Cache-Control', 'max-age=' . $ttl . ', immutable');
|
||||
$expires = new \DateTime();
|
||||
$expires->setTimestamp($this->timeFactory->getTime());
|
||||
$expires->add(new \DateInterval('PT' . $ttl . 'S'));
|
||||
$response->addHeader('Expires', $expires->format(\DateTime::RFC1123));
|
||||
$response->addHeader('Pragma', 'cache');
|
||||
|
||||
// store current cache hash
|
||||
$this->config->setUserValue($this->userSession->getUser()->getUID(), $this->appName, 'icons-css', md5($css));
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array with the user theme & font settings
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getUserValues(): array{
|
||||
$userTheme = $this->config->getUserValue($this->userSession->getUser()->getUID(), $this->appName, 'theme', false);
|
||||
$userFont = $this->config->getUserValue($this->userSession->getUser()->getUID(), $this->appName, 'font', false);
|
||||
|
||||
return [$userTheme, $userFont];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all matches from the $rule regex
|
||||
*
|
||||
* @param string $rule regex to match
|
||||
* @param string $css string to parse
|
||||
* @return string
|
||||
*/
|
||||
private function filterOutRule(string $rule, string $css): string {
|
||||
return preg_replace($rule, '', $css);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct uri prefix to make uri valid again
|
||||
*
|
||||
* @param string $css
|
||||
* @param string $webDir
|
||||
* @return string
|
||||
*/
|
||||
private function rebaseUrls(string $css, string $webDir): string {
|
||||
$re = '/url\([\'"]([^\/][\.\w?=\/-]*)[\'"]\)/x';
|
||||
$subst = 'url(\'' . $webDir . '/$1\')';
|
||||
|
||||
return preg_replace($re, $subst, $css);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all matches from the $rule regex
|
||||
*
|
||||
* @param string $css string to parse
|
||||
* @return string
|
||||
*/
|
||||
private function invertSvgIconsColor(string $css) {
|
||||
return str_replace(['/000', '/fff', '/***'], ['/***', '/000', '/fff'], $css);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string SCSS code for variables from OC_Defaults
|
||||
*/
|
||||
private function getInjectedVariables(): string {
|
||||
if ($this->injectedVariables !== null) {
|
||||
return $this->injectedVariables;
|
||||
}
|
||||
$variables = '';
|
||||
foreach ($this->defaults->getScssVariables() as $key => $value) {
|
||||
$variables .= '$' . $key . ': ' . $value . ';';
|
||||
}
|
||||
|
||||
// check for valid variables / otherwise fall back to defaults
|
||||
try {
|
||||
$scss = new Compiler();
|
||||
$scss->compile($variables);
|
||||
$this->injectedVariables = $variables;
|
||||
} catch (ParserException $e) {
|
||||
$this->logger->error($e, ['app' => 'core']);
|
||||
}
|
||||
return $variables;
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Accessibility\Controller;
|
||||
|
||||
use OCA\Accessibility\AccessibilityProvider;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\OCSController;
|
||||
use OCP\AppFramework\OCS\OCSBadRequestException;
|
||||
use OCP\IConfig;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUserSession;
|
||||
|
||||
class ConfigController extends OCSController {
|
||||
|
||||
/** @var string */
|
||||
protected $appName;
|
||||
|
||||
/** @var string */
|
||||
protected $userId;
|
||||
|
||||
/** @var string */
|
||||
protected $serverRoot;
|
||||
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/** @var IUserSession */
|
||||
private $userSession;
|
||||
|
||||
/** @var AccessibilityProvider */
|
||||
private $accessibilityProvider;
|
||||
|
||||
/**
|
||||
* Config constructor.
|
||||
*
|
||||
* @param string $appName
|
||||
* @param IRequest $request
|
||||
* @param IConfig $config
|
||||
* @param IUserSession $userSession
|
||||
* @param AccessibilityProvider $accessibilityProvider
|
||||
*/
|
||||
public function __construct(string $appName,
|
||||
IRequest $request,
|
||||
IConfig $config,
|
||||
IUserSession $userSession,
|
||||
AccessibilityProvider $accessibilityProvider) {
|
||||
parent::__construct($appName, $request);
|
||||
$this->appName = $appName;
|
||||
$this->config = $config;
|
||||
$this->userSession = $userSession;
|
||||
$this->accessibilityProvider = $accessibilityProvider;
|
||||
$this->userId = $userSession->getUser()->getUID();
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*
|
||||
* Get user accessibility config
|
||||
*
|
||||
* @param string $key theme or font
|
||||
* @return DataResponse
|
||||
*/
|
||||
public function getConfig(): DataResponse {
|
||||
return new DataResponse([
|
||||
'theme' => $this->config->getUserValue($this->userId, $this->appName, 'theme', false),
|
||||
'font' => $this->config->getUserValue($this->userId, $this->appName, 'font', false)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*
|
||||
* Set theme or font config
|
||||
*
|
||||
* @param string $key theme or font
|
||||
* @return DataResponse
|
||||
* @throws Exception
|
||||
*/
|
||||
public function setConfig(string $key, $value): DataResponse {
|
||||
if ($key === 'theme' || $key === 'font') {
|
||||
|
||||
if ($value === false) {
|
||||
$this->config->deleteUserValue($this->userId, $this->appName, $key);
|
||||
$userValues = $this->config->getUserKeys($this->userId, $this->appName);
|
||||
|
||||
// remove hash if no settings selected
|
||||
if (count($userValues) === 1 && $userValues[0] === 'icons-css') {
|
||||
$this->config->deleteUserValue($this->userId, $this->appName, 'icons-css');
|
||||
}
|
||||
|
||||
return new DataResponse();
|
||||
}
|
||||
|
||||
$themes = $this->accessibilityProvider->getThemes();
|
||||
$fonts = $this->accessibilityProvider->getFonts();
|
||||
|
||||
$availableOptions = array_map(function($option) {
|
||||
return $option['id'];
|
||||
}, array_merge($themes, $fonts));
|
||||
|
||||
if (in_array($value, $availableOptions)) {
|
||||
$this->config->setUserValue($this->userId, $this->appName, $key, $value);
|
||||
return new DataResponse();
|
||||
}
|
||||
|
||||
throw new OCSBadRequestException('Invalid value: ' . $value);
|
||||
}
|
||||
|
||||
throw new OCSBadRequestException('Invalid key: ' . $key);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Accessibility\Settings;
|
||||
|
||||
use OCA\Accessibility\AccessibilityProvider;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUserSession;
|
||||
use OCP\Settings\ISettings;
|
||||
|
||||
class Personal implements ISettings {
|
||||
|
||||
/** @var string */
|
||||
protected $appName;
|
||||
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/** @var IUserSession */
|
||||
private $userSession;
|
||||
|
||||
/** @var IL10N */
|
||||
private $l;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
/** @var AccessibilityProvider */
|
||||
private $accessibilityProvider;
|
||||
|
||||
/**
|
||||
* Settings constructor.
|
||||
*
|
||||
* @param string $appName
|
||||
* @param IConfig $config
|
||||
* @param IUserSession $userSession
|
||||
* @param IL10N $l
|
||||
* @param IURLGenerator $urlGenerator
|
||||
* @param AccessibilityProvider $accessibilityProvider
|
||||
*/
|
||||
public function __construct(string $appName,
|
||||
IConfig $config,
|
||||
IUserSession $userSession,
|
||||
IL10N $l,
|
||||
IURLGenerator $urlGenerator,
|
||||
AccessibilityProvider $accessibilityProvider) {
|
||||
$this->appName = $appName;
|
||||
$this->config = $config;
|
||||
$this->userSession = $userSession;
|
||||
$this->l = $l;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->accessibilityProvider = $accessibilityProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TemplateResponse returns the instance with all parameters set, ready to be rendered
|
||||
* @since 9.1
|
||||
*/
|
||||
public function getForm() {
|
||||
|
||||
$serverData = [
|
||||
'themes' => $this->accessibilityProvider->getThemes(),
|
||||
'fonts' => $this->accessibilityProvider->getFonts(),
|
||||
'theme' => $this->config->getUserValue($this->userSession->getUser()->getUID(), $this->appName, 'theme', false),
|
||||
'font' => $this->config->getUserValue($this->userSession->getUser()->getUID(), $this->appName, 'font', false)
|
||||
];
|
||||
|
||||
return new TemplateResponse($this->appName, 'settings-personal', ['serverData' => $serverData]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the section ID, e.g. 'sharing'
|
||||
* @since 9.1
|
||||
*/
|
||||
public function getSection() {
|
||||
return $this->appName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int whether the form should be rather on the top or bottom of
|
||||
* the admin section. The forms are arranged in ascending order of the
|
||||
* priority values. It is required to return a value between 0 and 100.
|
||||
*
|
||||
* E.g.: 70
|
||||
* @since 9.1
|
||||
*/
|
||||
public function getPriority() {
|
||||
return 40;
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Accessibility\Settings;
|
||||
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\Settings\IIconSection;
|
||||
|
||||
class PersonalSection implements IIconSection {
|
||||
|
||||
/** @var string */
|
||||
protected $appName;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
/** @var IL10N */
|
||||
private $l;
|
||||
|
||||
/**
|
||||
* Personal Section constructor.
|
||||
*
|
||||
* @param string $appName
|
||||
* @param IURLGenerator $urlGenerator
|
||||
* @param IL10N $l
|
||||
*/
|
||||
public function __construct(string $appName,
|
||||
IURLGenerator $urlGenerator,
|
||||
IL10N $l) {
|
||||
$this->appName = $appName;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->l = $l;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the relative path to an 16*16 icon describing the section.
|
||||
* e.g. '/core/img/places/files.svg'
|
||||
*
|
||||
* @returns string
|
||||
* @since 13.0.0
|
||||
*/
|
||||
public function getIcon() {
|
||||
return $this->urlGenerator->imagePath($this->appName, 'app-dark.svg');
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the ID of the section. It is supposed to be a lower case string,
|
||||
* e.g. 'ldap'
|
||||
*
|
||||
* @returns string
|
||||
* @since 9.1
|
||||
*/
|
||||
public function getID() {
|
||||
return $this->appName;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the translated name as it should be displayed, e.g. 'LDAP / AD
|
||||
* integration'. Use the L10N service to translate it.
|
||||
*
|
||||
* @return string
|
||||
* @since 9.1
|
||||
*/
|
||||
public function getName() {
|
||||
return $this->l->t('Accessibility');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int whether the form should be rather on the top or bottom of
|
||||
* the settings navigation. The sections are arranged in ascending order of
|
||||
* the priority values. It is required to return a value between 0 and 99.
|
||||
*
|
||||
* E.g.: 70
|
||||
* @since 9.1
|
||||
*/
|
||||
public function getPriority() {
|
||||
return 15;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"name": "accessibility",
|
||||
"description": "Provides multiple accessibilities options to ease your use of nextcloud",
|
||||
"version": "1.0.2",
|
||||
"author": "John Molakvoæ <skjnldsv@protonmail.com>",
|
||||
"license": "agpl",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "webpack --config webpack.dev.js",
|
||||
"watch": "webpack --progress --watch --config webpack.dev.js",
|
||||
"build": "webpack --progress --hide-modules --config webpack.prod.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.18.1",
|
||||
"vue": "^2.5.16"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 2 versions",
|
||||
"not ie <= 11"
|
||||
],
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-loader": "^7.1.5",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"css-loader": "^1.0.0",
|
||||
"file-loader": "^1.1.11",
|
||||
"vue-loader": "^15.2.6",
|
||||
"vue-template-compiler": "^2.5.16",
|
||||
"webpack": "^4.16.1",
|
||||
"webpack-cli": "^3.1.0",
|
||||
"webpack-merge": "^4.1.3"
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
<template>
|
||||
<div id="accessibility">
|
||||
<div id="themes" class="section">
|
||||
<h2>{{t('accessibility', 'Themes')}}</h2>
|
||||
<div class="themes-list preview-list">
|
||||
<preview v-for="preview in themes" :preview="preview"
|
||||
:key="preview.id" :selected="selected.theme"
|
||||
v-on:select="selectTheme"></preview>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fonts" class="section">
|
||||
<h2>{{t('accessibility', 'Fonts')}}</h2>
|
||||
<div class="fonts-list preview-list">
|
||||
<preview v-for="preview in fonts" :preview="preview"
|
||||
:key="preview.id" :selected="selected.font"
|
||||
v-on:select="selectFont"></preview>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import preview from './components/itemPreview';
|
||||
import axios from 'axios';
|
||||
|
||||
export default {
|
||||
name: 'Accessibility',
|
||||
components: { preview },
|
||||
beforeMount() {
|
||||
// importing server data into the app
|
||||
const serverDataElmt = document.getElementById('serverData');
|
||||
if (serverDataElmt !== null) {
|
||||
this.serverData = JSON.parse(
|
||||
document.getElementById('serverData').dataset.server
|
||||
);
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
serverData: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
themes() {
|
||||
return this.serverData.themes;
|
||||
},
|
||||
fonts() {
|
||||
return this.serverData.fonts;
|
||||
},
|
||||
selected() {
|
||||
return {
|
||||
theme: this.serverData.theme,
|
||||
font: this.serverData.font
|
||||
};
|
||||
},
|
||||
tokenHeaders() {
|
||||
return { headers: { requesttoken: OC.requestToken } };
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectTheme(id) {
|
||||
this.selectItem('theme', id);
|
||||
},
|
||||
selectFont(id) {
|
||||
this.selectItem('font', id);
|
||||
},
|
||||
|
||||
/**
|
||||
* Commit a change and force reload css
|
||||
* Fetching the file again will trigger the server update
|
||||
*
|
||||
* @param {string} type type of the change (font or theme)
|
||||
* @param {string} id the data of the change
|
||||
*/
|
||||
selectItem(type, id) {
|
||||
axios.post(
|
||||
OC.linkToOCS('apps/accessibility/api/v1/config', 2) + type,
|
||||
{ value: id },
|
||||
this.tokenHeaders
|
||||
)
|
||||
.then(response => {
|
||||
this.serverData[type] = id;
|
||||
|
||||
// Remove old link
|
||||
let link = document.querySelector('link[rel=stylesheet][href*=accessibility][href*=user-]');
|
||||
if (!link) {
|
||||
// insert new css
|
||||
let link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = OC.generateUrl('/apps/accessibility/css/user-style.css') + '?v=' + new Date().getTime();
|
||||
document.head.appendChild(link);
|
||||
} else {
|
||||
// compare arrays
|
||||
if (
|
||||
JSON.stringify(Object.values(this.selected)) ===
|
||||
JSON.stringify([false, false])
|
||||
) {
|
||||
// if nothing is selected, blindly remove the css
|
||||
link.remove();
|
||||
} else {
|
||||
// force update
|
||||
link.href =
|
||||
link.href.split('?')[0] +
|
||||
'?v=' +
|
||||
new Date().getTime();
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err, err.response);
|
||||
OC.Notification.showTemporary(t('accessibility', err.response.data.ocs.meta.message + '. Unable to apply the setting.'));
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -1,27 +0,0 @@
|
||||
<template>
|
||||
<a :class="{preview: true, selected: preview.id === selected}"
|
||||
href="#" @click="selectItem">
|
||||
<div class="preview-image" :style="{backgroundImage: 'url(' + preview.img + ')'}"></div>
|
||||
<h3>
|
||||
<span>{{preview.title}}</span>
|
||||
<div class="icon-checkmark-color">{{t('accessibility', 'enabled')}}</div>
|
||||
</h3>
|
||||
<p>{{preview.text}}</p>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'itemPreview',
|
||||
props: ['preview', 'selected'],
|
||||
methods: {
|
||||
selectItem() {
|
||||
this.$emit(
|
||||
'select',
|
||||
// if we clicked the already selected one: disable it
|
||||
this.preview.id === this.selected ? false : this.preview.id
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -1,12 +0,0 @@
|
||||
import Vue from 'vue';
|
||||
import App from './App.vue';
|
||||
|
||||
/* global t */
|
||||
// bind to window
|
||||
Vue.prototype.OC = OC;
|
||||
Vue.prototype.t = t;
|
||||
|
||||
new Vue({
|
||||
el: '#accessibility',
|
||||
render: h => h(App)
|
||||
});
|
||||
@@ -1,29 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
script('accessibility', 'accessibility');
|
||||
style('accessibility', 'style');
|
||||
?>
|
||||
|
||||
<span id="serverData" data-server="<?php p(json_encode($_['serverData']));?>"></span>
|
||||
<span id="accessibility"></span>
|
||||
@@ -1,42 +0,0 @@
|
||||
const path = require('path');
|
||||
const { VueLoaderPlugin } = require('vue-loader');
|
||||
|
||||
module.exports = {
|
||||
entry: path.join(__dirname, 'src', 'main.js'),
|
||||
output: {
|
||||
path: path.resolve(__dirname, './js'),
|
||||
publicPath: '/js/',
|
||||
filename: 'accessibility.js'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ['vue-style-loader', 'css-loader']
|
||||
},
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader'
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg|gif|svg)$/,
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: '[name].[ext]?[hash]'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [new VueLoaderPlugin()],
|
||||
resolve: {
|
||||
alias: {
|
||||
vue$: 'vue/dist/vue.esm.js'
|
||||
},
|
||||
extensions: ['*', '.js', '.vue', '.json']
|
||||
}
|
||||
};
|
||||
@@ -1,12 +0,0 @@
|
||||
const merge = require('webpack-merge');
|
||||
const common = require('./webpack.common.js');
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: 'development',
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
noInfo: true,
|
||||
overlay: true
|
||||
},
|
||||
devtool: '#eval-source-map'
|
||||
});
|
||||
@@ -1,7 +0,0 @@
|
||||
const merge = require('webpack-merge');
|
||||
const common = require('./webpack.common.js');
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: 'production',
|
||||
devtool: '#source-map'
|
||||
});
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
|
||||
* @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
<?xml version="1.0"?>
|
||||
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
|
||||
<info>
|
||||
<id>admin_audit</id>
|
||||
<name>Auditing / Logging</name>
|
||||
<summary>Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions.</summary>
|
||||
<description>Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions.</description>
|
||||
<version>1.4.0</version>
|
||||
<licence>agpl</licence>
|
||||
<licence>AGPL</licence>
|
||||
<author>Nextcloud</author>
|
||||
<version>1.3.0</version>
|
||||
<namespace>AdminAudit</namespace>
|
||||
<dependencies>
|
||||
<nextcloud min-version="13" max-version="13" />
|
||||
</dependencies>
|
||||
<types>
|
||||
<logging/>
|
||||
</types>
|
||||
<category>monitoring</category>
|
||||
<bugs>https://github.com/nextcloud/server/issues</bugs>
|
||||
<dependencies>
|
||||
<nextcloud min-version="14" max-version="14" />
|
||||
</dependencies>
|
||||
<background-jobs>
|
||||
<job>OCA\AdminAudit\BackgroundJobs\Rotate</job>
|
||||
</background-jobs>
|
||||
</info>
|
||||
|
||||
@@ -43,7 +43,8 @@ namespace Composer\Autoload;
|
||||
class ClassLoader
|
||||
{
|
||||
// PSR-4
|
||||
private $prefixLengthsPsr4 = array();
|
||||
private $firstCharsPsr4 = array();
|
||||
private $prefixLengthsPsr4 = array(); // For BC with legacy static maps
|
||||
private $prefixDirsPsr4 = array();
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
@@ -170,11 +171,10 @@ class ClassLoader
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
if ('\\' !== substr($prefix, -1)) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->firstCharsPsr4[$prefix[0]] = true;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
@@ -221,11 +221,10 @@ class ClassLoader
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
if ('\\' !== substr($prefix, -1)) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->firstCharsPsr4[$prefix[0]] = true;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
@@ -279,7 +278,7 @@ class ClassLoader
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -373,11 +372,11 @@ class ClassLoader
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
if (isset($this->firstCharsPsr4[$first]) || isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
$search = $subPath.'\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
|
||||
@@ -12,11 +12,9 @@ return array(
|
||||
'OCA\\AdminAudit\\Actions\\Console' => $baseDir . '/../lib/Actions/Console.php',
|
||||
'OCA\\AdminAudit\\Actions\\Files' => $baseDir . '/../lib/Actions/Files.php',
|
||||
'OCA\\AdminAudit\\Actions\\GroupManagement' => $baseDir . '/../lib/Actions/GroupManagement.php',
|
||||
'OCA\\AdminAudit\\Actions\\Security' => $baseDir . '/../lib/Actions/Security.php',
|
||||
'OCA\\AdminAudit\\Actions\\Sharing' => $baseDir . '/../lib/Actions/Sharing.php',
|
||||
'OCA\\AdminAudit\\Actions\\Trashbin' => $baseDir . '/../lib/Actions/Trashbin.php',
|
||||
'OCA\\AdminAudit\\Actions\\UserManagement' => $baseDir . '/../lib/Actions/UserManagement.php',
|
||||
'OCA\\AdminAudit\\Actions\\Versions' => $baseDir . '/../lib/Actions/Versions.php',
|
||||
'OCA\\AdminAudit\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php',
|
||||
'OCA\\AdminAudit\\BackgroundJobs\\Rotate' => $baseDir . '/../lib/BackgroundJobs/Rotate.php',
|
||||
);
|
||||
|
||||
@@ -6,11 +6,8 @@ namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInitAdminAudit
|
||||
{
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'O' =>
|
||||
array (
|
||||
'OCA\\AdminAudit\\' => 15,
|
||||
),
|
||||
public static $firstCharsPsr4 = array (
|
||||
'O' => true,
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
@@ -27,19 +24,17 @@ class ComposerStaticInitAdminAudit
|
||||
'OCA\\AdminAudit\\Actions\\Console' => __DIR__ . '/..' . '/../lib/Actions/Console.php',
|
||||
'OCA\\AdminAudit\\Actions\\Files' => __DIR__ . '/..' . '/../lib/Actions/Files.php',
|
||||
'OCA\\AdminAudit\\Actions\\GroupManagement' => __DIR__ . '/..' . '/../lib/Actions/GroupManagement.php',
|
||||
'OCA\\AdminAudit\\Actions\\Security' => __DIR__ . '/..' . '/../lib/Actions/Security.php',
|
||||
'OCA\\AdminAudit\\Actions\\Sharing' => __DIR__ . '/..' . '/../lib/Actions/Sharing.php',
|
||||
'OCA\\AdminAudit\\Actions\\Trashbin' => __DIR__ . '/..' . '/../lib/Actions/Trashbin.php',
|
||||
'OCA\\AdminAudit\\Actions\\UserManagement' => __DIR__ . '/..' . '/../lib/Actions/UserManagement.php',
|
||||
'OCA\\AdminAudit\\Actions\\Versions' => __DIR__ . '/..' . '/../lib/Actions/Versions.php',
|
||||
'OCA\\AdminAudit\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php',
|
||||
'OCA\\AdminAudit\\BackgroundJobs\\Rotate' => __DIR__ . '/..' . '/../lib/BackgroundJobs/Rotate.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInitAdminAudit::$prefixLengthsPsr4;
|
||||
$loader->firstCharsPsr4 = ComposerStaticInitAdminAudit::$firstCharsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInitAdminAudit::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInitAdminAudit::$classMap;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
@@ -46,10 +45,10 @@ class Action {
|
||||
* @param array $elements
|
||||
* @param bool $obfuscateParameters
|
||||
*/
|
||||
public function log(string $text,
|
||||
public function log($text,
|
||||
array $params,
|
||||
array $elements,
|
||||
bool $obfuscateParameters = false) {
|
||||
$obfuscateParameters = false) {
|
||||
foreach($elements as $element) {
|
||||
if(!isset($params[$element])) {
|
||||
if ($obfuscateParameters) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
@@ -29,7 +28,7 @@ class AppManagement extends Action {
|
||||
/**
|
||||
* @param string $appName
|
||||
*/
|
||||
public function enableApp(string $appName) {
|
||||
public function enableApp($appName) {
|
||||
$this->log('App "%s" enabled',
|
||||
['app' => $appName],
|
||||
['app']
|
||||
@@ -40,7 +39,7 @@ class AppManagement extends Action {
|
||||
* @param string $appName
|
||||
* @param string[] $groups
|
||||
*/
|
||||
public function enableAppForGroups(string $appName, array $groups) {
|
||||
public function enableAppForGroups($appName, array $groups) {
|
||||
$this->log('App "%s" enabled for groups: %s',
|
||||
['app' => $appName, 'groups' => implode(', ', $groups)],
|
||||
['app', 'groups']
|
||||
@@ -50,7 +49,7 @@ class AppManagement extends Action {
|
||||
/**
|
||||
* @param string $appName
|
||||
*/
|
||||
public function disableApp(string $appName) {
|
||||
public function disableApp($appName) {
|
||||
$this->log('App "%s" disabled',
|
||||
['app' => $appName],
|
||||
['app']
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
@@ -29,7 +28,7 @@ class Console extends Action {
|
||||
/**
|
||||
* @param $arguments
|
||||
*/
|
||||
public function runCommand(array $arguments) {
|
||||
public function runCommand($arguments) {
|
||||
if ($arguments[1] === '_completion') {
|
||||
// Don't log autocompletion
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
|
||||
*
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\AdminAudit\Actions;
|
||||
use OCP\IUser;
|
||||
|
||||
/**
|
||||
* Class Sharing logs the sharing actions
|
||||
*
|
||||
* @package OCA\AdminAudit\Actions
|
||||
*/
|
||||
class Security extends Action {
|
||||
/**
|
||||
* Log twofactor auth enabled
|
||||
*
|
||||
* @param IUser $user
|
||||
* @param array $params
|
||||
*/
|
||||
public function twofactorFailed(IUser $user, array $params) {
|
||||
$params['uid'] = $user->getUID();
|
||||
$params['displayName'] = $user->getDisplayName();
|
||||
|
||||
$this->log(
|
||||
'Failed two factor attempt by user %s (%s) with provider %s',
|
||||
$params,
|
||||
[
|
||||
'displayName',
|
||||
'uid',
|
||||
'provider',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs unsharing of data
|
||||
*
|
||||
* @param IUser $user
|
||||
* @param array $params
|
||||
*/
|
||||
public function twofactorSuccess(IUser $user, array $params) {
|
||||
$params['uid'] = $user->getUID();
|
||||
$params['displayName'] = $user->getDisplayName();
|
||||
|
||||
$this->log(
|
||||
'Successful two factor attempt by user %s (%s) with provider %s',
|
||||
$params,
|
||||
[
|
||||
'displayName',
|
||||
'uid',
|
||||
'provider',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
@@ -78,19 +77,6 @@ class Sharing extends Action {
|
||||
'id',
|
||||
]
|
||||
);
|
||||
} elseif($params['shareType'] === Share::SHARE_TYPE_ROOM) {
|
||||
$this->log(
|
||||
'The %s "%s" with ID "%s" has been shared to the room "%s" with permissions "%s" (Share ID: %s)',
|
||||
$params,
|
||||
[
|
||||
'itemType',
|
||||
'itemTarget',
|
||||
'itemSource',
|
||||
'shareWith',
|
||||
'permissions',
|
||||
'id',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,18 +121,6 @@ class Sharing extends Action {
|
||||
'id',
|
||||
]
|
||||
);
|
||||
} elseif($params['shareType'] === Share::SHARE_TYPE_ROOM) {
|
||||
$this->log(
|
||||
'The %s "%s" with ID "%s" has been unshared from the room "%s" (Share ID: %s)',
|
||||
$params,
|
||||
[
|
||||
'itemType',
|
||||
'fileTarget',
|
||||
'itemSource',
|
||||
'shareWith',
|
||||
'id',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
|
||||
*
|
||||
@@ -28,13 +27,13 @@ namespace OCA\AdminAudit\Actions;
|
||||
|
||||
class Trashbin extends Action {
|
||||
|
||||
public function delete(array $params) {
|
||||
public function delete($params) {
|
||||
$this->log('File "%s" deleted from trash bin.',
|
||||
['path' => $params['path']], ['path']
|
||||
);
|
||||
}
|
||||
|
||||
public function restore(array $params) {
|
||||
public function restore($params) {
|
||||
$this->log('File "%s" restored from trash bin.',
|
||||
['path' => $params['filePath']], ['path']
|
||||
);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
@@ -50,19 +49,6 @@ class UserManagement extends Action {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log assignments of users (typically user backends)
|
||||
*
|
||||
* @param string $uid
|
||||
*/
|
||||
public function assign(string $uid) {
|
||||
$this->log(
|
||||
'UserID assigned: "%s"',
|
||||
[ 'uid' => $uid ],
|
||||
[ 'uid' ]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log deletion of users
|
||||
*
|
||||
@@ -78,44 +64,20 @@ class UserManagement extends Action {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log unassignments of users (typically user backends, no data removed)
|
||||
*
|
||||
* @param string $uid
|
||||
*/
|
||||
public function unassign(string $uid) {
|
||||
$this->log(
|
||||
'UserID unassigned: "%s"',
|
||||
[ 'uid' => $uid ],
|
||||
[ 'uid' ]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log enabling of users
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function change(array $params) {
|
||||
switch($params['feature']) {
|
||||
case 'enabled':
|
||||
$this->log(
|
||||
$params['value'] === 'true' ? 'User enabled: "%s"' : 'User disabled: "%s"',
|
||||
['user' => $params['user']->getUID()],
|
||||
[
|
||||
'user',
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'eMailAddress':
|
||||
$this->log(
|
||||
'Email address changed for user %s',
|
||||
['user' => $params['user']->getUID()],
|
||||
[
|
||||
'user',
|
||||
]
|
||||
);
|
||||
break;
|
||||
if ($params['feature'] === 'enabled') {
|
||||
$this->log(
|
||||
$params['value'] === 'true' ? 'User enabled: "%s"' : 'User disabled: "%s"',
|
||||
['user' => $params['user']->getUID()],
|
||||
[
|
||||
'user',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Bjoern Schiessle <bjoern@schiessle.org>
|
||||
*
|
||||
@@ -28,7 +27,7 @@ namespace OCA\AdminAudit\Actions;
|
||||
|
||||
class Versions extends Action {
|
||||
|
||||
public function rollback(array $params) {
|
||||
public function rollback($params) {
|
||||
$this->log('Version "%s" of "%s" was restored.',
|
||||
[
|
||||
'version' => $params['revision'],
|
||||
@@ -38,7 +37,7 @@ class Versions extends Action {
|
||||
);
|
||||
}
|
||||
|
||||
public function delete(array $params) {
|
||||
public function delete($params) {
|
||||
$this->log('Version "%s" was deleted.',
|
||||
['path' => $params['path']],
|
||||
['path']
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
@@ -34,14 +33,12 @@ use OCA\AdminAudit\Actions\Auth;
|
||||
use OCA\AdminAudit\Actions\Console;
|
||||
use OCA\AdminAudit\Actions\Files;
|
||||
use OCA\AdminAudit\Actions\GroupManagement;
|
||||
use OCA\AdminAudit\Actions\Security;
|
||||
use OCA\AdminAudit\Actions\Sharing;
|
||||
use OCA\AdminAudit\Actions\Trashbin;
|
||||
use OCA\AdminAudit\Actions\UserManagement;
|
||||
use OCA\AdminAudit\Actions\Versions;
|
||||
use OCP\App\ManagerEvent;
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\Authentication\TwoFactorAuth\IProvider;
|
||||
use OCP\Console\ConsoleEvent;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\ILogger;
|
||||
@@ -49,30 +46,11 @@ use OCP\IPreview;
|
||||
use OCP\IUserSession;
|
||||
use OCP\Util;
|
||||
use Symfony\Component\EventDispatcher\GenericEvent;
|
||||
use OCP\Share;
|
||||
|
||||
class Application extends App {
|
||||
|
||||
/** @var ILogger */
|
||||
protected $logger;
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct('admin_audit');
|
||||
$this->initLogger();
|
||||
}
|
||||
|
||||
public function initLogger() {
|
||||
$c = $this->getContainer()->getServer();
|
||||
$config = $c->getConfig();
|
||||
|
||||
$default = $config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/audit.log';
|
||||
$logFile = $config->getAppValue('admin_audit', 'logfile', $default);
|
||||
if($logFile === null) {
|
||||
$this->logger = $c->getLogger();
|
||||
return;
|
||||
}
|
||||
$this->logger = $c->getLogFactory()->getCustomLogger($logFile);
|
||||
|
||||
}
|
||||
|
||||
public function register() {
|
||||
@@ -83,24 +61,24 @@ class Application extends App {
|
||||
* Register hooks in order to log them
|
||||
*/
|
||||
protected function registerHooks() {
|
||||
$this->userManagementHooks();
|
||||
$this->groupHooks();
|
||||
$this->authHooks();
|
||||
$logger = $this->getContainer()->getServer()->getLogger();
|
||||
|
||||
$this->consoleHooks();
|
||||
$this->appHooks();
|
||||
$this->userManagementHooks($logger);
|
||||
$this->groupHooks($logger);
|
||||
$this->authHooks($logger);
|
||||
|
||||
$this->sharingHooks();
|
||||
$this->consoleHooks($logger);
|
||||
$this->appHooks($logger);
|
||||
|
||||
$this->fileHooks();
|
||||
$this->trashbinHooks();
|
||||
$this->versionsHooks();
|
||||
$this->sharingHooks($logger);
|
||||
|
||||
$this->securityHooks();
|
||||
$this->fileHooks($logger);
|
||||
$this->trashbinHooks($logger);
|
||||
$this->versionsHooks($logger);
|
||||
}
|
||||
|
||||
protected function userManagementHooks() {
|
||||
$userActions = new UserManagement($this->logger);
|
||||
protected function userManagementHooks(ILogger $logger) {
|
||||
$userActions = new UserManagement($logger);
|
||||
|
||||
Util::connectHook('OC_User', 'post_createUser', $userActions, 'create');
|
||||
Util::connectHook('OC_User', 'post_deleteUser', $userActions, 'delete');
|
||||
@@ -109,12 +87,10 @@ class Application extends App {
|
||||
/** @var IUserSession|Session $userSession */
|
||||
$userSession = $this->getContainer()->getServer()->getUserSession();
|
||||
$userSession->listen('\OC\User', 'postSetPassword', [$userActions, 'setPassword']);
|
||||
$userSession->listen('\OC\User', 'assignedUserId', [$userActions, 'assign']);
|
||||
$userSession->listen('\OC\User', 'postUnassignedUserId', [$userActions, 'unassign']);
|
||||
}
|
||||
|
||||
protected function groupHooks() {
|
||||
$groupActions = new GroupManagement($this->logger);
|
||||
protected function groupHooks(ILogger $logger) {
|
||||
$groupActions = new GroupManagement($logger);
|
||||
|
||||
/** @var IGroupManager|Manager $groupManager */
|
||||
$groupManager = $this->getContainer()->getServer()->getGroupManager();
|
||||
@@ -124,53 +100,53 @@ class Application extends App {
|
||||
$groupManager->listen('\OC\Group', 'postCreate', [$groupActions, 'createGroup']);
|
||||
}
|
||||
|
||||
protected function sharingHooks() {
|
||||
$shareActions = new Sharing($this->logger);
|
||||
protected function sharingHooks(ILogger $logger) {
|
||||
$shareActions = new Sharing($logger);
|
||||
|
||||
Util::connectHook(Share::class, 'post_shared', $shareActions, 'shared');
|
||||
Util::connectHook(Share::class, 'post_unshare', $shareActions, 'unshare');
|
||||
Util::connectHook(Share::class, 'post_update_permissions', $shareActions, 'updatePermissions');
|
||||
Util::connectHook(Share::class, 'post_update_password', $shareActions, 'updatePassword');
|
||||
Util::connectHook(Share::class, 'post_set_expiration_date', $shareActions, 'updateExpirationDate');
|
||||
Util::connectHook(Share::class, 'share_link_access', $shareActions, 'shareAccessed');
|
||||
Util::connectHook('OCP\Share', 'post_shared', $shareActions, 'shared');
|
||||
Util::connectHook('OCP\Share', 'post_unshare', $shareActions, 'unshare');
|
||||
Util::connectHook('OCP\Share', 'post_update_permissions', $shareActions, 'updatePermissions');
|
||||
Util::connectHook('OCP\Share', 'post_update_password', $shareActions, 'updatePassword');
|
||||
Util::connectHook('OCP\Share', 'post_set_expiration_date', $shareActions, 'updateExpirationDate');
|
||||
Util::connectHook('OCP\Share', 'share_link_access', $shareActions, 'shareAccessed');
|
||||
}
|
||||
|
||||
protected function authHooks() {
|
||||
$authActions = new Auth($this->logger);
|
||||
protected function authHooks(ILogger $logger) {
|
||||
$authActions = new Auth($logger);
|
||||
|
||||
Util::connectHook('OC_User', 'pre_login', $authActions, 'loginAttempt');
|
||||
Util::connectHook('OC_User', 'post_login', $authActions, 'loginSuccessful');
|
||||
Util::connectHook('OC_User', 'logout', $authActions, 'logout');
|
||||
}
|
||||
|
||||
protected function appHooks() {
|
||||
protected function appHooks(ILogger $logger) {
|
||||
|
||||
$eventDispatcher = $this->getContainer()->getServer()->getEventDispatcher();
|
||||
$eventDispatcher->addListener(ManagerEvent::EVENT_APP_ENABLE, function(ManagerEvent $event) {
|
||||
$appActions = new AppManagement($this->logger);
|
||||
$eventDispatcher->addListener(ManagerEvent::EVENT_APP_ENABLE, function(ManagerEvent $event) use ($logger) {
|
||||
$appActions = new AppManagement($logger);
|
||||
$appActions->enableApp($event->getAppID());
|
||||
});
|
||||
$eventDispatcher->addListener(ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, function(ManagerEvent $event) {
|
||||
$appActions = new AppManagement($this->logger);
|
||||
$eventDispatcher->addListener(ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, function(ManagerEvent $event) use ($logger) {
|
||||
$appActions = new AppManagement($logger);
|
||||
$appActions->enableAppForGroups($event->getAppID(), $event->getGroups());
|
||||
});
|
||||
$eventDispatcher->addListener(ManagerEvent::EVENT_APP_DISABLE, function(ManagerEvent $event) {
|
||||
$appActions = new AppManagement($this->logger);
|
||||
$eventDispatcher->addListener(ManagerEvent::EVENT_APP_DISABLE, function(ManagerEvent $event) use ($logger) {
|
||||
$appActions = new AppManagement($logger);
|
||||
$appActions->disableApp($event->getAppID());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
protected function consoleHooks() {
|
||||
protected function consoleHooks(ILogger $logger) {
|
||||
$eventDispatcher = $this->getContainer()->getServer()->getEventDispatcher();
|
||||
$eventDispatcher->addListener(ConsoleEvent::EVENT_RUN, function(ConsoleEvent $event) {
|
||||
$appActions = new Console($this->logger);
|
||||
$eventDispatcher->addListener(ConsoleEvent::EVENT_RUN, function(ConsoleEvent $event) use ($logger) {
|
||||
$appActions = new Console($logger);
|
||||
$appActions->runCommand($event->getArguments());
|
||||
});
|
||||
}
|
||||
|
||||
protected function fileHooks() {
|
||||
$fileActions = new Files($this->logger);
|
||||
protected function fileHooks(ILogger $logger) {
|
||||
$fileActions = new Files($logger);
|
||||
$eventDispatcher = $this->getContainer()->getServer()->getEventDispatcher();
|
||||
$eventDispatcher->addListener(
|
||||
IPreview::EVENT,
|
||||
@@ -231,27 +207,15 @@ class Application extends App {
|
||||
);
|
||||
}
|
||||
|
||||
protected function versionsHooks() {
|
||||
$versionsActions = new Versions($this->logger);
|
||||
protected function versionsHooks(ILogger $logger) {
|
||||
$versionsActions = new Versions($logger);
|
||||
Util::connectHook('\OCP\Versions', 'rollback', $versionsActions, 'rollback');
|
||||
Util::connectHook('\OCP\Versions', 'delete',$versionsActions, 'delete');
|
||||
}
|
||||
|
||||
protected function trashbinHooks() {
|
||||
$trashActions = new Trashbin($this->logger);
|
||||
protected function trashbinHooks(ILogger $logger) {
|
||||
$trashActions = new Trashbin($logger);
|
||||
Util::connectHook('\OCP\Trashbin', 'preDelete', $trashActions, 'delete');
|
||||
Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', $trashActions, 'restore');
|
||||
}
|
||||
|
||||
protected function securityHooks() {
|
||||
$eventDispatcher = $this->getContainer()->getServer()->getEventDispatcher();
|
||||
$eventDispatcher->addListener(IProvider::EVENT_SUCCESS, function(GenericEvent $event) {
|
||||
$security = new Security($this->logger);
|
||||
$security->twofactorSuccess($event->getSubject(), $event->getArguments());
|
||||
});
|
||||
$eventDispatcher->addListener(IProvider::EVENT_FAILED, function(GenericEvent $event) {
|
||||
$security = new Security($this->logger);
|
||||
$security->twofactorFailed($event->getSubject(), $event->getArguments());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Arthur Schiwon <blizzz@arthur-schiwon.de>
|
||||
*
|
||||
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\AdminAudit\BackgroundJobs;
|
||||
|
||||
use OC\BackgroundJob\TimedJob;
|
||||
use OCP\Log\RotationTrait;
|
||||
|
||||
class Rotate extends TimedJob {
|
||||
use RotationTrait;
|
||||
|
||||
public function __construct() {
|
||||
$this->setInterval(60*60*3);
|
||||
}
|
||||
|
||||
protected function run($argument) {
|
||||
$config = \OC::$server->getConfig();
|
||||
$default = $config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/audit.log';
|
||||
$this->filePath = $config->getAppValue('admin_audit', 'logfile', $default);
|
||||
|
||||
if($this->filePath === '') {
|
||||
// default log file, nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
$this->maxSize = $config->getSystemValue('log_rotate_size', 100 * 1024 * 1024);
|
||||
|
||||
if($this->shouldRotateBySize()) {
|
||||
$this->rotate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\AdminAudit\Tests\Actions;
|
||||
|
||||
use OCA\AdminAudit\Actions\Security;
|
||||
use OCP\ILogger;
|
||||
use OCP\IUser;
|
||||
use Test\TestCase;
|
||||
|
||||
class SecurityTest extends TestCase {
|
||||
/** @var ILogger|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $logger;
|
||||
|
||||
/** @var Security */
|
||||
private $security;
|
||||
|
||||
/** @var IUser|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $user;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->logger = $this->createMock(ILogger::class);
|
||||
$this->security = new Security($this->logger);
|
||||
|
||||
$this->user = $this->createMock(IUser::class);
|
||||
$this->user->method('getUID')->willReturn('myuid');
|
||||
$this->user->method('getDisplayName')->willReturn('mydisplayname');
|
||||
}
|
||||
|
||||
public function testTwofactorFailed() {
|
||||
$this->logger->expects($this->once())
|
||||
->method('info')
|
||||
->with(
|
||||
$this->equalTo('Failed two factor attempt by user mydisplayname (myuid) with provider myprovider'),
|
||||
['app' => 'admin_audit']
|
||||
);
|
||||
|
||||
$this->security->twofactorFailed($this->user, ['provider' => 'myprovider']);
|
||||
}
|
||||
|
||||
public function testTwofactorSuccess() {
|
||||
$this->logger->expects($this->once())
|
||||
->method('info')
|
||||
->with(
|
||||
$this->equalTo('Successful two factor attempt by user mydisplayname (myuid) with provider myprovider'),
|
||||
['app' => 'admin_audit']
|
||||
);
|
||||
|
||||
$this->security->twofactorSuccess($this->user, ['provider' => 'myprovider']);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,661 +0,0 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
@@ -1,2 +0,0 @@
|
||||
# cloud_federation_api
|
||||
The cloud federation API allows to share information like files, contacts, calendars, incoming calls, etc accross Nextcloud instances
|
||||
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
$app = new \OCA\CloudFederationAPI\AppInfo\Application();
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
|
||||
<id>cloud_federation_api</id>
|
||||
<name>Cloud Federation API</name>
|
||||
<summary>Enable clouds to communicate with each other and exchange data</summary>
|
||||
<description>The Cloud Federation API enables various Nextcloud instances to communicate with each other and to exchange data.</description>
|
||||
<version>0.0.1</version>
|
||||
<licence>agpl</licence>
|
||||
<author>Bjoern Schiessle</author>
|
||||
<namespace>CloudFederationAPI</namespace>
|
||||
<types>
|
||||
<filesystem/>
|
||||
</types>
|
||||
<category>files</category>
|
||||
<bugs>https://github.com/nextcloud/cloud_federation/issues</bugs>
|
||||
<dependencies>
|
||||
<nextcloud min-version="14" max-version="14"/>
|
||||
</dependencies>
|
||||
</info>
|
||||
@@ -1,37 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace OCA\CloudFederationAPI\AppInfo;
|
||||
|
||||
|
||||
use OCA\CloudFederationAPI\Capabilities;
|
||||
use OCP\AppFramework\App;
|
||||
|
||||
class Application extends App {
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct('cloud_federation_api');
|
||||
|
||||
$container = $this->getContainer();
|
||||
$container->registerCapability(Capabilities::class);
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace OCA\CloudFederationAPI;
|
||||
|
||||
|
||||
use OCP\Capabilities\ICapability;
|
||||
use OCP\IURLGenerator;
|
||||
|
||||
class Capabilities implements ICapability {
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
public function __construct(IURLGenerator $urlGenerator) {
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function an app uses to return the capabilities
|
||||
*
|
||||
* @return array Array containing the apps capabilities
|
||||
* @since 8.2.0
|
||||
*/
|
||||
public function getCapabilities() {
|
||||
$url = $this->urlGenerator->linkToRouteAbsolute('cloud_federation_api.requesthandlercontroller.addShare');
|
||||
$capabilities = ['ocm' =>
|
||||
[
|
||||
'enabled' => true,
|
||||
'apiVersion' => '1.0-proposal1',
|
||||
'endPoint' => substr($url, 0, strrpos($url, '/')),
|
||||
'shareTypes' => [
|
||||
[
|
||||
'name' => 'file',
|
||||
'protocols' => [
|
||||
'webdav' => '/public.php/webdav/',
|
||||
]
|
||||
],
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
return $capabilities;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace OCA\CloudFederationAPI;
|
||||
use OCP\Federation\ICloudFederationProviderManager;
|
||||
use OCP\GlobalScale\IConfig as IGsConfig;
|
||||
use OCP\IConfig;
|
||||
|
||||
|
||||
/**
|
||||
* Class config
|
||||
*
|
||||
* handles all the config parameters
|
||||
*
|
||||
* @package OCA\CloudFederationAPI
|
||||
*/
|
||||
class Config {
|
||||
|
||||
/** @var ICloudFederationProviderManager */
|
||||
private $cloudFederationProviderManager;
|
||||
|
||||
public function __construct(ICloudFederationProviderManager $cloudFederationProviderManager) {
|
||||
$this->cloudFederationProviderManager = $cloudFederationProviderManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* get a list of supported share types
|
||||
*
|
||||
* @param string $resourceType
|
||||
* @return array
|
||||
*/
|
||||
public function getSupportedShareTypes($resourceType) {
|
||||
try {
|
||||
$provider = $this->cloudFederationProviderManager->getCloudFederationProvider($resourceType);
|
||||
return $provider->getSupportedShareTypes();
|
||||
} catch (\Exception $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,300 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org>
|
||||
*
|
||||
* @author Bjoern Schiessle <bjoern@schiessle.org>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\CloudFederationAPI\Controller;
|
||||
|
||||
use OCA\CloudFederationAPI\Config;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\Federation\Exceptions\ActionNotSupportedException;
|
||||
use OCP\Federation\Exceptions\AuthenticationFailedException;
|
||||
use OCP\Federation\Exceptions\BadRequestException;
|
||||
use OCP\Federation\Exceptions\ProviderCouldNotAddShareException;
|
||||
use OCP\Federation\ICloudFederationFactory;
|
||||
use OCP\Federation\ICloudFederationProviderManager;
|
||||
use OCP\Federation\Exceptions\ProviderDoesNotExistsException;
|
||||
use OCP\Federation\ICloudIdManager;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\ILogger;
|
||||
use OCP\IRequest;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Share\Exceptions\ShareNotFound;
|
||||
|
||||
|
||||
/**
|
||||
* Class RequestHandlerController
|
||||
*
|
||||
* handle API between different Cloud instances
|
||||
*
|
||||
* @package OCA\CloudFederationAPI\Controller
|
||||
*/
|
||||
class RequestHandlerController extends Controller {
|
||||
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
|
||||
/** @var IGroupManager */
|
||||
private $groupManager;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
/** @var ICloudFederationProviderManager */
|
||||
private $cloudFederationProviderManager;
|
||||
|
||||
/** @var Config */
|
||||
private $config;
|
||||
|
||||
/** @var ICloudFederationFactory */
|
||||
private $factory;
|
||||
|
||||
/** @var ICloudIdManager */
|
||||
private $cloudIdManager;
|
||||
|
||||
public function __construct($appName,
|
||||
IRequest $request,
|
||||
ILogger $logger,
|
||||
IUserManager $userManager,
|
||||
IGroupManager $groupManager,
|
||||
IURLGenerator $urlGenerator,
|
||||
ICloudFederationProviderManager $cloudFederationProviderManager,
|
||||
Config $config,
|
||||
ICloudFederationFactory $factory,
|
||||
ICloudIdManager $cloudIdManager
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
|
||||
$this->logger = $logger;
|
||||
$this->userManager = $userManager;
|
||||
$this->groupManager = $groupManager;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->cloudFederationProviderManager = $cloudFederationProviderManager;
|
||||
$this->config = $config;
|
||||
$this->factory = $factory;
|
||||
$this->cloudIdManager = $cloudIdManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* add share
|
||||
*
|
||||
* @NoCSRFRequired
|
||||
* @PublicPage
|
||||
* @BruteForceProtection(action=receiveFederatedShare)
|
||||
*
|
||||
* @param string $shareWith
|
||||
* @param string $name resource name (e.g. document.odt)
|
||||
* @param string $description share description (optional)
|
||||
* @param string $providerId resource UID on the provider side
|
||||
* @param string $owner provider specific UID of the user who owns the resource
|
||||
* @param string $ownerDisplayName display name of the user who shared the item
|
||||
* @param string $sharedBy provider specific UID of the user who shared the resource
|
||||
* @param string $sharedByDisplayName display name of the user who shared the resource
|
||||
* @param array $protocol (e,.g. ['name' => 'webdav', 'options' => ['username' => 'john', 'permissions' => 31]])
|
||||
* @param string $shareType ('group' or 'user' share)
|
||||
* @param $resourceType ('file', 'calendar',...)
|
||||
* @return Http\DataResponse|JSONResponse
|
||||
*
|
||||
* Example: curl -H "Content-Type: application/json" -X POST -d '{"shareWith":"admin1@serve1","name":"welcome server2.txt","description":"desc","providerId":"2","owner":"admin2@http://localhost/server2","ownerDisplayName":"admin2 display","shareType":"user","resourceType":"file","protocol":{"name":"webdav","options":{"sharedSecret":"secret","permissions":"webdav-property"}}}' http://localhost/server/index.php/ocm/shares
|
||||
*/
|
||||
public function addShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, $protocol, $shareType, $resourceType) {
|
||||
|
||||
// check if all required parameters are set
|
||||
if ($shareWith === null ||
|
||||
$name === null ||
|
||||
$providerId === null ||
|
||||
$owner === null ||
|
||||
$resourceType === null ||
|
||||
$shareType === null ||
|
||||
!is_array($protocol) ||
|
||||
!isset($protocol['name']) ||
|
||||
!isset ($protocol['options']) ||
|
||||
!is_array($protocol['options']) ||
|
||||
!isset($protocol['options']['sharedSecret'])
|
||||
) {
|
||||
return new JSONResponse(
|
||||
['message' => 'Missing arguments'],
|
||||
Http::STATUS_BAD_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
$supportedShareTypes = $this->config->getSupportedShareTypes($resourceType);
|
||||
if (!in_array($shareType, $supportedShareTypes)) {
|
||||
return new JSONResponse(
|
||||
['message' => 'Share type "' . $shareType . '" not implemented'],
|
||||
Http::STATUS_NOT_IMPLEMENTED
|
||||
);
|
||||
}
|
||||
|
||||
$cloudId = $this->cloudIdManager->resolveCloudId($shareWith);
|
||||
$shareWith = $cloudId->getUser();
|
||||
|
||||
if ($shareType === 'user') {
|
||||
$shareWith = $this->mapUid($shareWith);
|
||||
|
||||
if (!$this->userManager->userExists($shareWith)) {
|
||||
return new JSONResponse(
|
||||
['message' => 'User "' . $shareWith . '" does not exists at ' . $this->urlGenerator->getBaseUrl()],
|
||||
Http::STATUS_BAD_REQUEST
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($shareType === 'group') {
|
||||
if(!$this->groupManager->groupExists($shareWith)) {
|
||||
return new JSONResponse(
|
||||
['message' => 'Group "' . $shareWith . '" does not exists at ' . $this->urlGenerator->getBaseUrl()],
|
||||
Http::STATUS_BAD_REQUEST
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// if no explicit display name is given, we use the uid as display name
|
||||
$ownerDisplayName = $ownerDisplayName === null ? $owner : $ownerDisplayName;
|
||||
$sharedByDisplayName = $sharedByDisplayName === null ? $sharedBy : $sharedByDisplayName;
|
||||
|
||||
// sharedBy* parameter is optional, if nothing is set we assume that it is the same user as the owner
|
||||
if ($sharedBy === null) {
|
||||
$sharedBy = $owner;
|
||||
$sharedByDisplayName = $ownerDisplayName;
|
||||
}
|
||||
|
||||
try {
|
||||
$provider = $this->cloudFederationProviderManager->getCloudFederationProvider($resourceType);
|
||||
$share = $this->factory->getCloudFederationShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, '', $shareType, $resourceType);
|
||||
$share->setProtocol($protocol);
|
||||
$provider->shareReceived($share);
|
||||
} catch (ProviderDoesNotExistsException $e) {
|
||||
return new JSONResponse(
|
||||
['message' => $e->getMessage()],
|
||||
Http::STATUS_NOT_IMPLEMENTED
|
||||
);
|
||||
} catch (ProviderCouldNotAddShareException $e) {
|
||||
return new JSONResponse(
|
||||
['message' => $e->getMessage()],
|
||||
$e->getCode()
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
return new JSONResponse(
|
||||
['message' => 'Internal error at ' . $this->urlGenerator->getBaseUrl()],
|
||||
Http::STATUS_BAD_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
$user = $this->userManager->get($shareWith);
|
||||
$recipientDisplayName = '';
|
||||
if($user) {
|
||||
$recipientDisplayName = $user->getDisplayName();
|
||||
}
|
||||
|
||||
return new JSONResponse(
|
||||
['recipientDisplayName' => $recipientDisplayName],
|
||||
Http::STATUS_CREATED);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* receive notification about existing share
|
||||
*
|
||||
* @NoCSRFRequired
|
||||
* @PublicPage
|
||||
* @BruteForceProtection(action=receiveFederatedShareNotification)
|
||||
*
|
||||
* @param string $notificationType (notification type, e.g. SHARE_ACCEPTED)
|
||||
* @param string $resourceType (calendar, file, contact,...)
|
||||
* @param string $providerId id of the share
|
||||
* @param array $notification the actual payload of the notification
|
||||
* @return JSONResponse
|
||||
*/
|
||||
public function receiveNotification($notificationType, $resourceType, $providerId, array $notification) {
|
||||
|
||||
// check if all required parameters are set
|
||||
if ($notificationType === null ||
|
||||
$resourceType === null ||
|
||||
$providerId === null ||
|
||||
!is_array($notification)
|
||||
) {
|
||||
return new JSONResponse(
|
||||
['message' => 'Missing arguments'],
|
||||
Http::STATUS_BAD_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$provider = $this->cloudFederationProviderManager->getCloudFederationProvider($resourceType);
|
||||
$result = $provider->notificationReceived($notificationType, $providerId, $notification);
|
||||
} catch (ProviderDoesNotExistsException $e) {
|
||||
return new JSONResponse(
|
||||
['message' => $e->getMessage()],
|
||||
Http::STATUS_BAD_REQUEST
|
||||
);
|
||||
} catch (ShareNotFound $e) {
|
||||
return new JSONResponse(
|
||||
['message' => $e->getMessage()],
|
||||
Http::STATUS_BAD_REQUEST
|
||||
);
|
||||
} catch (ActionNotSupportedException $e) {
|
||||
return new JSONResponse(
|
||||
['message' => $e->getMessage()],
|
||||
Http::STATUS_NOT_IMPLEMENTED
|
||||
);
|
||||
} catch (BadRequestException $e) {
|
||||
return new JSONResponse($e->getReturnMessage(), Http::STATUS_BAD_REQUEST);
|
||||
} catch (AuthenticationFailedException $e) {
|
||||
return new JSONResponse(["message" => "RESOURCE_NOT_FOUND"], Http::STATUS_FORBIDDEN);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
return new JSONResponse(
|
||||
['message' => 'Internal error at ' . $this->urlGenerator->getBaseUrl()],
|
||||
Http::STATUS_BAD_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
return new JSONResponse($result,Http::STATUS_CREATED);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* map login name to internal LDAP UID if a LDAP backend is in use
|
||||
*
|
||||
* @param string $uid
|
||||
* @return string mixed
|
||||
*/
|
||||
private function mapUid($uid) {
|
||||
// FIXME this should be a method in the user management instead
|
||||
$this->logger->debug('shareWith before, ' . $uid, ['app' => $this->appName]);
|
||||
\OCP\Util::emitHook(
|
||||
'\OCA\Files_Sharing\API\Server2Server',
|
||||
'preLoginNameUsedAsUserName',
|
||||
array('uid' => &$uid)
|
||||
);
|
||||
$this->logger->debug('shareWith after, ' . $uid, ['app' => $this->appName]);
|
||||
|
||||
return $uid;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
# compiled vue templates
|
||||
js/templates.js
|
||||
@@ -1,25 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2018, Joas Schilling <coding@schilljs.com>
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
*
|
||||
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
* @author Lukas Reschke <lukas@statuscode.ch>
|
||||
* @author Vincent Petry <pvince81@owncloud.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
$application = new \OCA\Comments\AppInfo\Application();
|
||||
$application->register();
|
||||
$eventDispatcher = \OC::$server->getEventDispatcher();
|
||||
$eventDispatcher->addListener(
|
||||
'OCA\Files::loadAdditionalScripts',
|
||||
function() {
|
||||
\OCP\Util::addScript('oc-backbone-webdav');
|
||||
\OCP\Util::addScript('comments', 'merged');
|
||||
\OCP\Util::addStyle('comments', 'autocomplete');
|
||||
\OCP\Util::addStyle('comments', 'comments');
|
||||
}
|
||||
);
|
||||
|
||||
$eventDispatcher->addListener(\OCP\Comments\CommentsEntityEvent::EVENT_ENTITY, function(\OCP\Comments\CommentsEntityEvent $event) {
|
||||
$event->addEntityCollection('files', function($name) {
|
||||
$nodes = \OC::$server->getUserFolder()->getById(intval($name));
|
||||
return !empty($nodes);
|
||||
});
|
||||
});
|
||||
|
||||
$notificationManager = \OC::$server->getNotificationManager();
|
||||
$notificationManager->registerNotifier(
|
||||
function() {
|
||||
$application = new \OCP\AppFramework\App('comments');
|
||||
return $application->getContainer()->query(\OCA\Comments\Notification\Notifier::class);
|
||||
},
|
||||
function () {
|
||||
$l = \OC::$server->getL10N('comments');
|
||||
return ['id' => 'comments', 'name' => $l->t('Comments')];
|
||||
}
|
||||
);
|
||||
|
||||
$commentsManager = \OC::$server->getCommentsManager();
|
||||
$commentsManager->registerEventHandler(function () {
|
||||
$application = new \OCP\AppFramework\App('comments');
|
||||
/** @var \OCA\Comments\EventHandler $handler */
|
||||
$handler = $application->getContainer()->query(\OCA\Comments\EventHandler::class);
|
||||
return $handler;
|
||||
});
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
<?xml version="1.0"?>
|
||||
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
|
||||
<info>
|
||||
<id>comments</id>
|
||||
<name>Comments</name>
|
||||
<summary>Files app plugin to add comments to files</summary>
|
||||
<description>Files app plugin to add comments to files</description>
|
||||
<version>1.4.0</version>
|
||||
<licence>agpl</licence>
|
||||
<licence>AGPL</licence>
|
||||
<author>Arthur Schiwon</author>
|
||||
<author>Vincent Petry</author>
|
||||
<default_enable/>
|
||||
<version>1.3.0</version>
|
||||
<dependencies>
|
||||
<nextcloud min-version="13" max-version="13" />
|
||||
</dependencies>
|
||||
<types>
|
||||
<logging/>
|
||||
</types>
|
||||
<category>office</category>
|
||||
<category>social</category>
|
||||
<bugs>https://github.com/nextcloud/server/issues</bugs>
|
||||
<dependencies>
|
||||
<nextcloud min-version="14" max-version="14" />
|
||||
</dependencies>
|
||||
|
||||
<activity>
|
||||
<settings>
|
||||
|
||||
@@ -43,7 +43,8 @@ namespace Composer\Autoload;
|
||||
class ClassLoader
|
||||
{
|
||||
// PSR-4
|
||||
private $prefixLengthsPsr4 = array();
|
||||
private $firstCharsPsr4 = array();
|
||||
private $prefixLengthsPsr4 = array(); // For BC with legacy static maps
|
||||
private $prefixDirsPsr4 = array();
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
@@ -170,11 +171,10 @@ class ClassLoader
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
if ('\\' !== substr($prefix, -1)) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->firstCharsPsr4[$prefix[0]] = true;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
@@ -221,11 +221,10 @@ class ClassLoader
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
if ('\\' !== substr($prefix, -1)) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->firstCharsPsr4[$prefix[0]] = true;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
@@ -279,7 +278,7 @@ class ClassLoader
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -373,11 +372,11 @@ class ClassLoader
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
if (isset($this->firstCharsPsr4[$first]) || isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
$search = $subPath.'\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
|
||||
@@ -17,6 +17,4 @@ return array(
|
||||
'OCA\\Comments\\JSSettingsHelper' => $baseDir . '/../lib/JSSettingsHelper.php',
|
||||
'OCA\\Comments\\Notification\\Listener' => $baseDir . '/../lib/Notification/Listener.php',
|
||||
'OCA\\Comments\\Notification\\Notifier' => $baseDir . '/../lib/Notification/Notifier.php',
|
||||
'OCA\\Comments\\Search\\Provider' => $baseDir . '/../lib/Search/Provider.php',
|
||||
'OCA\\Comments\\Search\\Result' => $baseDir . '/../lib/Search/Result.php',
|
||||
);
|
||||
|
||||
@@ -6,11 +6,8 @@ namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInitComments
|
||||
{
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'O' =>
|
||||
array (
|
||||
'OCA\\Comments\\' => 13,
|
||||
),
|
||||
public static $firstCharsPsr4 = array (
|
||||
'O' => true,
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
@@ -32,14 +29,12 @@ class ComposerStaticInitComments
|
||||
'OCA\\Comments\\JSSettingsHelper' => __DIR__ . '/..' . '/../lib/JSSettingsHelper.php',
|
||||
'OCA\\Comments\\Notification\\Listener' => __DIR__ . '/..' . '/../lib/Notification/Listener.php',
|
||||
'OCA\\Comments\\Notification\\Notifier' => __DIR__ . '/..' . '/../lib/Notification/Notifier.php',
|
||||
'OCA\\Comments\\Search\\Provider' => __DIR__ . '/..' . '/../lib/Search/Provider.php',
|
||||
'OCA\\Comments\\Search\\Result' => __DIR__ . '/..' . '/../lib/Search/Result.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInitComments::$prefixLengthsPsr4;
|
||||
$loader->firstCharsPsr4 = ComposerStaticInitComments::$firstCharsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInitComments::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInitComments::$classMap;
|
||||
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
left: 0;
|
||||
display: none;
|
||||
margin-top: 18px;
|
||||
background: var(--color-main-background);
|
||||
color: var(--color-main-text);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: 0 0 5px var(--color-box-shadow);
|
||||
background: $color-main-background;
|
||||
color: $color-main-text;
|
||||
border: 1px solid $color-border;
|
||||
border-radius: $border-radius;
|
||||
box-shadow: 0 0 5px $color-box-shadow;
|
||||
min-width: 120px;
|
||||
z-index: 11110 !important;
|
||||
}
|
||||
@@ -22,14 +22,14 @@
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
cursor: pointer;
|
||||
border-bottom: solid 1px var(--color-border);
|
||||
color: var(--color-main-text);
|
||||
border-bottom: solid 1px $color-border;
|
||||
color: $color-main-text;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.atwho-view .atwho-header .small {
|
||||
color: var(--color-main-text);
|
||||
color: $color-main-text;
|
||||
float: right;
|
||||
padding-top: 2px;
|
||||
margin-right: -5px;
|
||||
@@ -42,18 +42,18 @@
|
||||
}
|
||||
|
||||
.atwho-view .cur {
|
||||
background: var(--color-primary);
|
||||
color: var(--color-primary-text);
|
||||
background: $color-primary;
|
||||
color: $color-primary-text;
|
||||
}
|
||||
.atwho-view .cur small {
|
||||
color: var(--color-primary-text);
|
||||
color: $color-primary-text;
|
||||
}
|
||||
.atwho-view strong {
|
||||
color: var(--color-main-text);
|
||||
color: $color-main-text;
|
||||
font-weight: normal;
|
||||
}
|
||||
.atwho-view .cur strong {
|
||||
color: var(--color-primary-text);
|
||||
color: $color-primary-text;
|
||||
font-weight: normal;
|
||||
}
|
||||
.atwho-view ul {
|
||||
@@ -67,11 +67,11 @@
|
||||
.atwho-view ul li {
|
||||
display: block;
|
||||
padding: 5px 10px;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
border-bottom: 1px solid $color-border;
|
||||
cursor: pointer;
|
||||
}
|
||||
.atwho-view small {
|
||||
font-size: smaller;
|
||||
color: var(--color-main-text);
|
||||
color: $color-main-text;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@@ -13,46 +13,55 @@
|
||||
}
|
||||
|
||||
#commentsTabView .newCommentForm {
|
||||
margin-left: 36px;
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#commentsTabView .newCommentForm .message {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
min-height: 44px;
|
||||
margin: 0;
|
||||
|
||||
/* Prevent the text from overlapping with the submit button. */
|
||||
width: calc(100% - 81px); /* 36 (left margin) + 30 (right padding) + 15 (right padding of surrounding box) */
|
||||
margin-left: 36px;
|
||||
padding-right: 30px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#commentsTabView .newCommentForm {
|
||||
.submit,
|
||||
.submitLoading {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
margin: 0;
|
||||
padding: 13px;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
opacity: .3;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
#commentsTabView .newCommentForm .submit {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
right: 8px;
|
||||
width: 30px;
|
||||
margin: 0;
|
||||
padding: 7px 9px;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
opacity: .3;
|
||||
}
|
||||
|
||||
#commentsTabView .deleteLoading {
|
||||
padding: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#commentsTabView .newCommentForm .submit:not(:disabled):hover,
|
||||
#commentsTabView .newCommentForm .submit:not(:disabled):focus {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#commentsTabView .newCommentForm .submitLoading {
|
||||
background-position: left;
|
||||
|
||||
/* Match rules for '#commentsTabView .newCommentForm .submit' to place the
|
||||
loading icon at the same position as the confirm icon */
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
right: 8px;
|
||||
width: 30px;
|
||||
margin: 0;
|
||||
padding: 7px 9px;
|
||||
|
||||
/* Match rules for 'input[type="submit"]' to place the loading icon at the
|
||||
same position as the confirm icon */
|
||||
min-height: 34px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#commentsTabView .newCommentForm .cancel {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
#commentsTabView .newCommentForm div.message {
|
||||
resize: none;
|
||||
}
|
||||
@@ -64,12 +73,9 @@
|
||||
|
||||
#commentsTabView .comment {
|
||||
position: relative;
|
||||
/** padding bottom is little more so that the top and bottom gap look uniform **/
|
||||
padding: 10px 0px 15px;
|
||||
}
|
||||
|
||||
#commentsTabView .comments .comment {
|
||||
border-top: 1px solid var(--color-border);
|
||||
margin-bottom: 30px;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
#commentsTabView .comment .avatar,
|
||||
@@ -117,23 +123,20 @@
|
||||
background: -o-linear-gradient(rgba(255,255,255,0), rgba(255,255,255,1));
|
||||
background: -ms-linear-gradient(rgba(255,255,255,0), rgba(255,255,255,1));
|
||||
background: linear-gradient(rgba(255,255,255,0), rgba(255,255,255,1));
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#00FFFFFF', endColorstr='#FFFFFFFF');
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#commentsTabView .hidden {
|
||||
#commentsTabView .authorRow>div:not(.contactsmenu-popover) {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#commentsTabView .authorRow>div.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/** set min-height as 44px to ensure that it fits the button sizes. **/
|
||||
#commentsTabView .comment .authorRow {
|
||||
min-height: 44px;
|
||||
}
|
||||
#commentsTabView .comment .authorRow .tooltip {
|
||||
/** because of the padding on the element, the tooltip appear too far up,
|
||||
adding this brings them closer to the element**/
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#commentsTabView .comments li .message .avatar-name-wrapper,
|
||||
.atwho-view-ul * .avatar-name-wrapper,
|
||||
#commentsTabView .comment .authorRow {
|
||||
position: relative;
|
||||
@@ -144,105 +147,64 @@
|
||||
|
||||
#commentsTabView .comment:not(.newCommentRow) .message .avatar-name-wrapper:not(.currentUser),
|
||||
#commentsTabView .comment:not(.newCommentRow) .message .avatar-name-wrapper:not(.currentUser) .avatar,
|
||||
#commentsTabView .comment:not(.newCommentRow) .message .avatar-name-wrapper:not(.currentUser) .avatar img,
|
||||
#commentsTabView .comment .authorRow .avatar:not(.currentUser),
|
||||
#commentsTabView .comment .authorRow .author:not(.currentUser) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.atwho-view-ul .avatar-name-wrapper,
|
||||
.atwho-view-ul .avatar-name-wrapper .avatar,
|
||||
.atwho-view-ul .avatar-name-wrapper .avatar img {
|
||||
.atwho-view-ul .avatar-name-wrapper .avatar {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#commentsTabView .comments li .message .atwho-inserted,
|
||||
#commentsTabView .newCommentForm .atwho-inserted {
|
||||
.avatar-name-wrapper {
|
||||
/* Make the wrapper the positioning context of its child contacts
|
||||
* menu. */
|
||||
position: relative;
|
||||
|
||||
display: inline;
|
||||
vertical-align: top;
|
||||
background-color: var(--color-background-dark);
|
||||
border-radius: 50vh;
|
||||
padding: 1px 7px 1px 1px;
|
||||
|
||||
/* Ensure that the avatar and the user name will be kept together. */
|
||||
white-space: nowrap;
|
||||
|
||||
.avatar {
|
||||
img {
|
||||
vertical-align: top;
|
||||
}
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
vertical-align: middle;
|
||||
padding: 1px;
|
||||
margin-top: -3px;
|
||||
margin-left: 0;
|
||||
margin-right: 2px;
|
||||
}
|
||||
strong {
|
||||
/* Ensure that the user name is shown in bold, as different browsers
|
||||
* use different font weights for strong elements. */
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
.avatar-name-wrapper.currentUser {
|
||||
background-color: var(--color-primary);
|
||||
color: var(--color-primary-text);
|
||||
}
|
||||
#commentsTabView .comments li .message .atwho-inserted {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.atwho-view-ul * .avatar-name-wrapper {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#commentsTabView .comment .author,
|
||||
#commentsTabView .comment .date {
|
||||
opacity: .5;
|
||||
}
|
||||
#commentsTabView .comment .author {
|
||||
max-width: 210px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#commentsTabView .comment .date {
|
||||
margin-left: auto;
|
||||
/** this is to fix the tooltip being too close due to the margin-top applied
|
||||
to bring the tooltip closer for the action icons **/
|
||||
padding: 10px 0px;
|
||||
}
|
||||
|
||||
#commentsTabView .comments > li:not(.newCommentRow) .message {
|
||||
#commentsTabView .comments li .message {
|
||||
padding-left: 40px;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#commentsTabView .comment .action {
|
||||
opacity: 0.3;
|
||||
padding: 14px;
|
||||
display: block;
|
||||
opacity: 0;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#commentsTabView .comment .action:hover,
|
||||
#commentsTabView .comment .action:focus {
|
||||
#commentsTabView .comment:hover .action {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
#commentsTabView .comment .action:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#commentsTabView .newCommentRow .action-container {
|
||||
margin-left: auto;
|
||||
#commentsTabView .comment .action.delete,
|
||||
#commentsTabView .comment .deleteLoading {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#commentsTabView .comment.disabled .message {
|
||||
#commentsTabView .comment.disabled {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
#commentsTabView .comment.disabled .action {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#commentsTabView .message.error {
|
||||
@@ -254,8 +216,3 @@
|
||||
.app-files .action-comment {
|
||||
padding: 16px 14px;
|
||||
}
|
||||
|
||||
#commentsTabView .comment .message .contactsmenu-popover {
|
||||
left: -6px;
|
||||
top: 24px;
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3
|
||||
* or later.
|
||||
*
|
||||
* See the COPYING-README file.
|
||||
*
|
||||
*/
|
||||
|
||||
/* global Handlebars */
|
||||
(function() {
|
||||
var TEMPLATE_MENU =
|
||||
'<ul>' +
|
||||
'{{#each items}}' +
|
||||
'<li>' +
|
||||
'<a href="#" class="menuitem action {{name}} permanent" data-action="{{name}}">' +
|
||||
'{{#if iconClass}}' +
|
||||
'<span class="icon {{iconClass}}"></span>' +
|
||||
'{{else}}' +
|
||||
'<span class="no-icon"></span>' +
|
||||
'{{/if}}' +
|
||||
'<span>{{displayName}}</span>' +
|
||||
'</li>' +
|
||||
'{{/each}}' +
|
||||
'</ul>';
|
||||
|
||||
/**
|
||||
* Construct a new CommentsModifyMenuinstance
|
||||
* @constructs CommentsModifyMenu
|
||||
* @memberof OC.Comments
|
||||
*/
|
||||
var CommentsModifyMenu = OC.Backbone.View.extend({
|
||||
tagName: 'div',
|
||||
className: 'commentsModifyMenu popovermenu bubble menu',
|
||||
_scopes: [
|
||||
{
|
||||
name: 'edit',
|
||||
displayName: t('comments', 'Edit comment'),
|
||||
iconClass: 'icon-rename'
|
||||
},
|
||||
{
|
||||
name: 'delete',
|
||||
displayName: t('comments', 'Delete comment'),
|
||||
iconClass: 'icon-delete'
|
||||
}
|
||||
],
|
||||
initialize: function() {
|
||||
|
||||
},
|
||||
events: {
|
||||
'click a.action': '_onClickAction'
|
||||
},
|
||||
|
||||
template: Handlebars.compile(TEMPLATE_MENU),
|
||||
|
||||
/**
|
||||
* Event handler whenever an action has been clicked within the menu
|
||||
*
|
||||
* @param {Object} event event object
|
||||
*/
|
||||
_onClickAction: function(event) {
|
||||
var $target = $(event.currentTarget);
|
||||
if (!$target.hasClass('menuitem')) {
|
||||
$target = $target.closest('.menuitem');
|
||||
}
|
||||
|
||||
OC.hideMenus();
|
||||
|
||||
this.trigger('select:menu-item-clicked', event, $target.data('action'));
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the menu with the currently set items
|
||||
*/
|
||||
render: function() {
|
||||
this.$el.html(this.template({
|
||||
items: this._scopes
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays the menu
|
||||
*/
|
||||
show: function(context) {
|
||||
this._context = context;
|
||||
|
||||
for(var i in this._scopes) {
|
||||
this._scopes[i].active = false;
|
||||
}
|
||||
|
||||
|
||||
var $el = $(context.target);
|
||||
var offsetIcon = $el.offset();
|
||||
var offsetContainer = $el.closest('.authorRow').offset();
|
||||
|
||||
// adding some extra top offset to push the menu below the button.
|
||||
var position = {
|
||||
top: offsetIcon.top - offsetContainer.top + 48,
|
||||
left: '',
|
||||
right: ''
|
||||
};
|
||||
|
||||
position.left = offsetIcon.left - offsetContainer.left;
|
||||
|
||||
if (position.left > 200) {
|
||||
// we need to position the menu to the right.
|
||||
position.left = '';
|
||||
position.right = this.$el.closest('.comment').find('.date').width();
|
||||
this.$el.removeClass('menu-left').addClass('menu-right');
|
||||
} else {
|
||||
this.$el.removeClass('menu-right').addClass('menu-left');
|
||||
}
|
||||
this.$el.css(position);
|
||||
this.render();
|
||||
this.$el.removeClass('hidden');
|
||||
|
||||
OC.showMenu(null, this.$el);
|
||||
}
|
||||
});
|
||||
|
||||
OCA.Comments = OCA.Comments || {};
|
||||
OCA.Comments.CommentsModifyMenu = CommentsModifyMenu;
|
||||
})(OC, OCA);
|
||||
@@ -21,22 +21,24 @@
|
||||
'<div class="loading hidden" style="height: 50px"></div>';
|
||||
|
||||
var EDIT_COMMENT_TEMPLATE =
|
||||
'<{{tag}} class="newCommentRow comment" data-id="{{id}}">' +
|
||||
'<div class="newCommentRow comment" data-id="{{id}}">' +
|
||||
' <div class="authorRow">' +
|
||||
' <div class="avatar currentUser" data-username="{{actorId}}"></div>' +
|
||||
' <div class="author currentUser">{{actorDisplayName}}</div>' +
|
||||
'{{#if isEditMode}}' +
|
||||
' <div class="action-container">' +
|
||||
' <a href="#" class="action cancel icon icon-close has-tooltip" title="{{cancelText}}"></a>' +
|
||||
' </div>' +
|
||||
' <a href="#" class="action delete icon icon-delete has-tooltip" title="{{deleteTooltip}}"></a>' +
|
||||
' <div class="deleteLoading icon-loading-small hidden"></div>'+
|
||||
'{{/if}}' +
|
||||
' </div>' +
|
||||
' <form class="newCommentForm">' +
|
||||
' <div contentEditable="true" class="message" data-placeholder="{{newMessagePlaceholder}}">{{message}}</div>' +
|
||||
' <input class="submit icon-confirm has-tooltip" type="submit" value="" title="{{submitText}}"/>' +
|
||||
' <input class="submit icon-confirm" type="submit" value="" />' +
|
||||
'{{#if isEditMode}}' +
|
||||
' <input class="cancel pull-right" type="button" value="{{cancelText}}" />' +
|
||||
'{{/if}}' +
|
||||
' <div class="submitLoading icon-loading-small hidden"></div>'+
|
||||
' </form>' +
|
||||
'</{{tag}}>';
|
||||
'</div>';
|
||||
|
||||
var COMMENT_TEMPLATE =
|
||||
'<li class="comment{{#if isUnread}} unread{{/if}}{{#if isLong}} collapsed{{/if}}" data-id="{{id}}">' +
|
||||
@@ -44,8 +46,7 @@
|
||||
' <div class="avatar{{#if isUserAuthor}} currentUser{{/if}}" {{#if actorId}}data-username="{{actorId}}"{{/if}}> </div>' +
|
||||
' <div class="author{{#if isUserAuthor}} currentUser{{/if}}">{{actorDisplayName}}</div>' +
|
||||
'{{#if isUserAuthor}}' +
|
||||
' <a href="#" class="action more icon icon-more has-tooltip"></a>' +
|
||||
' <div class="deleteLoading icon-loading-small hidden"></div>' +
|
||||
' <a href="#" class="action edit icon icon-rename has-tooltip" title="{{editTooltip}}"></a>' +
|
||||
'{{/if}}' +
|
||||
' <div class="date has-tooltip live-relative-timestamp" data-timestamp="{{timestamp}}" title="{{altDate}}">{{date}}</div>' +
|
||||
' </div>' +
|
||||
@@ -63,11 +64,12 @@
|
||||
id: 'commentsTabView',
|
||||
className: 'tab commentsTabView',
|
||||
_autoCompleteData: undefined,
|
||||
_commentsModifyMenu: undefined,
|
||||
|
||||
events: {
|
||||
'submit .newCommentForm': '_onSubmitComment',
|
||||
'click .showMore': '_onClickShowMore',
|
||||
'click .action.edit': '_onClickEditComment',
|
||||
'click .action.delete': '_onClickDeleteComment',
|
||||
'click .cancel': '_onClickCloseComment',
|
||||
'click .comment': '_onClickComment',
|
||||
'keyup div.message': '_onTextChange',
|
||||
@@ -112,9 +114,9 @@
|
||||
actorId: currentUser.uid,
|
||||
actorDisplayName: currentUser.displayName,
|
||||
newMessagePlaceholder: t('comments', 'New comment …'),
|
||||
deleteTooltip: t('comments', 'Delete comment'),
|
||||
submitText: t('comments', 'Post'),
|
||||
cancelText: t('comments', 'Cancel'),
|
||||
tag: 'li'
|
||||
cancelText: t('comments', 'Cancel')
|
||||
}, params));
|
||||
},
|
||||
|
||||
@@ -164,7 +166,7 @@
|
||||
emptyResultLabel: t('comments', 'No comments yet, start the conversation!'),
|
||||
moreLabel: t('comments', 'More comments …')
|
||||
}));
|
||||
this.$el.find('.comments').before(this.editCommentTemplate({ tag: 'div'}));
|
||||
this.$el.find('.comments').before(this.editCommentTemplate({}));
|
||||
this.$el.find('.has-tooltip').tooltip();
|
||||
this.$container = this.$el.find('ul.comments');
|
||||
this.$el.find('.avatar').avatar(OC.getCurrentUser().uid, 32);
|
||||
@@ -172,7 +174,6 @@
|
||||
this.$el.find('.message').on('keydown input change', this._onTypeComment);
|
||||
|
||||
autosize(this.$el.find('.newCommentRow .message'))
|
||||
this.$el.find('.newCommentForm .message').focus();
|
||||
},
|
||||
|
||||
_initAutoComplete: function($target) {
|
||||
@@ -195,28 +196,22 @@
|
||||
},
|
||||
sorter: function (q, items) { return items; }
|
||||
},
|
||||
displayTpl: function (item) {
|
||||
return '<li>' +
|
||||
'<span class="avatar-name-wrapper">' +
|
||||
'<span class="avatar" ' +
|
||||
'data-username="' + escapeHTML(item.id) + '" ' + // for avatars
|
||||
'data-user="' + escapeHTML(item.id) + '" ' + // for contactsmenu
|
||||
'data-user-display-name="' + escapeHTML(item.label) + '">' +
|
||||
'</span>' +
|
||||
'<strong>' + escapeHTML(item.label) + '</strong>' +
|
||||
'</span></li>';
|
||||
},
|
||||
insertTpl: function (item) {
|
||||
return '' +
|
||||
'<span class="avatar-name-wrapper">' +
|
||||
'<span class="avatar" ' +
|
||||
'data-username="' + escapeHTML(item.id) + '" ' + // for avatars
|
||||
'data-user="' + escapeHTML(item.id) + '" ' + // for contactsmenu
|
||||
'data-user-display-name="' + escapeHTML(item.label) + '">' +
|
||||
'</span>' +
|
||||
'<strong>' + escapeHTML(item.label) + '</strong>' +
|
||||
'</span>';
|
||||
},
|
||||
displayTpl: '<li>'
|
||||
+ '<span class="avatar-name-wrapper">'
|
||||
+ '<div class="avatar" '
|
||||
+ 'data-username="${id}"' // for avatars
|
||||
+ ' data-user="${id}"' // for contactsmenu
|
||||
+ ' data-user-display-name="${label}"></div>'
|
||||
+ ' <strong>${label}</strong>'
|
||||
+ '</span></li>',
|
||||
insertTpl: ''
|
||||
+ '<span class="avatar-name-wrapper">'
|
||||
+ '<div class="avatar" '
|
||||
+ 'data-username="${id}"' // for avatars
|
||||
+ ' data-user="${id}"' // for contactsmenu
|
||||
+ ' data-user-display-name="${label}"></div>'
|
||||
+ ' <strong>${label}</strong>'
|
||||
+ '</span>',
|
||||
searchKey: "label"
|
||||
});
|
||||
$target.on('inserted.atwho', function (je, $el) {
|
||||
@@ -226,7 +221,7 @@
|
||||
// passing the whole comments form would re-apply and request
|
||||
// avatars from the server
|
||||
$(je.target).find(
|
||||
'span[data-username="' + $el.find('[data-username]').data('username') + '"]'
|
||||
'div[data-username="' + $el.find('[data-username]').data('username') + '"]'
|
||||
).parent(),
|
||||
editionMode
|
||||
);
|
||||
@@ -245,22 +240,19 @@
|
||||
if(!_.isUndefined(this._autoCompleteRequestCall)) {
|
||||
this._autoCompleteRequestCall.abort();
|
||||
}
|
||||
this._autoCompleteRequestCall = $.ajax({
|
||||
url: OC.linkToOCS('core', 2) + 'autocomplete/get',
|
||||
data: {
|
||||
this._autoCompleteRequestCall = $.get(
|
||||
OC.generateUrl('/autocomplete/get'),
|
||||
{
|
||||
search: query,
|
||||
itemType: 'files',
|
||||
itemId: s.model.get('id'),
|
||||
sorter: 'commenters|share-recipients',
|
||||
limit: OC.appConfig.comments.maxAutoCompleteResults
|
||||
},
|
||||
beforeSend: function (request) {
|
||||
request.setRequestHeader('Accept', 'application/json');
|
||||
},
|
||||
success: function (result) {
|
||||
callback(result.ocs.data);
|
||||
function (data) {
|
||||
callback(data);
|
||||
}
|
||||
});
|
||||
);
|
||||
}, 400);
|
||||
},
|
||||
|
||||
@@ -310,8 +302,6 @@
|
||||
}
|
||||
);
|
||||
}
|
||||
this.$el.find('.newCommentForm .message').focus();
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -392,15 +382,7 @@
|
||||
|
||||
_postRenderItem: function($el, editionMode) {
|
||||
$el.find('.has-tooltip').tooltip();
|
||||
var inlineAvatars = $el.find('.message .avatar');
|
||||
if ($($el.context).hasClass('message')) {
|
||||
inlineAvatars = $el.find('.avatar');
|
||||
}
|
||||
inlineAvatars.each(function () {
|
||||
var $this = $(this);
|
||||
$this.avatar($this.attr('data-username'), 16);
|
||||
});
|
||||
$el.find('.authorRow .avatar').each(function () {
|
||||
$el.find('.avatar').each(function() {
|
||||
var $this = $(this);
|
||||
$this.avatar($this.attr('data-username'), 32);
|
||||
});
|
||||
@@ -416,24 +398,6 @@
|
||||
// it is the case when writing a comment and mentioning a person
|
||||
$message = $el;
|
||||
}
|
||||
|
||||
|
||||
if (!editionMode) {
|
||||
var self = this;
|
||||
// add the dropdown menu to display the edit and delete option
|
||||
var modifyCommentMenu = new OCA.Comments.CommentsModifyMenu();
|
||||
$el.find('.authorRow').append(modifyCommentMenu.$el);
|
||||
$el.find('.more').on('click', _.bind(modifyCommentMenu.show, modifyCommentMenu));
|
||||
|
||||
self.listenTo(modifyCommentMenu, 'select:menu-item-clicked', function(ev, action) {
|
||||
if (action === 'edit') {
|
||||
self._onClickEditComment(ev);
|
||||
} else if (action === 'delete') {
|
||||
self._onClickDeleteComment(ev);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this._postRenderMessage($message, editionMode);
|
||||
},
|
||||
|
||||
@@ -442,13 +406,14 @@
|
||||
return;
|
||||
}
|
||||
|
||||
$el.find('.avatar-name-wrapper').each(function() {
|
||||
var $this = $(this);
|
||||
var $avatar = $this.find('.avatar');
|
||||
$el.find('.avatar').each(function() {
|
||||
var avatar = $(this);
|
||||
var strong = $(this).next();
|
||||
var appendTo = $(this).parent();
|
||||
|
||||
var user = $avatar.data('user');
|
||||
if (user !== OC.getCurrentUser().uid) {
|
||||
$this.contactsMenu(user, 0, $this);
|
||||
var username = $(this).data('username');
|
||||
if (username !== oc_current_user) {
|
||||
$.merge(avatar, strong).contactsMenu(avatar.data('user'), 0, appendTo);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -487,22 +452,20 @@
|
||||
},
|
||||
|
||||
_composeHTMLMention: function(uid, displayName) {
|
||||
var avatar = '' +
|
||||
'<span class="avatar" ' +
|
||||
'data-username="' + _.escape(uid) + '" ' +
|
||||
'data-user="' + _.escape(uid) + '" ' +
|
||||
'data-user-display-name="' + _.escape(displayName) + '">' +
|
||||
'</span>';
|
||||
var avatar = '<div class="avatar" '
|
||||
+ 'data-username="' + _.escape(uid) + '"'
|
||||
+ ' data-user="' + _.escape(uid) + '"'
|
||||
+ ' data-user-display-name="'
|
||||
+ _.escape(displayName) + '"></div>';
|
||||
|
||||
var isCurrentUser = (uid === OC.getCurrentUser().uid);
|
||||
|
||||
return '' +
|
||||
'<span class="atwho-inserted" contenteditable="false">' +
|
||||
'<span class="avatar-name-wrapper' + (isCurrentUser ? ' currentUser' : '') + '">' +
|
||||
avatar +
|
||||
'<strong>' + _.escape(displayName) + '</strong>' +
|
||||
'</span>' +
|
||||
'</span>';
|
||||
return ''
|
||||
+ '<span class="atwho-inserted" contenteditable="false">'
|
||||
+ '<span class="avatar-name-wrapper' + (isCurrentUser ? ' currentUser' : '') + '">'
|
||||
+ avatar + ' <strong>'+ _.escape(displayName)+'</strong>'
|
||||
+ '</span>'
|
||||
+ '</span>';
|
||||
},
|
||||
|
||||
nextPage: function() {
|
||||
@@ -601,13 +564,15 @@
|
||||
var $comment = $(ev.target).closest('.comment');
|
||||
var commentId = $comment.data('id');
|
||||
var $loading = $comment.find('.deleteLoading');
|
||||
var $moreIcon = $comment.find('.more');
|
||||
var $commentField = $comment.find('.message');
|
||||
var $submit = $comment.find('.submit');
|
||||
var $cancel = $comment.find('.cancel');
|
||||
|
||||
$commentField.prop('contenteditable', false);
|
||||
$submit.prop('disabled', true);
|
||||
$cancel.prop('disabled', true);
|
||||
$comment.addClass('disabled');
|
||||
$loading.removeClass('hidden');
|
||||
$moreIcon.addClass('hidden');
|
||||
|
||||
$comment.data('commentEl', $comment);
|
||||
|
||||
this.collection.get(commentId).destroy({
|
||||
success: function() {
|
||||
@@ -616,8 +581,10 @@
|
||||
},
|
||||
error: function() {
|
||||
$loading.addClass('hidden');
|
||||
$moreIcon.removeClass('hidden');
|
||||
$comment.removeClass('disabled');
|
||||
$commentField.prop('contenteditable', true);
|
||||
$submit.prop('disabled', false);
|
||||
$cancel.prop('disabled', false);
|
||||
|
||||
OC.Notification.showTemporary(t('comments', 'Error occurred while retrieving comment with ID {id}', {id: commentId}));
|
||||
}
|
||||
@@ -698,12 +665,6 @@
|
||||
}, {
|
||||
success: function(model) {
|
||||
self._onSubmitSuccess(model, $form);
|
||||
if(model.get('message').trim() === model.previous('message').trim()) {
|
||||
// model change event doesn't trigger, manually remove the row.
|
||||
var $row = $form.closest('.comment');
|
||||
$row.data('commentEl').removeClass('hidden');
|
||||
$row.remove();
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
self._onSubmitError($form, commentId);
|
||||
|
||||
@@ -4,10 +4,8 @@
|
||||
"commentcollection.js",
|
||||
"commentsummarymodel.js",
|
||||
"commentstabview.js",
|
||||
"commentsmodifymenu.js",
|
||||
"filesplugin.js",
|
||||
"activitytabviewplugin.js",
|
||||
"search.js",
|
||||
"vendor/Caret.js/dist/jquery.caret.min.js",
|
||||
"vendor/At.js/dist/js/jquery.atwho.min.js"
|
||||
]
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3
|
||||
* or later.
|
||||
*
|
||||
* See the COPYING-README file.
|
||||
*
|
||||
*/
|
||||
(function(OC, OCA, $) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Construct a new FileActions instance
|
||||
* @constructs Files
|
||||
*/
|
||||
var Comment = function() {
|
||||
this.initialize();
|
||||
};
|
||||
|
||||
Comment.prototype = {
|
||||
|
||||
fileList: null,
|
||||
|
||||
/**
|
||||
* Initialize the file search
|
||||
*/
|
||||
initialize: function() {
|
||||
|
||||
var self = this;
|
||||
|
||||
this.fileAppLoaded = function() {
|
||||
return !!OCA.Files && !!OCA.Files.App;
|
||||
};
|
||||
function inFileList($row, result) {
|
||||
return false;
|
||||
|
||||
if (! self.fileAppLoaded()) {
|
||||
return false;
|
||||
}
|
||||
var dir = self.fileList.getCurrentDirectory().replace(/\/+$/,'');
|
||||
var resultDir = OC.dirname(result.path);
|
||||
return dir === resultDir && self.fileList.inList(result.name);
|
||||
}
|
||||
function hideNoFilterResults() {
|
||||
var $nofilterresults = $('.nofilterresults');
|
||||
if ( ! $nofilterresults.hasClass('hidden') ) {
|
||||
$nofilterresults.addClass('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {jQuery} $row
|
||||
* @param {Object} result
|
||||
* @param {int} result.id
|
||||
* @param {string} result.comment
|
||||
* @param {string} result.authorId
|
||||
* @param {string} result.authorName
|
||||
* @param {string} result.link
|
||||
* @param {string} result.fileName
|
||||
* @param {string} result.path
|
||||
* @returns {*}
|
||||
*/
|
||||
this.renderCommentResult = function($row, result) {
|
||||
if (inFileList($row, result)) {
|
||||
return null;
|
||||
}
|
||||
hideNoFilterResults();
|
||||
/*render preview icon, show path beneath filename,
|
||||
show size and last modified date on the right */
|
||||
this.updateLegacyMimetype(result);
|
||||
|
||||
var $pathDiv = $('<div>').addClass('path').text(result.path);
|
||||
|
||||
var $avatar = $('<div>');
|
||||
$avatar.addClass('avatar')
|
||||
.css('display', 'inline-block')
|
||||
.css('vertical-align', 'middle')
|
||||
.css('margin', '0 5px 2px 3px');
|
||||
|
||||
if (result.authorName) {
|
||||
$avatar.avatar(result.authorId, 21, undefined, false, undefined, result.authorName);
|
||||
} else {
|
||||
$avatar.avatar(result.authorId, 21);
|
||||
}
|
||||
|
||||
$row.find('td.info div.name').after($pathDiv).text(result.comment).prepend($('<span>').addClass('path').css('margin-right', '5px').text(result.authorName)).prepend($avatar);
|
||||
$row.find('td.result a').attr('href', result.link);
|
||||
|
||||
$row.find('td.icon')
|
||||
.css('background-image', 'url(' + OC.imagePath('core', 'actions/comment') + ')')
|
||||
.css('opacity', '.4');
|
||||
var dir = OC.dirname(result.path);
|
||||
// "result.path" does not include a leading "/", so "OC.dirname"
|
||||
// returns the path itself for files or folders in the root.
|
||||
if (dir === result.path) {
|
||||
dir = '/';
|
||||
}
|
||||
$row.find('td.info a').attr('href',
|
||||
OC.generateUrl('/apps/files/?dir={dir}&scrollto={scrollto}', {dir: dir, scrollto: result.fileName})
|
||||
);
|
||||
|
||||
return $row;
|
||||
};
|
||||
|
||||
this.handleCommentClick = function($row, result, event) {
|
||||
if (self.fileAppLoaded() && self.fileList.id === 'files') {
|
||||
self.fileList.changeDirectory(OC.dirname(result.path));
|
||||
self.fileList.scrollTo(result.name);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
this.updateLegacyMimetype = function (result) {
|
||||
// backward compatibility:
|
||||
if (!result.mime && result.mime_type) {
|
||||
result.mime = result.mime_type;
|
||||
}
|
||||
};
|
||||
this.setFileList = function (fileList) {
|
||||
this.fileList = fileList;
|
||||
};
|
||||
|
||||
OC.Plugins.register('OCA.Search.Core', this);
|
||||
},
|
||||
attach: function(search) {
|
||||
search.setRenderer('comment', this.renderCommentResult.bind(this));
|
||||
search.setHandler('comment', this.handleCommentClick.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
OCA.Search.comment = new Comment();
|
||||
})(OC, OCA, $);
|
||||
@@ -2,6 +2,20 @@ OC.L10N.register(
|
||||
"comments",
|
||||
{
|
||||
"Comments" : "Kommentare",
|
||||
"New comment …" : "Nuwe kommentaar…",
|
||||
"Delete comment" : "Skrap kommentaar",
|
||||
"Post" : "Plaas",
|
||||
"Cancel" : "Kanselleer",
|
||||
"Edit comment" : "Wysig kommentaar",
|
||||
"[Deleted user]" : "[Geskrapte gebruiker]",
|
||||
"No comments yet, start the conversation!" : "Nog geen kommentaar, begin die gesprek!",
|
||||
"More comments …" : "Nog kommentare …",
|
||||
"Save" : "Bewaar",
|
||||
"Allowed characters {count} of {max}" : "Toegelate karakters {telling} van {maks}",
|
||||
"Error occurred while updating comment with id {id}" : "’n Fout het voorgekom toe kommentaar met id {id} bygewerk is",
|
||||
"Error occurred while posting comment" : "’n Fout het voorgekom toe kommentaar geplaas is",
|
||||
"_%n unread comment_::_%n unread comments_" : ["%n ongeleesde kommentaar","%n ongeleeste kommentare"],
|
||||
"Comment" : "Kommentaar",
|
||||
"You commented" : "U het kommentaar gelewer",
|
||||
"%1$s commented" : "%1$s het kommentaar gelewer",
|
||||
"{author} commented" : "{outeur} het kommentaar gelewer",
|
||||
@@ -10,25 +24,10 @@ OC.L10N.register(
|
||||
"%1$s commented on %2$s" : "%1$s het op %2$s kommentaar gelewer",
|
||||
"{author} commented on {file}" : "{outeur} het op {lêer} kommentaar gelewer",
|
||||
"<strong>Comments</strong> for files" : "<strong>Kommentare</strong> vir lêers",
|
||||
"You were mentioned on “{file}”, in a comment by a user that has since been deleted" : "Jy is genoem op “{lêer}”, in die kommentaar van 'n gebruiker wat intussen geskrap is.",
|
||||
"%1$s mentioned you in a comment on “%2$s”" : "%1$s het u in ’n kommentaar op “%2$s” genoem",
|
||||
"{user} mentioned you in a comment on “{file}”" : "{gebruiker} het u in ’n kommentaar oor “{lêer}” genoem",
|
||||
"Files app plugin to add comments to files" : "Lêertoepinprop om kommentaar op lêers te lewer",
|
||||
"Edit comment" : "Wysig kommentaar",
|
||||
"Delete comment" : "Skrap kommentaar",
|
||||
"New comment …" : "Nuwe kommentaar…",
|
||||
"Post" : "Plaas",
|
||||
"Cancel" : "Kanselleer",
|
||||
"[Deleted user]" : "[Geskrapte gebruiker]",
|
||||
"No comments yet, start the conversation!" : "Nog geen kommentaar, begin die gesprek!",
|
||||
"More comments …" : "Nog kommentare …",
|
||||
"Save" : "Bewaar",
|
||||
"Allowed characters {count} of {max}" : "Toegelate karakters {telling} van {maks}",
|
||||
"Error occurred while retrieving comment with ID {id}" : "’n Fout het voorgekom toe kommentaar met ID {id} opgehaal is",
|
||||
"Error occurred while updating comment with id {id}" : "’n Fout het voorgekom toe kommentaar met ID {id} bygewerk is",
|
||||
"Error occurred while posting comment" : "’n Fout het voorgekom toe kommentaar geplaas is",
|
||||
"_%n unread comment_::_%n unread comments_" : ["%n ongelese kommentaar","%n ongelese kommentare"],
|
||||
"Comment" : "Kommentaar",
|
||||
"You were mentioned on “%s”, in a comment by a user that has since been deleted" : "Jy was genoem op \"%s\", in die kommentaar van 'n gebruiker wat intussen geskrap is.",
|
||||
"%1$s mentioned you in a comment on “%2$s”" : "%1$s het u in ’n kommentaar op “%2$s” genoem"
|
||||
"Unknown user" : "Onbekende gebruiker",
|
||||
"A (now) deleted user mentioned you in a comment on “%s”" : "’n (Nou) geskrapte gebruiker het u in ’n kommentaar op “%s” genoem",
|
||||
"A (now) deleted user mentioned you in a comment on “{file}”" : "’n (Nou) geskrapte gebruiker het u in ’n kommentaar op “{lêer}” genoem"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
{ "translations": {
|
||||
"Comments" : "Kommentare",
|
||||
"New comment …" : "Nuwe kommentaar…",
|
||||
"Delete comment" : "Skrap kommentaar",
|
||||
"Post" : "Plaas",
|
||||
"Cancel" : "Kanselleer",
|
||||
"Edit comment" : "Wysig kommentaar",
|
||||
"[Deleted user]" : "[Geskrapte gebruiker]",
|
||||
"No comments yet, start the conversation!" : "Nog geen kommentaar, begin die gesprek!",
|
||||
"More comments …" : "Nog kommentare …",
|
||||
"Save" : "Bewaar",
|
||||
"Allowed characters {count} of {max}" : "Toegelate karakters {telling} van {maks}",
|
||||
"Error occurred while updating comment with id {id}" : "’n Fout het voorgekom toe kommentaar met id {id} bygewerk is",
|
||||
"Error occurred while posting comment" : "’n Fout het voorgekom toe kommentaar geplaas is",
|
||||
"_%n unread comment_::_%n unread comments_" : ["%n ongeleesde kommentaar","%n ongeleeste kommentare"],
|
||||
"Comment" : "Kommentaar",
|
||||
"You commented" : "U het kommentaar gelewer",
|
||||
"%1$s commented" : "%1$s het kommentaar gelewer",
|
||||
"{author} commented" : "{outeur} het kommentaar gelewer",
|
||||
@@ -8,25 +22,10 @@
|
||||
"%1$s commented on %2$s" : "%1$s het op %2$s kommentaar gelewer",
|
||||
"{author} commented on {file}" : "{outeur} het op {lêer} kommentaar gelewer",
|
||||
"<strong>Comments</strong> for files" : "<strong>Kommentare</strong> vir lêers",
|
||||
"You were mentioned on “{file}”, in a comment by a user that has since been deleted" : "Jy is genoem op “{lêer}”, in die kommentaar van 'n gebruiker wat intussen geskrap is.",
|
||||
"%1$s mentioned you in a comment on “%2$s”" : "%1$s het u in ’n kommentaar op “%2$s” genoem",
|
||||
"{user} mentioned you in a comment on “{file}”" : "{gebruiker} het u in ’n kommentaar oor “{lêer}” genoem",
|
||||
"Files app plugin to add comments to files" : "Lêertoepinprop om kommentaar op lêers te lewer",
|
||||
"Edit comment" : "Wysig kommentaar",
|
||||
"Delete comment" : "Skrap kommentaar",
|
||||
"New comment …" : "Nuwe kommentaar…",
|
||||
"Post" : "Plaas",
|
||||
"Cancel" : "Kanselleer",
|
||||
"[Deleted user]" : "[Geskrapte gebruiker]",
|
||||
"No comments yet, start the conversation!" : "Nog geen kommentaar, begin die gesprek!",
|
||||
"More comments …" : "Nog kommentare …",
|
||||
"Save" : "Bewaar",
|
||||
"Allowed characters {count} of {max}" : "Toegelate karakters {telling} van {maks}",
|
||||
"Error occurred while retrieving comment with ID {id}" : "’n Fout het voorgekom toe kommentaar met ID {id} opgehaal is",
|
||||
"Error occurred while updating comment with id {id}" : "’n Fout het voorgekom toe kommentaar met ID {id} bygewerk is",
|
||||
"Error occurred while posting comment" : "’n Fout het voorgekom toe kommentaar geplaas is",
|
||||
"_%n unread comment_::_%n unread comments_" : ["%n ongelese kommentaar","%n ongelese kommentare"],
|
||||
"Comment" : "Kommentaar",
|
||||
"You were mentioned on “%s”, in a comment by a user that has since been deleted" : "Jy was genoem op \"%s\", in die kommentaar van 'n gebruiker wat intussen geskrap is.",
|
||||
"%1$s mentioned you in a comment on “%2$s”" : "%1$s het u in ’n kommentaar op “%2$s” genoem"
|
||||
"Unknown user" : "Onbekende gebruiker",
|
||||
"A (now) deleted user mentioned you in a comment on “%s”" : "’n (Nou) geskrapte gebruiker het u in ’n kommentaar op “%s” genoem",
|
||||
"A (now) deleted user mentioned you in a comment on “{file}”" : "’n (Nou) geskrapte gebruiker het u in ’n kommentaar op “{lêer}” genoem"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -2,18 +2,11 @@ OC.L10N.register(
|
||||
"comments",
|
||||
{
|
||||
"Comments" : "تعليقات",
|
||||
"You commented" : "قمت بكتابة تعليق",
|
||||
"%1$s commented" : "%1$s كتب تعليق",
|
||||
"You commented on %1$s" : "لقد علقت على %1$s",
|
||||
"You commented on {file}" : "علقت على {file}",
|
||||
"%1$s commented on %2$s" : "%1$s كتب تعليق على %2$s",
|
||||
"{author} commented on {file}" : "{author} علّق على {file}",
|
||||
"<strong>Comments</strong> for files" : "<strong>تعليقات</strong> على الملفات",
|
||||
"Edit comment" : "تعديل التعليق",
|
||||
"Delete comment" : "حذف التعليق",
|
||||
"New comment …" : "تعليق جديد",
|
||||
"Delete comment" : "حذف التعليق",
|
||||
"Post" : "ارسال",
|
||||
"Cancel" : "إلغاء",
|
||||
"Edit comment" : "تعديل التعليق",
|
||||
"[Deleted user]" : "[مستخدم محذوف]",
|
||||
"No comments yet, start the conversation!" : "لا يوجد تعليقات, ابدأ النقاش الآن!",
|
||||
"More comments …" : "مزيد من التعليقات...",
|
||||
@@ -21,7 +14,13 @@ OC.L10N.register(
|
||||
"Allowed characters {count} of {max}" : "عدد الأحرف المسموح بها {count} من {max}",
|
||||
"Error occurred while updating comment with id {id}" : "حصل خطأ أثناء تعديل التعليق رقم {id}",
|
||||
"Error occurred while posting comment" : "حصل خطأ أثناء إرسال التعليق",
|
||||
"_%n unread comment_::_%n unread comments_" : ["%n تعليق غير مقروء","%n تعليق غير مقروء","تعليقان غير مقروءة","%n تعليقات غير مقروء","%n تعليق غير مقروء","%n تعليق غير مقروء"],
|
||||
"Comment" : "تعليق"
|
||||
"Comment" : "تعليق",
|
||||
"You commented" : "قمت بكتابة تعليق",
|
||||
"%1$s commented" : "%1$s كتب تعليق",
|
||||
"You commented on {file}" : "علقت على {file}",
|
||||
"%1$s commented on %2$s" : "%1$s كتب تعليق على %2$s",
|
||||
"{author} commented on {file}" : "{author} علّق على {file}",
|
||||
"<strong>Comments</strong> for files" : "<strong>تعليقات</strong> على الملفات",
|
||||
"Unknown user" : "مستخدم غير معروف"
|
||||
},
|
||||
"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;");
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
{ "translations": {
|
||||
"Comments" : "تعليقات",
|
||||
"You commented" : "قمت بكتابة تعليق",
|
||||
"%1$s commented" : "%1$s كتب تعليق",
|
||||
"You commented on %1$s" : "لقد علقت على %1$s",
|
||||
"You commented on {file}" : "علقت على {file}",
|
||||
"%1$s commented on %2$s" : "%1$s كتب تعليق على %2$s",
|
||||
"{author} commented on {file}" : "{author} علّق على {file}",
|
||||
"<strong>Comments</strong> for files" : "<strong>تعليقات</strong> على الملفات",
|
||||
"Edit comment" : "تعديل التعليق",
|
||||
"Delete comment" : "حذف التعليق",
|
||||
"New comment …" : "تعليق جديد",
|
||||
"Delete comment" : "حذف التعليق",
|
||||
"Post" : "ارسال",
|
||||
"Cancel" : "إلغاء",
|
||||
"Edit comment" : "تعديل التعليق",
|
||||
"[Deleted user]" : "[مستخدم محذوف]",
|
||||
"No comments yet, start the conversation!" : "لا يوجد تعليقات, ابدأ النقاش الآن!",
|
||||
"More comments …" : "مزيد من التعليقات...",
|
||||
@@ -19,7 +12,13 @@
|
||||
"Allowed characters {count} of {max}" : "عدد الأحرف المسموح بها {count} من {max}",
|
||||
"Error occurred while updating comment with id {id}" : "حصل خطأ أثناء تعديل التعليق رقم {id}",
|
||||
"Error occurred while posting comment" : "حصل خطأ أثناء إرسال التعليق",
|
||||
"_%n unread comment_::_%n unread comments_" : ["%n تعليق غير مقروء","%n تعليق غير مقروء","تعليقان غير مقروءة","%n تعليقات غير مقروء","%n تعليق غير مقروء","%n تعليق غير مقروء"],
|
||||
"Comment" : "تعليق"
|
||||
"Comment" : "تعليق",
|
||||
"You commented" : "قمت بكتابة تعليق",
|
||||
"%1$s commented" : "%1$s كتب تعليق",
|
||||
"You commented on {file}" : "علقت على {file}",
|
||||
"%1$s commented on %2$s" : "%1$s كتب تعليق على %2$s",
|
||||
"{author} commented on {file}" : "{author} علّق على {file}",
|
||||
"<strong>Comments</strong> for files" : "<strong>تعليقات</strong> على الملفات",
|
||||
"Unknown user" : "مستخدم غير معروف"
|
||||
},"pluralForm" :"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;"
|
||||
}
|
||||
@@ -2,20 +2,11 @@ OC.L10N.register(
|
||||
"comments",
|
||||
{
|
||||
"Comments" : "Comentarios",
|
||||
"You commented" : "Comentesti",
|
||||
"%1$s commented" : "%1$s comentó",
|
||||
"{author} commented" : "{author} comentó",
|
||||
"You commented on %1$s" : "Comentesti en %1$s",
|
||||
"You commented on {file}" : "Comentesti en {file}",
|
||||
"%1$s commented on %2$s" : "%1$s comentó en %2$s",
|
||||
"{author} commented on {file}" : "{autor} comentó en {ficheru}",
|
||||
"<strong>Comments</strong> for files" : "<strong>Comentarios</strong> pa ficheros",
|
||||
"{user} mentioned you in a comment on “{file}”" : "{user} mentóte nun comentariu de «{file}»",
|
||||
"Edit comment" : "Editar comentariu",
|
||||
"Delete comment" : "Desaniciar comentariu",
|
||||
"New comment …" : "Comentariu nuevu...",
|
||||
"Delete comment" : "Desaniciar comentariu",
|
||||
"Post" : "Espublizar",
|
||||
"Cancel" : "Encaboxar",
|
||||
"Edit comment" : "Editar comentariu",
|
||||
"[Deleted user]" : "[Usuariu desaniciáu]",
|
||||
"No comments yet, start the conversation!" : "Entá nun hai comentarios. ¡Entama una conversación!",
|
||||
"More comments …" : "Más comentarios...",
|
||||
@@ -25,6 +16,18 @@ OC.L10N.register(
|
||||
"Error occurred while posting comment" : "Asocedieron fallos entrín s'espublizaba'l comentariu",
|
||||
"_%n unread comment_::_%n unread comments_" : ["%n comentariu ensin lleer","%n comentarios ensin lleer"],
|
||||
"Comment" : "Comentariu",
|
||||
"%1$s mentioned you in a comment on “%2$s”" : "%1$s mentóte nun comentariu de «%2$s»"
|
||||
"You commented" : "Comentesti",
|
||||
"%1$s commented" : "%1$s comentó",
|
||||
"{author} commented" : "{author} comentó",
|
||||
"You commented on %1$s" : "Comentesti en %1$s",
|
||||
"You commented on {file}" : "Comentesti en {file}",
|
||||
"%1$s commented on %2$s" : "%1$s comentó en %2$s",
|
||||
"{author} commented on {file}" : "{autor} comentó en {ficheru}",
|
||||
"<strong>Comments</strong> for files" : "<strong>Comentarios</strong> pa ficheros",
|
||||
"%1$s mentioned you in a comment on “%2$s”" : "%1$s mentóte nun comentariu de «%2$s»",
|
||||
"{user} mentioned you in a comment on “{file}”" : "{user} mentóte nun comentariu de «{file}»",
|
||||
"Unknown user" : "Usuariu desconocíu",
|
||||
"A (now) deleted user mentioned you in a comment on “%s”" : "Un usuariu (agora) desaniciáu mentóte nun comentariu de «%s»",
|
||||
"A (now) deleted user mentioned you in a comment on “{file}”" : "Un usuariu (agora) desaniciáu mentóte nun comentariu de «{file}»"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -1,19 +1,10 @@
|
||||
{ "translations": {
|
||||
"Comments" : "Comentarios",
|
||||
"You commented" : "Comentesti",
|
||||
"%1$s commented" : "%1$s comentó",
|
||||
"{author} commented" : "{author} comentó",
|
||||
"You commented on %1$s" : "Comentesti en %1$s",
|
||||
"You commented on {file}" : "Comentesti en {file}",
|
||||
"%1$s commented on %2$s" : "%1$s comentó en %2$s",
|
||||
"{author} commented on {file}" : "{autor} comentó en {ficheru}",
|
||||
"<strong>Comments</strong> for files" : "<strong>Comentarios</strong> pa ficheros",
|
||||
"{user} mentioned you in a comment on “{file}”" : "{user} mentóte nun comentariu de «{file}»",
|
||||
"Edit comment" : "Editar comentariu",
|
||||
"Delete comment" : "Desaniciar comentariu",
|
||||
"New comment …" : "Comentariu nuevu...",
|
||||
"Delete comment" : "Desaniciar comentariu",
|
||||
"Post" : "Espublizar",
|
||||
"Cancel" : "Encaboxar",
|
||||
"Edit comment" : "Editar comentariu",
|
||||
"[Deleted user]" : "[Usuariu desaniciáu]",
|
||||
"No comments yet, start the conversation!" : "Entá nun hai comentarios. ¡Entama una conversación!",
|
||||
"More comments …" : "Más comentarios...",
|
||||
@@ -23,6 +14,18 @@
|
||||
"Error occurred while posting comment" : "Asocedieron fallos entrín s'espublizaba'l comentariu",
|
||||
"_%n unread comment_::_%n unread comments_" : ["%n comentariu ensin lleer","%n comentarios ensin lleer"],
|
||||
"Comment" : "Comentariu",
|
||||
"%1$s mentioned you in a comment on “%2$s”" : "%1$s mentóte nun comentariu de «%2$s»"
|
||||
"You commented" : "Comentesti",
|
||||
"%1$s commented" : "%1$s comentó",
|
||||
"{author} commented" : "{author} comentó",
|
||||
"You commented on %1$s" : "Comentesti en %1$s",
|
||||
"You commented on {file}" : "Comentesti en {file}",
|
||||
"%1$s commented on %2$s" : "%1$s comentó en %2$s",
|
||||
"{author} commented on {file}" : "{autor} comentó en {ficheru}",
|
||||
"<strong>Comments</strong> for files" : "<strong>Comentarios</strong> pa ficheros",
|
||||
"%1$s mentioned you in a comment on “%2$s”" : "%1$s mentóte nun comentariu de «%2$s»",
|
||||
"{user} mentioned you in a comment on “{file}”" : "{user} mentóte nun comentariu de «{file}»",
|
||||
"Unknown user" : "Usuariu desconocíu",
|
||||
"A (now) deleted user mentioned you in a comment on “%s”" : "Un usuariu (agora) desaniciáu mentóte nun comentariu de «%s»",
|
||||
"A (now) deleted user mentioned you in a comment on “{file}”" : "Un usuariu (agora) desaniciáu mentóte nun comentariu de «{file}»"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -2,6 +2,20 @@ OC.L10N.register(
|
||||
"comments",
|
||||
{
|
||||
"Comments" : "Коментари",
|
||||
"New comment …" : "Нов коментар...",
|
||||
"Delete comment" : "Изтрий коментар",
|
||||
"Post" : "Публикация",
|
||||
"Cancel" : "Отказ",
|
||||
"Edit comment" : "Редактирай коментра",
|
||||
"[Deleted user]" : "[Изтрит потребител]",
|
||||
"No comments yet, start the conversation!" : "Все-още няма коментари, започни разговор!",
|
||||
"More comments …" : "Още коментари...",
|
||||
"Save" : "Запазване",
|
||||
"Allowed characters {count} of {max}" : "Позволени символи {count} от {max}",
|
||||
"Error occurred while updating comment with id {id}" : "Възникна грешка по време на обновяване на коментар с код {id}",
|
||||
"Error occurred while posting comment" : "Възникна грешка по време на изпращане на коментар",
|
||||
"_%n unread comment_::_%n unread comments_" : ["%n непрочетен коментар","%n непрочетени коментари"],
|
||||
"Comment" : "Коментар",
|
||||
"You commented" : "Вие коментирахте",
|
||||
"%1$s commented" : "%1$s коментира",
|
||||
"{author} commented" : "{author} коментира",
|
||||
@@ -10,23 +24,12 @@ OC.L10N.register(
|
||||
"%1$s commented on %2$s" : "%1$s коментиран за %2$s",
|
||||
"{author} commented on {file}" : "{author} коментира за {file}",
|
||||
"<strong>Comments</strong> for files" : "<strong>Коментари</strong> за файлове",
|
||||
"You were mentioned on “{file}”, in a comment by a user that has since been deleted" : "Бяхте споменат/а към “{file}”, в коментар от потребител, който вече е изтрит",
|
||||
"{user} mentioned you in a comment on “{file}”" : "{user} те спомена в коментар за “{file}”",
|
||||
"Edit comment" : "Редактирай коментра",
|
||||
"Delete comment" : "Изтрий коментар",
|
||||
"New comment …" : "Нов коментар...",
|
||||
"Post" : "Публикация",
|
||||
"Cancel" : "Отказ",
|
||||
"[Deleted user]" : "[Изтрит потребител]",
|
||||
"No comments yet, start the conversation!" : "Все още няма коментари, започнете разговор!",
|
||||
"More comments …" : "Още коментари...",
|
||||
"Save" : "Запазване",
|
||||
"Allowed characters {count} of {max}" : "Позволени символи {count} от {max}",
|
||||
"Error occurred while updating comment with id {id}" : "Възникна грешка по време на обновяване на коментар с код {id}",
|
||||
"Error occurred while posting comment" : "Възникна грешка по време на изпращане на коментар",
|
||||
"_%n unread comment_::_%n unread comments_" : ["%n непрочетен коментар","%n непрочетени коментари"],
|
||||
"Comment" : "Коментар",
|
||||
"You were mentioned on “%s”, in a comment by a user that has since been deleted" : "Бяхте споменат/а на “%s”, в коментар от потребител, който вече е изтрит",
|
||||
"%1$s mentioned you in a comment on “%2$s”" : "%1$s те спомена в коментар за “%2$s”"
|
||||
"You were mentioned on “{file}”, in a comment by a user that has since been deleted" : "Бяхте споменат/а към “{file}”, в коментар от потребител, който вече е изтрит",
|
||||
"%1$s mentioned you in a comment on “%2$s”" : "%1$s те спомена в коментар за “%2$s”",
|
||||
"{user} mentioned you in a comment on “{file}”" : "{user} те спомена в коментар за “{file}”",
|
||||
"Unknown user" : "Непознат потребител",
|
||||
"A (now) deleted user mentioned you in a comment on “%s”" : "(Токущо) изтрит потребител те коментира в “%s”",
|
||||
"A (now) deleted user mentioned you in a comment on “{file}”" : "(Токущо) изтрит потребител те спомена в коментар за “{file}”"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
{ "translations": {
|
||||
"Comments" : "Коментари",
|
||||
"New comment …" : "Нов коментар...",
|
||||
"Delete comment" : "Изтрий коментар",
|
||||
"Post" : "Публикация",
|
||||
"Cancel" : "Отказ",
|
||||
"Edit comment" : "Редактирай коментра",
|
||||
"[Deleted user]" : "[Изтрит потребител]",
|
||||
"No comments yet, start the conversation!" : "Все-още няма коментари, започни разговор!",
|
||||
"More comments …" : "Още коментари...",
|
||||
"Save" : "Запазване",
|
||||
"Allowed characters {count} of {max}" : "Позволени символи {count} от {max}",
|
||||
"Error occurred while updating comment with id {id}" : "Възникна грешка по време на обновяване на коментар с код {id}",
|
||||
"Error occurred while posting comment" : "Възникна грешка по време на изпращане на коментар",
|
||||
"_%n unread comment_::_%n unread comments_" : ["%n непрочетен коментар","%n непрочетени коментари"],
|
||||
"Comment" : "Коментар",
|
||||
"You commented" : "Вие коментирахте",
|
||||
"%1$s commented" : "%1$s коментира",
|
||||
"{author} commented" : "{author} коментира",
|
||||
@@ -8,23 +22,12 @@
|
||||
"%1$s commented on %2$s" : "%1$s коментиран за %2$s",
|
||||
"{author} commented on {file}" : "{author} коментира за {file}",
|
||||
"<strong>Comments</strong> for files" : "<strong>Коментари</strong> за файлове",
|
||||
"You were mentioned on “{file}”, in a comment by a user that has since been deleted" : "Бяхте споменат/а към “{file}”, в коментар от потребител, който вече е изтрит",
|
||||
"{user} mentioned you in a comment on “{file}”" : "{user} те спомена в коментар за “{file}”",
|
||||
"Edit comment" : "Редактирай коментра",
|
||||
"Delete comment" : "Изтрий коментар",
|
||||
"New comment …" : "Нов коментар...",
|
||||
"Post" : "Публикация",
|
||||
"Cancel" : "Отказ",
|
||||
"[Deleted user]" : "[Изтрит потребител]",
|
||||
"No comments yet, start the conversation!" : "Все още няма коментари, започнете разговор!",
|
||||
"More comments …" : "Още коментари...",
|
||||
"Save" : "Запазване",
|
||||
"Allowed characters {count} of {max}" : "Позволени символи {count} от {max}",
|
||||
"Error occurred while updating comment with id {id}" : "Възникна грешка по време на обновяване на коментар с код {id}",
|
||||
"Error occurred while posting comment" : "Възникна грешка по време на изпращане на коментар",
|
||||
"_%n unread comment_::_%n unread comments_" : ["%n непрочетен коментар","%n непрочетени коментари"],
|
||||
"Comment" : "Коментар",
|
||||
"You were mentioned on “%s”, in a comment by a user that has since been deleted" : "Бяхте споменат/а на “%s”, в коментар от потребител, който вече е изтрит",
|
||||
"%1$s mentioned you in a comment on “%2$s”" : "%1$s те спомена в коментар за “%2$s”"
|
||||
"You were mentioned on “{file}”, in a comment by a user that has since been deleted" : "Бяхте споменат/а към “{file}”, в коментар от потребител, който вече е изтрит",
|
||||
"%1$s mentioned you in a comment on “%2$s”" : "%1$s те спомена в коментар за “%2$s”",
|
||||
"{user} mentioned you in a comment on “{file}”" : "{user} те спомена в коментар за “{file}”",
|
||||
"Unknown user" : "Непознат потребител",
|
||||
"A (now) deleted user mentioned you in a comment on “%s”" : "(Токущо) изтрит потребител те коментира в “%s”",
|
||||
"A (now) deleted user mentioned you in a comment on “{file}”" : "(Токущо) изтрит потребител те спомена в коментар за “{file}”"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -2,6 +2,20 @@ OC.L10N.register(
|
||||
"comments",
|
||||
{
|
||||
"Comments" : "Comentaris",
|
||||
"New comment …" : "Nou comentari...",
|
||||
"Delete comment" : "Esborrar comentari",
|
||||
"Post" : "Publica",
|
||||
"Cancel" : "Cancel·la",
|
||||
"Edit comment" : "Editar comentari",
|
||||
"[Deleted user]" : "[usuari Esborrat]",
|
||||
"No comments yet, start the conversation!" : "Encara no hi ha comentaris. Comenceu la conversa!",
|
||||
"More comments …" : "Més comentaris...",
|
||||
"Save" : "Desa",
|
||||
"Allowed characters {count} of {max}" : "{count} caràcters permesos de {max}",
|
||||
"Error occurred while updating comment with id {id}" : "Hi ha hagut un error en actualitzar el comentari amb id {id}",
|
||||
"Error occurred while posting comment" : "Hi ha hagut un error en publicar el comentari",
|
||||
"_%n unread comment_::_%n unread comments_" : ["%n comentari no llegit","%n comentaris no llegits"],
|
||||
"Comment" : "Comentari",
|
||||
"You commented" : "Heu comentat",
|
||||
"%1$s commented" : "%1$s ha comentat",
|
||||
"{author} commented" : "{author} ha comentat",
|
||||
@@ -9,27 +23,13 @@ OC.L10N.register(
|
||||
"You commented on {file}" : "Heu comentat a {file}",
|
||||
"%1$s commented on %2$s" : "%1$s ha comentat a %2$s",
|
||||
"{author} commented on {file}" : "{author} ha comentat a {file}",
|
||||
"<strong>Comments</strong> for files" : "<strong>Comentaris</strong> per fitxers",
|
||||
"You were mentioned on “{file}”, in a comment by a user that has since been deleted" : "Heu estat esmentats a \"{file}\" en un comentari d'un usuari que ja no existeix",
|
||||
"{user} mentioned you in a comment on “{file}”" : "{user} us ha esmentat en un comentari de “{file}”",
|
||||
"Files app plugin to add comments to files" : "Connexió de l’aplicació de fitxers per afegir comentaris als fitxers",
|
||||
"Edit comment" : "Edita comentari",
|
||||
"Delete comment" : "Suprimeix comentari",
|
||||
"New comment …" : "Nou comentari...",
|
||||
"Post" : "Publica",
|
||||
"Cancel" : "Cancel·la",
|
||||
"[Deleted user]" : "[Usuari suprimit]",
|
||||
"No comments yet, start the conversation!" : "Encara no hi ha comentaris. Enceteu la conversa!",
|
||||
"More comments …" : "Més comentaris...",
|
||||
"Save" : "Desa",
|
||||
"Allowed characters {count} of {max}" : "{count} caràcters permesos de {max}",
|
||||
"Error occurred while retrieving comment with ID {id}" : "S'ha produït un error en recuperar el comentari amb l'ID {id}",
|
||||
"Error occurred while updating comment with id {id}" : "Hi ha hagut un error en actualitzar el comentari amb id {id}",
|
||||
"Error occurred while posting comment" : "Hi ha hagut un error en publicar el comentari",
|
||||
"_%n unread comment_::_%n unread comments_" : ["%n comentari no llegit","%n comentaris no llegits"],
|
||||
"_1 new comment_::_{unread} new comments_" : ["1 comentari nou","{unread} comentaris nous"],
|
||||
"Comment" : "Comentari",
|
||||
"You were mentioned on “%s”, in a comment by a user that has since been deleted" : "Hau estat esmentats a \"%s\" en un comentari d'un usuari que ja no existeix",
|
||||
"%1$s mentioned you in a comment on “%2$s”" : "%1$s us ha esmentat en un comentari a “%2$s”"
|
||||
"<strong>Comments</strong> for files" : "<strong>Comentaris</strong> per arxius",
|
||||
"You were mentioned on “%s”, in a comment by a user that has since been deleted" : "Has estat mencionat a \"%s\" en un comentari d'un usuari que ja no existeix",
|
||||
"You were mentioned on “{file}”, in a comment by a user that has since been deleted" : "Has estat mencionat a \"{file}\" en un comentari d'un usuari que ja no existeix",
|
||||
"%1$s mentioned you in a comment on “%2$s”" : "%1$s us ha nomenat en un comentari a “%2$s”",
|
||||
"{user} mentioned you in a comment on “{file}”" : "{user} us ha nomenat en un comentari de “{file}”",
|
||||
"Unknown user" : "Usuari desconegut",
|
||||
"A (now) deleted user mentioned you in a comment on “%s”" : "Un usuari (ara) esborrat us ha nomenat en un comentari a “%s”",
|
||||
"A (now) deleted user mentioned you in a comment on “{file}”" : "Un usuari (ara) esborrat us ha nomenat en un comentari de “{file}”"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
{ "translations": {
|
||||
"Comments" : "Comentaris",
|
||||
"New comment …" : "Nou comentari...",
|
||||
"Delete comment" : "Esborrar comentari",
|
||||
"Post" : "Publica",
|
||||
"Cancel" : "Cancel·la",
|
||||
"Edit comment" : "Editar comentari",
|
||||
"[Deleted user]" : "[usuari Esborrat]",
|
||||
"No comments yet, start the conversation!" : "Encara no hi ha comentaris. Comenceu la conversa!",
|
||||
"More comments …" : "Més comentaris...",
|
||||
"Save" : "Desa",
|
||||
"Allowed characters {count} of {max}" : "{count} caràcters permesos de {max}",
|
||||
"Error occurred while updating comment with id {id}" : "Hi ha hagut un error en actualitzar el comentari amb id {id}",
|
||||
"Error occurred while posting comment" : "Hi ha hagut un error en publicar el comentari",
|
||||
"_%n unread comment_::_%n unread comments_" : ["%n comentari no llegit","%n comentaris no llegits"],
|
||||
"Comment" : "Comentari",
|
||||
"You commented" : "Heu comentat",
|
||||
"%1$s commented" : "%1$s ha comentat",
|
||||
"{author} commented" : "{author} ha comentat",
|
||||
@@ -7,27 +21,13 @@
|
||||
"You commented on {file}" : "Heu comentat a {file}",
|
||||
"%1$s commented on %2$s" : "%1$s ha comentat a %2$s",
|
||||
"{author} commented on {file}" : "{author} ha comentat a {file}",
|
||||
"<strong>Comments</strong> for files" : "<strong>Comentaris</strong> per fitxers",
|
||||
"You were mentioned on “{file}”, in a comment by a user that has since been deleted" : "Heu estat esmentats a \"{file}\" en un comentari d'un usuari que ja no existeix",
|
||||
"{user} mentioned you in a comment on “{file}”" : "{user} us ha esmentat en un comentari de “{file}”",
|
||||
"Files app plugin to add comments to files" : "Connexió de l’aplicació de fitxers per afegir comentaris als fitxers",
|
||||
"Edit comment" : "Edita comentari",
|
||||
"Delete comment" : "Suprimeix comentari",
|
||||
"New comment …" : "Nou comentari...",
|
||||
"Post" : "Publica",
|
||||
"Cancel" : "Cancel·la",
|
||||
"[Deleted user]" : "[Usuari suprimit]",
|
||||
"No comments yet, start the conversation!" : "Encara no hi ha comentaris. Enceteu la conversa!",
|
||||
"More comments …" : "Més comentaris...",
|
||||
"Save" : "Desa",
|
||||
"Allowed characters {count} of {max}" : "{count} caràcters permesos de {max}",
|
||||
"Error occurred while retrieving comment with ID {id}" : "S'ha produït un error en recuperar el comentari amb l'ID {id}",
|
||||
"Error occurred while updating comment with id {id}" : "Hi ha hagut un error en actualitzar el comentari amb id {id}",
|
||||
"Error occurred while posting comment" : "Hi ha hagut un error en publicar el comentari",
|
||||
"_%n unread comment_::_%n unread comments_" : ["%n comentari no llegit","%n comentaris no llegits"],
|
||||
"_1 new comment_::_{unread} new comments_" : ["1 comentari nou","{unread} comentaris nous"],
|
||||
"Comment" : "Comentari",
|
||||
"You were mentioned on “%s”, in a comment by a user that has since been deleted" : "Hau estat esmentats a \"%s\" en un comentari d'un usuari que ja no existeix",
|
||||
"%1$s mentioned you in a comment on “%2$s”" : "%1$s us ha esmentat en un comentari a “%2$s”"
|
||||
"<strong>Comments</strong> for files" : "<strong>Comentaris</strong> per arxius",
|
||||
"You were mentioned on “%s”, in a comment by a user that has since been deleted" : "Has estat mencionat a \"%s\" en un comentari d'un usuari que ja no existeix",
|
||||
"You were mentioned on “{file}”, in a comment by a user that has since been deleted" : "Has estat mencionat a \"{file}\" en un comentari d'un usuari que ja no existeix",
|
||||
"%1$s mentioned you in a comment on “%2$s”" : "%1$s us ha nomenat en un comentari a “%2$s”",
|
||||
"{user} mentioned you in a comment on “{file}”" : "{user} us ha nomenat en un comentari de “{file}”",
|
||||
"Unknown user" : "Usuari desconegut",
|
||||
"A (now) deleted user mentioned you in a comment on “%s”" : "Un usuari (ara) esborrat us ha nomenat en un comentari a “%s”",
|
||||
"A (now) deleted user mentioned you in a comment on “{file}”" : "Un usuari (ara) esborrat us ha nomenat en un comentari de “{file}”"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||