Compare commits
298 Commits
checkValid
...
v9.0.1RC2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6774f60240 | ||
|
|
9c800f62dd | ||
|
|
059df32542 | ||
|
|
43579e784f | ||
|
|
193a33a8ad | ||
|
|
b84746ce36 | ||
|
|
2cb45e71ea | ||
|
|
e3cc82df38 | ||
|
|
05fd4f2da0 | ||
|
|
d16553d2d8 | ||
|
|
8453073fdb | ||
|
|
b6fb3148c2 | ||
|
|
cfe0a6450e | ||
|
|
5f5e13351a | ||
|
|
ef8b75960c | ||
|
|
f28817aed5 | ||
|
|
cb300d164e | ||
|
|
e664e582fb | ||
|
|
9bd5fd23e7 | ||
|
|
835e24826d | ||
|
|
8d6aff69a8 | ||
|
|
7c7e079a36 | ||
|
|
1f7b037a59 | ||
|
|
99843c06f9 | ||
|
|
ed88f7b8b5 | ||
|
|
03f89d8242 | ||
|
|
f981661195 | ||
|
|
424c2b8263 | ||
|
|
2ab503f70a | ||
|
|
cdff098357 | ||
|
|
57596e1415 | ||
|
|
dd8d1c2b94 | ||
|
|
2660cf80c3 | ||
|
|
3cadc45ca5 | ||
|
|
8c9842fbd9 | ||
|
|
456035a750 | ||
|
|
fb77aa2e62 | ||
|
|
3b4999c835 | ||
|
|
52f4acf23d | ||
|
|
6b7c694a42 | ||
|
|
605abf9088 | ||
|
|
b5922e467a | ||
|
|
1733b6e8c8 | ||
|
|
c066882309 | ||
|
|
b456035aa7 | ||
|
|
03f461591e | ||
|
|
1f4738c372 | ||
|
|
af7c34d9a6 | ||
|
|
515ebc02b0 | ||
|
|
0bb7644150 | ||
|
|
c9eadb7b77 | ||
|
|
2139130ec8 | ||
|
|
195cf4111e | ||
|
|
443d72ee87 | ||
|
|
5a630c6a0e | ||
|
|
24670b4218 | ||
|
|
c864420d6c | ||
|
|
f77ce8829c | ||
|
|
70a5233f69 | ||
|
|
5ca10cf6f8 | ||
|
|
0009bf244b | ||
|
|
3d0e4bdc82 | ||
|
|
6edf403f3f | ||
|
|
43516ebef9 | ||
|
|
53f95373ba | ||
|
|
a9c13dc58a | ||
|
|
7fb32f8358 | ||
|
|
656c7f38b6 | ||
|
|
97dc57a143 | ||
|
|
3b98e8c56d | ||
|
|
1a100c69ba | ||
|
|
03fcbc8ceb | ||
|
|
49b5b1b08c | ||
|
|
2da2f26e56 | ||
|
|
d60f39e4f2 | ||
|
|
329e72a286 | ||
|
|
90a2be58f8 | ||
|
|
9b2fdc5358 | ||
|
|
0d9edd80f1 | ||
|
|
4fdb2254a4 | ||
|
|
b3ead7568d | ||
|
|
f8f292ab16 | ||
|
|
c7f1b82b4b | ||
|
|
aa75cfcf14 | ||
|
|
e9fc791e9f | ||
|
|
4d8c81a2d9 | ||
|
|
ad852ca24a | ||
|
|
50e94db4d5 | ||
|
|
ae2c207b6e | ||
|
|
f5fe2b4d08 | ||
|
|
77afc18b82 | ||
|
|
3a955ca43c | ||
|
|
204288f4a5 | ||
|
|
8af75c734a | ||
|
|
177ad39854 | ||
|
|
d59860aacd | ||
|
|
f6b6813f5f | ||
|
|
da95db78d0 | ||
|
|
3d19a179c7 | ||
|
|
3f4be066f9 | ||
|
|
419adc8d50 | ||
|
|
627724bc6a | ||
|
|
b3fef39006 | ||
|
|
3d9187e231 | ||
|
|
1a239b2f0d | ||
|
|
6afac05937 | ||
|
|
571de654cd | ||
|
|
546a0929a6 | ||
|
|
fefaa1fd87 | ||
|
|
ce0f68c7ce | ||
|
|
660c0da0a9 | ||
|
|
179ac2b98c | ||
|
|
d282affec2 | ||
|
|
8ef8be6be5 | ||
|
|
064e13511f | ||
|
|
a7dfc70e2e | ||
|
|
df056e1299 | ||
|
|
26deb0a897 | ||
|
|
3c77311397 | ||
|
|
fb705fa305 | ||
|
|
c513317c49 | ||
|
|
d9b632c001 | ||
|
|
1fdf790c8f | ||
|
|
d65c3a77e2 | ||
|
|
382b18e85e | ||
|
|
9e5c5c804b | ||
|
|
7d80b20368 | ||
|
|
1e1c0885c6 | ||
|
|
8e0b78c746 | ||
|
|
e7152f495c | ||
|
|
00ef623f1c | ||
|
|
9dfcb55a2f | ||
|
|
ec4c5a3e75 | ||
|
|
f46225fa2c | ||
|
|
f28f6a3001 | ||
|
|
25027c31ff | ||
|
|
b8af6e703a | ||
|
|
403eb87f4d | ||
|
|
ed4ed0e26f | ||
|
|
24dcdf9d5c | ||
|
|
3a773f4eca | ||
|
|
e71ebf334d | ||
|
|
08b9193919 | ||
|
|
dbcb037639 | ||
|
|
adbc5bbd1f | ||
|
|
4720719b08 | ||
|
|
7b818d9442 | ||
|
|
aa91d50d04 | ||
|
|
95cd1671b3 | ||
|
|
8c035ae6f6 | ||
|
|
d54106dada | ||
|
|
f9ad57ee52 | ||
|
|
cdadd4cd1b | ||
|
|
6f7fc6c5db | ||
|
|
9ef7340dc7 | ||
|
|
d1978a6950 | ||
|
|
dfc7e6b421 | ||
|
|
b580d26270 | ||
|
|
5466a800fa | ||
|
|
0f372a2a8c | ||
|
|
987146d5db | ||
|
|
9d688e655d | ||
|
|
f29440dbbc | ||
|
|
5a6b2956d8 | ||
|
|
99de93a6c6 | ||
|
|
4159187a6e | ||
|
|
6f64c99653 | ||
|
|
ce0f28c123 | ||
|
|
cca3a249fd | ||
|
|
0608455b01 | ||
|
|
3a5e90fa03 | ||
|
|
e2c557ab82 | ||
|
|
e1727477ac | ||
|
|
cf232c4182 | ||
|
|
efa673136e | ||
|
|
3380a3af74 | ||
|
|
80d0e43fc7 | ||
|
|
effc522572 | ||
|
|
689f3dea45 | ||
|
|
3a73f97706 | ||
|
|
a09ffb274b | ||
|
|
2609bf3500 | ||
|
|
9190885b4e | ||
|
|
f79195853e | ||
|
|
884a3b9bb4 | ||
|
|
a9ed80869a | ||
|
|
8e49a99896 | ||
|
|
5b593450c3 | ||
|
|
69cf557d0b | ||
|
|
5c38c1c845 | ||
|
|
dac69225d2 | ||
|
|
f984718921 | ||
|
|
b752a08d63 | ||
|
|
9bc99bb297 | ||
|
|
e3de44ea51 | ||
|
|
aeb480a8c3 | ||
|
|
b0c7eba7d0 | ||
|
|
e89f27e191 | ||
|
|
bd17cc793c | ||
|
|
62a36ee884 | ||
|
|
a58e374956 | ||
|
|
67c4759017 | ||
|
|
36b543c490 | ||
|
|
8465f9b6c6 | ||
|
|
281a0e9e03 | ||
|
|
7126b64088 | ||
|
|
38d137c34c | ||
|
|
098ce188b7 | ||
|
|
730620cfb2 | ||
|
|
dd7131512d | ||
|
|
85168315ab | ||
|
|
09febaad49 | ||
|
|
83894b58fe | ||
|
|
f374368568 | ||
|
|
4030b82c97 | ||
|
|
4e90bc017e | ||
|
|
b315a5ee29 | ||
|
|
b017434302 | ||
|
|
48cdf38d00 | ||
|
|
11f76b2918 | ||
|
|
316f86f2a3 | ||
|
|
bd144efeb1 | ||
|
|
88b9db44a0 | ||
|
|
91c7d293ca | ||
|
|
313b881d2b | ||
|
|
9120448302 | ||
|
|
d4d4b8d51a | ||
|
|
b955ce6141 | ||
|
|
e2b43c4095 | ||
|
|
d84cccb911 | ||
|
|
c2a16e0584 | ||
|
|
d652c87f86 | ||
|
|
72d4e672b2 | ||
|
|
95201a829f | ||
|
|
734b3e1408 | ||
|
|
ddf56b84d4 | ||
|
|
1975ffa9e7 | ||
|
|
81f694d83e | ||
|
|
8d6a6667e5 | ||
|
|
f9a80a9c12 | ||
|
|
d63f5561fb | ||
|
|
213dfa2b85 | ||
|
|
859303164e | ||
|
|
795331212a | ||
|
|
c9476115b0 | ||
|
|
cfd9c7f6b9 | ||
|
|
ebfc8b67d7 | ||
|
|
f9e1d4d56e | ||
|
|
fab42c7cd2 | ||
|
|
9232a124e2 | ||
|
|
b5a06ecd5c | ||
|
|
79811b5806 | ||
|
|
6413fffdcb | ||
|
|
b3b57621b7 | ||
|
|
6bfeb4595d | ||
|
|
5af8ebe3bd | ||
|
|
27f3dcc682 | ||
|
|
b1a1a46193 | ||
|
|
71b3033b35 | ||
|
|
c1876ea51c | ||
|
|
9ec89b99b1 | ||
|
|
0945cb7a0e | ||
|
|
6f4712a314 | ||
|
|
d043b6ba91 | ||
|
|
ef66729980 | ||
|
|
17f5f19187 | ||
|
|
71e3f7f866 | ||
|
|
0a5f34ab80 | ||
|
|
5488bb74fe | ||
|
|
4b85660984 | ||
|
|
5080c34d78 | ||
|
|
5d402fc817 | ||
|
|
fb62043cc1 | ||
|
|
b3c9ed8d5c | ||
|
|
9737290e39 | ||
|
|
121ff350d4 | ||
|
|
bb0c304482 | ||
|
|
c9c85b8d4a | ||
|
|
eb59aa8be4 | ||
|
|
96d45e90dc | ||
|
|
0655f25406 | ||
|
|
434747f450 | ||
|
|
7ff2b9232b | ||
|
|
3d28f364c5 | ||
|
|
62399f7852 | ||
|
|
4fc6deaaf0 | ||
|
|
e6c6ee8d2a | ||
|
|
3673cfae3c | ||
|
|
4da858b3b7 | ||
|
|
8e8f5cdddf | ||
|
|
f603c57751 | ||
|
|
e155f28f5e | ||
|
|
dd556d77da | ||
|
|
750fc9ae26 | ||
|
|
4186bcbdf2 | ||
|
|
98f79173ed | ||
|
|
8d07cb4d85 | ||
|
|
445957a0e2 |
18
.htaccess
18
.htaccess
@@ -37,6 +37,17 @@
|
||||
SetEnv htaccessWorking true
|
||||
</IfModule>
|
||||
</IfModule>
|
||||
<IfModule mod_php7.c>
|
||||
php_value upload_max_filesize 513M
|
||||
php_value post_max_size 513M
|
||||
php_value memory_limit 512M
|
||||
php_value mbstring.func_overload 0
|
||||
php_value default_charset 'UTF-8'
|
||||
php_value output_buffering 0
|
||||
<IfModule mod_env.c>
|
||||
SetEnv htaccessWorking true
|
||||
</IfModule>
|
||||
</IfModule>
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
@@ -51,9 +62,9 @@
|
||||
|
||||
# Rewrite rules for `front_controller_active`
|
||||
Options -MultiViews
|
||||
RewriteRule ^core/js/oc.js$ index.php/core/js/oc.js [PT,E=PATH_INFO:$1]
|
||||
RewriteRule ^core/preview.png$ index.php/core/preview.png [PT,E=PATH_INFO:$1]
|
||||
RewriteCond %{REQUEST_FILENAME} !\.(css|js|svg|gif|png|html|ttf|woff|ico)$
|
||||
RewriteRule ^core/js/oc.js$ index.php [PT,E=PATH_INFO:$1]
|
||||
RewriteRule ^core/preview.png$ index.php [PT,E=PATH_INFO:$1]
|
||||
RewriteCond %{REQUEST_FILENAME} !\.(css|js|svg|gif|png|html|ttf|woff|ico|jpg|jpeg)$
|
||||
RewriteCond %{REQUEST_FILENAME} !core/img/favicon.ico$
|
||||
RewriteCond %{REQUEST_FILENAME} !/remote.php
|
||||
RewriteCond %{REQUEST_FILENAME} !/public.php
|
||||
@@ -65,7 +76,6 @@
|
||||
RewriteCond %{REQUEST_FILENAME} !/updater/
|
||||
RewriteCond %{REQUEST_FILENAME} !/ocs-provider/
|
||||
RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge/.*
|
||||
RewriteRule .* index.php [PT,E=PATH_INFO:$1]
|
||||
</IfModule>
|
||||
<IfModule mod_mime.c>
|
||||
AddType image/svg+xml svg svgz
|
||||
|
||||
11
.travis.yml
11
.travis.yml
@@ -13,7 +13,7 @@ env:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
# - /^stable\d+(\.\d+)?$/
|
||||
- /^stable\d+(\.\d+)?$/
|
||||
|
||||
addons:
|
||||
apt:
|
||||
@@ -29,6 +29,7 @@ install:
|
||||
|
||||
|
||||
script:
|
||||
- sh -c "if [ '$TC' = 'syntax' ]; then composer install && vendor/bin/parallel-lint --exclude vendor/jakub-onderka/ --exclude 3rdparty/symfony/polyfill-php70/Resources/stubs/ --exclude 3rdparty/patchwork/utf8/src/Patchwork/Utf8/Bootup/ --exclude 3rdparty/paragonie/random_compat/lib/ .; fi"
|
||||
- sh -c "if [ '$TEST_DAV' != '1' ]; then echo \"Not testing DAV\"; fi"
|
||||
- sh -c "if [ '$TEST_DAV' = '1' ]; then echo \"Testing DAV\"; fi"
|
||||
|
||||
@@ -42,5 +43,13 @@ matrix:
|
||||
env: DB=sqlite;TC=carddav
|
||||
- php: 5.4
|
||||
env: DB=sqlite;TC=caldav
|
||||
- php: 5.4
|
||||
env: DB=sqlite;TC=syntax;TEST_DAV=0
|
||||
- php: 5.5
|
||||
env: DB=sqlite;TC=syntax;TEST_DAV=0
|
||||
- php: 5.6
|
||||
env: DB=sqlite;TC=syntax;TEST_DAV=0
|
||||
- php: 7.0
|
||||
env: DB=sqlite;TC=syntax;TEST_DAV=0
|
||||
|
||||
fast_finish: true
|
||||
|
||||
2
3rdparty
2
3rdparty
Submodule 3rdparty updated: fc0c1159f4...afb726b309
@@ -105,7 +105,7 @@ class Extension implements IExtension {
|
||||
public function getTypeIcon($type) {
|
||||
switch ($type) {
|
||||
case self::APP_NAME:
|
||||
return false;
|
||||
return 'icon-comment';
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -150,6 +150,9 @@ class Extension implements IExtension {
|
||||
|
||||
switch ($text) {
|
||||
case self::ADD_COMMENT_SUBJECT:
|
||||
if ($this->authorIsCurrentUser($params[0])) {
|
||||
return (string) $l->t('You commented');
|
||||
}
|
||||
return (string) $l->t('%1$s commented', $params);
|
||||
case self::ADD_COMMENT_MESSAGE:
|
||||
return $this->convertParameterToComment($params[0], 120);
|
||||
@@ -168,6 +171,9 @@ class Extension implements IExtension {
|
||||
|
||||
switch ($text) {
|
||||
case self::ADD_COMMENT_SUBJECT:
|
||||
if ($this->authorIsCurrentUser($params[0])) {
|
||||
return (string) $l->t('You commented on %2$s', $params);
|
||||
}
|
||||
return (string) $l->t('%1$s commented on %2$s', $params);
|
||||
case self::ADD_COMMENT_MESSAGE:
|
||||
return $this->convertParameterToComment($params[0]);
|
||||
@@ -176,6 +182,21 @@ class Extension implements IExtension {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the author is the current user
|
||||
*
|
||||
* @param string $user Parameter e.g. `<user display-name="admin">admin</user>`
|
||||
* @return bool
|
||||
*/
|
||||
protected function authorIsCurrentUser($user) {
|
||||
try {
|
||||
return strip_tags($user) === $this->activityManager->getCurrentUserId();
|
||||
} catch (\UnexpectedValueException $e) {
|
||||
// FIXME this is awkward, but we have no access to the current user in emails
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The extension can define the type of parameters for translation
|
||||
*
|
||||
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Uložit",
|
||||
"Allowed characters {count} of {max}" : "Povolených znaků {count} z {max}",
|
||||
"{count} unread comments" : "{count} nepřečtených komentářů",
|
||||
"Comment" : "Komentář"
|
||||
"Comment" : "Komentář",
|
||||
"<strong>Comments</strong> for files" : "<strong>Komentáře</strong> souborů"
|
||||
},
|
||||
"nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Uložit",
|
||||
"Allowed characters {count} of {max}" : "Povolených znaků {count} z {max}",
|
||||
"{count} unread comments" : "{count} nepřečtených komentářů",
|
||||
"Comment" : "Komentář"
|
||||
"Comment" : "Komentář",
|
||||
"<strong>Comments</strong> for files" : "<strong>Komentáře</strong> souborů"
|
||||
},"pluralForm" :"nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Speichern",
|
||||
"Allowed characters {count} of {max}" : "Erlaubte Zeichen {count} von {max}",
|
||||
"{count} unread comments" : "{count} ungelesene Kommentare",
|
||||
"Comment" : "Kommentar"
|
||||
"Comment" : "Kommentar",
|
||||
"<strong>Comments</strong> for files" : "<strong>Kommentare</strong> für Dateien"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Speichern",
|
||||
"Allowed characters {count} of {max}" : "Erlaubte Zeichen {count} von {max}",
|
||||
"{count} unread comments" : "{count} ungelesene Kommentare",
|
||||
"Comment" : "Kommentar"
|
||||
"Comment" : "Kommentar",
|
||||
"<strong>Comments</strong> for files" : "<strong>Kommentare</strong> für Dateien"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Speichern",
|
||||
"Allowed characters {count} of {max}" : "{count} von {max} Zeichen benutzt",
|
||||
"{count} unread comments" : "[count] ungelesene Kommentare",
|
||||
"Comment" : "Kommentar"
|
||||
"Comment" : "Kommentar",
|
||||
"<strong>Comments</strong> for files" : "<strong>Kommentare</strong> für Dateien"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Speichern",
|
||||
"Allowed characters {count} of {max}" : "{count} von {max} Zeichen benutzt",
|
||||
"{count} unread comments" : "[count] ungelesene Kommentare",
|
||||
"Comment" : "Kommentar"
|
||||
"Comment" : "Kommentar",
|
||||
"<strong>Comments</strong> for files" : "<strong>Kommentare</strong> für Dateien"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Save",
|
||||
"Allowed characters {count} of {max}" : "Allowed characters {count} of {max}",
|
||||
"{count} unread comments" : "{count} unread comments",
|
||||
"Comment" : "Comment"
|
||||
"Comment" : "Comment",
|
||||
"<strong>Comments</strong> for files" : "<strong>Comments</strong> for files"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Save",
|
||||
"Allowed characters {count} of {max}" : "Allowed characters {count} of {max}",
|
||||
"{count} unread comments" : "{count} unread comments",
|
||||
"Comment" : "Comment"
|
||||
"Comment" : "Comment",
|
||||
"<strong>Comments</strong> for files" : "<strong>Comments</strong> for files"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Konservi",
|
||||
"Allowed characters {count} of {max}" : "Permesataj karakteroj: {count} el {max}",
|
||||
"{count} unread comments" : "{count} nelegitaj komentoj",
|
||||
"Comment" : "Komento"
|
||||
"Comment" : "Komento",
|
||||
"<strong>Comments</strong> for files" : "<strong>Komentoj</strong> por dosieroj"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Konservi",
|
||||
"Allowed characters {count} of {max}" : "Permesataj karakteroj: {count} el {max}",
|
||||
"{count} unread comments" : "{count} nelegitaj komentoj",
|
||||
"Comment" : "Komento"
|
||||
"Comment" : "Komento",
|
||||
"<strong>Comments</strong> for files" : "<strong>Komentoj</strong> por dosieroj"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Guardar",
|
||||
"Allowed characters {count} of {max}" : "Caracteres permitidos {count} de {max}",
|
||||
"{count} unread comments" : "{count} comentarios no leídos",
|
||||
"Comment" : "Comentario"
|
||||
"Comment" : "Comentario",
|
||||
"<strong>Comments</strong> for files" : "<strong>Comentarios</strong> en archivos"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Guardar",
|
||||
"Allowed characters {count} of {max}" : "Caracteres permitidos {count} de {max}",
|
||||
"{count} unread comments" : "{count} comentarios no leídos",
|
||||
"Comment" : "Comentario"
|
||||
"Comment" : "Comentario",
|
||||
"<strong>Comments</strong> for files" : "<strong>Comentarios</strong> en archivos"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Tallenna",
|
||||
"Allowed characters {count} of {max}" : "Sallittujen merkkien määrä {count}/{max}",
|
||||
"{count} unread comments" : "{count} lukematonta kommenttia",
|
||||
"Comment" : "Kommentti"
|
||||
"Comment" : "Kommentti",
|
||||
"<strong>Comments</strong> for files" : "<strong>Kommentit</strong> tiedostoille"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Tallenna",
|
||||
"Allowed characters {count} of {max}" : "Sallittujen merkkien määrä {count}/{max}",
|
||||
"{count} unread comments" : "{count} lukematonta kommenttia",
|
||||
"Comment" : "Kommentti"
|
||||
"Comment" : "Kommentti",
|
||||
"<strong>Comments</strong> for files" : "<strong>Kommentit</strong> tiedostoille"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Enregistrer",
|
||||
"Allowed characters {count} of {max}" : "{count} sur {max} caractères autorisés",
|
||||
"{count} unread comments" : "{count} commentaires non lus",
|
||||
"Comment" : "Commenter"
|
||||
"Comment" : "Commenter",
|
||||
"<strong>Comments</strong> for files" : "<strong>Commentaires</strong> pour les fichiers"
|
||||
},
|
||||
"nplurals=2; plural=(n > 1);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Enregistrer",
|
||||
"Allowed characters {count} of {max}" : "{count} sur {max} caractères autorisés",
|
||||
"{count} unread comments" : "{count} commentaires non lus",
|
||||
"Comment" : "Commenter"
|
||||
"Comment" : "Commenter",
|
||||
"<strong>Comments</strong> for files" : "<strong>Commentaires</strong> pour les fichiers"
|
||||
},"pluralForm" :"nplurals=2; plural=(n > 1);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "שמירה",
|
||||
"Allowed characters {count} of {max}" : "תווים מותרים {count} מתוך {max}",
|
||||
"{count} unread comments" : "{count} תגובות שלא נקראו",
|
||||
"Comment" : "תגובה"
|
||||
"Comment" : "תגובה",
|
||||
"<strong>Comments</strong> for files" : "<strong>תגובות</strong> לקבצים"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "שמירה",
|
||||
"Allowed characters {count} of {max}" : "תווים מותרים {count} מתוך {max}",
|
||||
"{count} unread comments" : "{count} תגובות שלא נקראו",
|
||||
"Comment" : "תגובה"
|
||||
"Comment" : "תגובה",
|
||||
"<strong>Comments</strong> for files" : "<strong>תגובות</strong> לקבצים"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -3,6 +3,11 @@ OC.L10N.register(
|
||||
{
|
||||
"Cancel" : "Mégsem",
|
||||
"Save" : "Mentés",
|
||||
"Comment" : "Komment"
|
||||
"Comment" : "Komment",
|
||||
"<strong>Comments</strong> for files" : "<strong>Hozzászólások</strong> a fájlokhoz",
|
||||
"Delete comment" : "Hozzászólás törlése",
|
||||
"Edit comment" : "Hozzászólás szerkesztése",
|
||||
"No other comments available" : "Nincs több hozzászólás.",
|
||||
"Post" : "Küldés"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
{ "translations": {
|
||||
"Cancel" : "Mégsem",
|
||||
"Save" : "Mentés",
|
||||
"Comment" : "Komment"
|
||||
"Comment" : "Komment",
|
||||
"<strong>Comments</strong> for files" : "<strong>Hozzászólások</strong> a fájlokhoz",
|
||||
"Delete comment" : "Hozzászólás törlése",
|
||||
"Edit comment" : "Hozzászólás szerkesztése",
|
||||
"No other comments available" : "Nincs több hozzászólás.",
|
||||
"Post" : "Küldés"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Simpan",
|
||||
"Allowed characters {count} of {max}" : "Karakter yang diizinkan {count} dari {max}",
|
||||
"{count} unread comments" : "{count} komentar belum dibaca",
|
||||
"Comment" : "Komentar"
|
||||
"Comment" : "Komentar",
|
||||
"<strong>Comments</strong> for files" : "<strong>Komentar</strong> untuk berkas"
|
||||
},
|
||||
"nplurals=1; plural=0;");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Simpan",
|
||||
"Allowed characters {count} of {max}" : "Karakter yang diizinkan {count} dari {max}",
|
||||
"{count} unread comments" : "{count} komentar belum dibaca",
|
||||
"Comment" : "Komentar"
|
||||
"Comment" : "Komentar",
|
||||
"<strong>Comments</strong> for files" : "<strong>Komentar</strong> untuk berkas"
|
||||
},"pluralForm" :"nplurals=1; plural=0;"
|
||||
}
|
||||
@@ -3,6 +3,11 @@ OC.L10N.register(
|
||||
{
|
||||
"Cancel" : "Hætta við",
|
||||
"Save" : "Vista",
|
||||
"Comment" : "Athugasemd"
|
||||
"Comment" : "Athugasemd",
|
||||
"<strong>Comments</strong> for files" : "<strong>Athugasemdir</strong> við skrár",
|
||||
"Delete comment" : "Eyða athugasemd",
|
||||
"Edit comment" : "Breyta athugasemd",
|
||||
"No other comments available" : "Engar aðrar athugasemdir eru tiltækar",
|
||||
"Post" : "Senda"
|
||||
},
|
||||
"nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);");
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
{ "translations": {
|
||||
"Cancel" : "Hætta við",
|
||||
"Save" : "Vista",
|
||||
"Comment" : "Athugasemd"
|
||||
"Comment" : "Athugasemd",
|
||||
"<strong>Comments</strong> for files" : "<strong>Athugasemdir</strong> við skrár",
|
||||
"Delete comment" : "Eyða athugasemd",
|
||||
"Edit comment" : "Breyta athugasemd",
|
||||
"No other comments available" : "Engar aðrar athugasemdir eru tiltækar",
|
||||
"Post" : "Senda"
|
||||
},"pluralForm" :"nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Salva",
|
||||
"Allowed characters {count} of {max}" : "Caratteri consentiti {count} di {max}",
|
||||
"{count} unread comments" : "{count} commenti non letti",
|
||||
"Comment" : "Commento"
|
||||
"Comment" : "Commento",
|
||||
"<strong>Comments</strong> for files" : "<strong>Commenti</strong> sui file"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Salva",
|
||||
"Allowed characters {count} of {max}" : "Caratteri consentiti {count} di {max}",
|
||||
"{count} unread comments" : "{count} commenti non letti",
|
||||
"Comment" : "Commento"
|
||||
"Comment" : "Commento",
|
||||
"<strong>Comments</strong> for files" : "<strong>Commenti</strong> sui file"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "保存",
|
||||
"Allowed characters {count} of {max}" : "入力文字数 {count} / {max}",
|
||||
"{count} unread comments" : "未読コメント数 {count}",
|
||||
"Comment" : "コメント"
|
||||
"Comment" : "コメント",
|
||||
"<strong>Comments</strong> for files" : "ファイルについての<strong>コメント</strong>"
|
||||
},
|
||||
"nplurals=1; plural=0;");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "保存",
|
||||
"Allowed characters {count} of {max}" : "入力文字数 {count} / {max}",
|
||||
"{count} unread comments" : "未読コメント数 {count}",
|
||||
"Comment" : "コメント"
|
||||
"Comment" : "コメント",
|
||||
"<strong>Comments</strong> for files" : "ファイルについての<strong>コメント</strong>"
|
||||
},"pluralForm" :"nplurals=1; plural=0;"
|
||||
}
|
||||
@@ -3,6 +3,11 @@ OC.L10N.register(
|
||||
{
|
||||
"Cancel" : "취소",
|
||||
"Save" : "저장",
|
||||
"Comment" : "설명"
|
||||
"Comment" : "설명",
|
||||
"<strong>Comments</strong> for files" : "파일에 <strong>댓글</strong> 남기기",
|
||||
"Delete comment" : "댓글 삭제",
|
||||
"Edit comment" : "댓글 편집",
|
||||
"No other comments available" : "더 이상 댓글 없음",
|
||||
"Post" : "게시"
|
||||
},
|
||||
"nplurals=1; plural=0;");
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
{ "translations": {
|
||||
"Cancel" : "취소",
|
||||
"Save" : "저장",
|
||||
"Comment" : "설명"
|
||||
"Comment" : "설명",
|
||||
"<strong>Comments</strong> for files" : "파일에 <strong>댓글</strong> 남기기",
|
||||
"Delete comment" : "댓글 삭제",
|
||||
"Edit comment" : "댓글 편집",
|
||||
"No other comments available" : "더 이상 댓글 없음",
|
||||
"Post" : "게시"
|
||||
},"pluralForm" :"nplurals=1; plural=0;"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Lagre",
|
||||
"Allowed characters {count} of {max}" : "Antall tegn tillatt {count} av {max}",
|
||||
"{count} unread comments" : "{count} uleste kommentarer",
|
||||
"Comment" : "Kommentar"
|
||||
"Comment" : "Kommentar",
|
||||
"<strong>Comments</strong> for files" : "<strong>Kommentarer</strong> for filer"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Lagre",
|
||||
"Allowed characters {count} of {max}" : "Antall tegn tillatt {count} av {max}",
|
||||
"{count} unread comments" : "{count} uleste kommentarer",
|
||||
"Comment" : "Kommentar"
|
||||
"Comment" : "Kommentar",
|
||||
"<strong>Comments</strong> for files" : "<strong>Kommentarer</strong> for filer"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Bewaren",
|
||||
"Allowed characters {count} of {max}" : "{count} van de {max} toegestane tekens",
|
||||
"{count} unread comments" : "{count} ongelezen reacties",
|
||||
"Comment" : "Reactie"
|
||||
"Comment" : "Reactie",
|
||||
"<strong>Comments</strong> for files" : "<strong>Reacties</strong> voor bestanden"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Bewaren",
|
||||
"Allowed characters {count} of {max}" : "{count} van de {max} toegestane tekens",
|
||||
"{count} unread comments" : "{count} ongelezen reacties",
|
||||
"Comment" : "Reactie"
|
||||
"Comment" : "Reactie",
|
||||
"<strong>Comments</strong> for files" : "<strong>Reacties</strong> voor bestanden"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -3,6 +3,11 @@ OC.L10N.register(
|
||||
{
|
||||
"Cancel" : "Anuluj",
|
||||
"Save" : "Zapisz",
|
||||
"Comment" : "Komentarz"
|
||||
"Comment" : "Komentarz",
|
||||
"<strong>Comments</strong> for files" : "<strong>Komentarze</strong> dla plików",
|
||||
"Delete comment" : "Skasuj komentarz",
|
||||
"Edit comment" : "Edytuj komentarz",
|
||||
"No other comments available" : "Nie ma więcej komentarzy",
|
||||
"Post" : "Zapisz"
|
||||
},
|
||||
"nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);");
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
{ "translations": {
|
||||
"Cancel" : "Anuluj",
|
||||
"Save" : "Zapisz",
|
||||
"Comment" : "Komentarz"
|
||||
"Comment" : "Komentarz",
|
||||
"<strong>Comments</strong> for files" : "<strong>Komentarze</strong> dla plików",
|
||||
"Delete comment" : "Skasuj komentarz",
|
||||
"Edit comment" : "Edytuj komentarz",
|
||||
"No other comments available" : "Nie ma więcej komentarzy",
|
||||
"Post" : "Zapisz"
|
||||
},"pluralForm" :"nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Salvar",
|
||||
"Allowed characters {count} of {max}" : "Caracteres permitidos {count} de {max}",
|
||||
"{count} unread comments" : "{count} comentários não lidos",
|
||||
"Comment" : "Comentário"
|
||||
"Comment" : "Comentário",
|
||||
"<strong>Comments</strong> for files" : "<strong>Comentários</strong> por arquivos"
|
||||
},
|
||||
"nplurals=2; plural=(n > 1);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Salvar",
|
||||
"Allowed characters {count} of {max}" : "Caracteres permitidos {count} de {max}",
|
||||
"{count} unread comments" : "{count} comentários não lidos",
|
||||
"Comment" : "Comentário"
|
||||
"Comment" : "Comentário",
|
||||
"<strong>Comments</strong> for files" : "<strong>Comentários</strong> por arquivos"
|
||||
},"pluralForm" :"nplurals=2; plural=(n > 1);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Guardar",
|
||||
"Allowed characters {count} of {max}" : "{count} de {max} caracteres restantes",
|
||||
"{count} unread comments" : "{count} comentários não lidos",
|
||||
"Comment" : "Comentário"
|
||||
"Comment" : "Comentário",
|
||||
"<strong>Comments</strong> for files" : "<strong>Comentários</strong> para ficheiros"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Guardar",
|
||||
"Allowed characters {count} of {max}" : "{count} de {max} caracteres restantes",
|
||||
"{count} unread comments" : "{count} comentários não lidos",
|
||||
"Comment" : "Comentário"
|
||||
"Comment" : "Comentário",
|
||||
"<strong>Comments</strong> for files" : "<strong>Comentários</strong> para ficheiros"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Сохранить",
|
||||
"Allowed characters {count} of {max}" : "Допустимых символов {count} из {max}",
|
||||
"{count} unread comments" : "{count} непрочитанных комментариев",
|
||||
"Comment" : "Коментарий"
|
||||
"Comment" : "Коментарий",
|
||||
"<strong>Comments</strong> for files" : "<strong>Комментарии</strong> к файлам"
|
||||
},
|
||||
"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Сохранить",
|
||||
"Allowed characters {count} of {max}" : "Допустимых символов {count} из {max}",
|
||||
"{count} unread comments" : "{count} непрочитанных комментариев",
|
||||
"Comment" : "Коментарий"
|
||||
"Comment" : "Коментарий",
|
||||
"<strong>Comments</strong> for files" : "<strong>Комментарии</strong> к файлам"
|
||||
},"pluralForm" :"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Shrani",
|
||||
"Allowed characters {count} of {max}" : "Dovoljeni znaki: {count} od {max}",
|
||||
"{count} unread comments" : "{count} neprebranih opomb",
|
||||
"Comment" : "Opomba"
|
||||
"Comment" : "Opomba",
|
||||
"<strong>Comments</strong> for files" : "<strong>Opombe</strong> datotek"
|
||||
},
|
||||
"nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Shrani",
|
||||
"Allowed characters {count} of {max}" : "Dovoljeni znaki: {count} od {max}",
|
||||
"{count} unread comments" : "{count} neprebranih opomb",
|
||||
"Comment" : "Opomba"
|
||||
"Comment" : "Opomba",
|
||||
"<strong>Comments</strong> for files" : "<strong>Opombe</strong> datotek"
|
||||
},"pluralForm" :"nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Ruaje",
|
||||
"Allowed characters {count} of {max}" : "Shenja të lejuara {count} nga {max}",
|
||||
"{count} unread comments" : "{count} komente të palexuar",
|
||||
"Comment" : "Koment"
|
||||
"Comment" : "Koment",
|
||||
"<strong>Comments</strong> for files" : "<strong>Komente</strong> për kartela"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Ruaje",
|
||||
"Allowed characters {count} of {max}" : "Shenja të lejuara {count} nga {max}",
|
||||
"{count} unread comments" : "{count} komente të palexuar",
|
||||
"Comment" : "Koment"
|
||||
"Comment" : "Koment",
|
||||
"<strong>Comments</strong> for files" : "<strong>Komente</strong> për kartela"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -3,6 +3,11 @@ OC.L10N.register(
|
||||
{
|
||||
"Cancel" : "Одустани",
|
||||
"Save" : "Сачувај",
|
||||
"Comment" : "Коментар"
|
||||
"Comment" : "Коментар",
|
||||
"<strong>Comments</strong> for files" : "<strong>Коментари</strong> фајлова",
|
||||
"Delete comment" : "Обриши коментар",
|
||||
"Edit comment" : "Уреди коментар",
|
||||
"No other comments available" : "Нема других коментара",
|
||||
"Post" : "Објави"
|
||||
},
|
||||
"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);");
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
{ "translations": {
|
||||
"Cancel" : "Одустани",
|
||||
"Save" : "Сачувај",
|
||||
"Comment" : "Коментар"
|
||||
"Comment" : "Коментар",
|
||||
"<strong>Comments</strong> for files" : "<strong>Коментари</strong> фајлова",
|
||||
"Delete comment" : "Обриши коментар",
|
||||
"Edit comment" : "Уреди коментар",
|
||||
"No other comments available" : "Нема других коментара",
|
||||
"Post" : "Објави"
|
||||
},"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Spara",
|
||||
"Allowed characters {count} of {max}" : "Tillåtet antal tecken {count} av {max}",
|
||||
"{count} unread comments" : "{count} olästa kommentarer",
|
||||
"Comment" : "Kommentar"
|
||||
"Comment" : "Kommentar",
|
||||
"<strong>Comments</strong> for files" : "<strong>Kommentarer</strong> till filer"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Spara",
|
||||
"Allowed characters {count} of {max}" : "Tillåtet antal tecken {count} av {max}",
|
||||
"{count} unread comments" : "{count} olästa kommentarer",
|
||||
"Comment" : "Kommentar"
|
||||
"Comment" : "Kommentar",
|
||||
"<strong>Comments</strong> for files" : "<strong>Kommentarer</strong> till filer"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -12,6 +12,7 @@ OC.L10N.register(
|
||||
"Edit comment" : "แก้ไขความคิดเห็น",
|
||||
"[Deleted user]" : "[ผู้ใช้ถูกลบไปแล้ว]",
|
||||
"Save" : "บันทึก",
|
||||
"Comment" : "แสดงความคิดเห็น"
|
||||
"Comment" : "แสดงความคิดเห็น",
|
||||
"<strong>Comments</strong> for files" : "<strong>แสดงความคิดเห็น</strong> สำหรับไฟล์"
|
||||
},
|
||||
"nplurals=1; plural=0;");
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"Edit comment" : "แก้ไขความคิดเห็น",
|
||||
"[Deleted user]" : "[ผู้ใช้ถูกลบไปแล้ว]",
|
||||
"Save" : "บันทึก",
|
||||
"Comment" : "แสดงความคิดเห็น"
|
||||
"Comment" : "แสดงความคิดเห็น",
|
||||
"<strong>Comments</strong> for files" : "<strong>แสดงความคิดเห็น</strong> สำหรับไฟล์"
|
||||
},"pluralForm" :"nplurals=1; plural=0;"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Kaydet",
|
||||
"Allowed characters {count} of {max}" : "İzin verilen karakterler {count} {max}",
|
||||
"{count} unread comments" : "{count} okunmamış yorumlar",
|
||||
"Comment" : "Yorum"
|
||||
"Comment" : "Yorum",
|
||||
"<strong>Comments</strong> for files" : "Dosyalar için <strong>Yorumlar</strong>"
|
||||
},
|
||||
"nplurals=2; plural=(n > 1);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Kaydet",
|
||||
"Allowed characters {count} of {max}" : "İzin verilen karakterler {count} {max}",
|
||||
"{count} unread comments" : "{count} okunmamış yorumlar",
|
||||
"Comment" : "Yorum"
|
||||
"Comment" : "Yorum",
|
||||
"<strong>Comments</strong> for files" : "Dosyalar için <strong>Yorumlar</strong>"
|
||||
},"pluralForm" :"nplurals=2; plural=(n > 1);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "Зберегти",
|
||||
"Allowed characters {count} of {max}" : "Доступно символів {count} з {max}",
|
||||
"{count} unread comments" : "{count} непрочитаних коментарів",
|
||||
"Comment" : "Коментар"
|
||||
"Comment" : "Коментар",
|
||||
"<strong>Comments</strong> for files" : "<strong>Коментарі</strong> до файлів"
|
||||
},
|
||||
"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "Зберегти",
|
||||
"Allowed characters {count} of {max}" : "Доступно символів {count} з {max}",
|
||||
"{count} unread comments" : "{count} непрочитаних коментарів",
|
||||
"Comment" : "Коментар"
|
||||
"Comment" : "Коментар",
|
||||
"<strong>Comments</strong> for files" : "<strong>Коментарі</strong> до файлів"
|
||||
},"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
|
||||
}
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Save" : "保存",
|
||||
"Allowed characters {count} of {max}" : "当前字数: {count},最大允许:{max}",
|
||||
"{count} unread comments" : "{count} 条未读评论",
|
||||
"Comment" : "评论"
|
||||
"Comment" : "评论",
|
||||
"<strong>Comments</strong> for files" : "<strong>评论文件</strong>"
|
||||
},
|
||||
"nplurals=1; plural=0;");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Save" : "保存",
|
||||
"Allowed characters {count} of {max}" : "当前字数: {count},最大允许:{max}",
|
||||
"{count} unread comments" : "{count} 条未读评论",
|
||||
"Comment" : "评论"
|
||||
"Comment" : "评论",
|
||||
"<strong>Comments</strong> for files" : "<strong>评论文件</strong>"
|
||||
},"pluralForm" :"nplurals=1; plural=0;"
|
||||
}
|
||||
@@ -99,18 +99,24 @@ class Application extends App {
|
||||
$container->registerService('MigrateAddressbooks', function($c) {
|
||||
/** @var IAppContainer $c */
|
||||
$db = $c->getServer()->getDatabaseConnection();
|
||||
$logger = $c->getServer()->getLogger();
|
||||
return new MigrateAddressbooks(
|
||||
new AddressBookAdapter($db),
|
||||
$c->query('CardDavBackend')
|
||||
$c->query('CardDavBackend'),
|
||||
$logger,
|
||||
null
|
||||
);
|
||||
});
|
||||
|
||||
$container->registerService('MigrateCalendars', function($c) {
|
||||
/** @var IAppContainer $c */
|
||||
$db = $c->getServer()->getDatabaseConnection();
|
||||
$logger = $c->getServer()->getLogger();
|
||||
return new MigrateCalendars(
|
||||
new CalendarAdapter($db),
|
||||
$c->query('CalDavBackend')
|
||||
$c->query('CalDavBackend'),
|
||||
$logger,
|
||||
null
|
||||
);
|
||||
});
|
||||
|
||||
@@ -204,4 +210,19 @@ class Application extends App {
|
||||
$this->getContainer()->getServer()->getLogger()->logException($ex);
|
||||
}
|
||||
}
|
||||
|
||||
public function generateBirthdays() {
|
||||
try {
|
||||
/** @var BirthdayService $migration */
|
||||
$migration = $this->getContainer()->query('BirthdayService');
|
||||
$userManager = $this->getContainer()->getServer()->getUserManager();
|
||||
|
||||
$userManager->callForAllUsers(function($user) use($migration) {
|
||||
/** @var IUser $user */
|
||||
$migration->syncUser($user->getUID());
|
||||
});
|
||||
} catch (\Exception $ex) {
|
||||
$this->getContainer()->getServer()->getLogger()->logException($ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ CREATE TABLE calendarobjects (
|
||||
description TEXT,
|
||||
calendarorder INT(11) UNSIGNED NOT NULL DEFAULT '0',
|
||||
calendarcolor VARBINARY(10),
|
||||
timezone TEXT,
|
||||
timezone CLOB,
|
||||
components VARBINARY(20),
|
||||
transparent TINYINT(1) NOT NULL DEFAULT '0',
|
||||
UNIQUE(principaluri, uri)
|
||||
@@ -337,7 +337,7 @@ CREATE TABLE calendarobjects (
|
||||
</field>
|
||||
<field>
|
||||
<name>timezone</name>
|
||||
<type>text</type>
|
||||
<type>clob</type>
|
||||
</field>
|
||||
<field>
|
||||
<name>components</name>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<description>ownCloud WebDAV endpoint</description>
|
||||
<licence>AGPL</licence>
|
||||
<author>owncloud.org</author>
|
||||
<version>0.1.4</version>
|
||||
<version>0.1.6</version>
|
||||
<default_enable/>
|
||||
<types>
|
||||
<filesystem/>
|
||||
|
||||
@@ -25,3 +25,4 @@ $app = new Application();
|
||||
$app->setupCron();
|
||||
$app->migrateAddressbooks();
|
||||
$app->migrateCalendars();
|
||||
$app->generateBirthdays();
|
||||
|
||||
@@ -29,7 +29,6 @@ use OCA\DAV\Command\SyncSystemAddressBook;
|
||||
$dbConnection = \OC::$server->getDatabaseConnection();
|
||||
$userManager = OC::$server->getUserManager();
|
||||
$groupManager = OC::$server->getGroupManager();
|
||||
$config = \OC::$server->getConfig();
|
||||
|
||||
$app = new Application();
|
||||
|
||||
@@ -38,12 +37,5 @@ $application->add(new CreateCalendar($userManager, $groupManager, $dbConnection)
|
||||
$application->add(new CreateAddressBook($userManager, $app->getContainer()->query('CardDavBackend')));
|
||||
$application->add(new SyncSystemAddressBook($app->getSyncService()));
|
||||
$application->add(new SyncBirthdayCalendar($userManager, $app->getContainer()->query('BirthdayService')));
|
||||
|
||||
// the occ tool is *for now* only available in debug mode for developers to test
|
||||
if ($config->getSystemValue('debug', false)){
|
||||
$app = new \OCA\Dav\AppInfo\Application();
|
||||
$migration = $app->getContainer()->query('MigrateAddressbooks');
|
||||
$application->add(new MigrateAddressbooks($userManager, $migration));
|
||||
$migration = $app->getContainer()->query('MigrateCalendars');
|
||||
$application->add(new MigrateCalendars($userManager, $migration));
|
||||
}
|
||||
$application->add(new MigrateAddressbooks($userManager, $app->getContainer()->query('MigrateAddressbooks')));
|
||||
$application->add(new MigrateCalendars($userManager, $app->getContainer()->query('MigrateCalendars')));
|
||||
|
||||
@@ -23,3 +23,4 @@ use OCA\Dav\AppInfo\Application;
|
||||
|
||||
$app = new Application();
|
||||
$app->setupCron();
|
||||
$app->generateBirthdays();
|
||||
|
||||
@@ -23,11 +23,12 @@
|
||||
|
||||
// Backends
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\Connector\LegacyDAVACL;
|
||||
use OCA\DAV\CalDAV\CalendarRoot;
|
||||
use OCA\DAV\Connector\Sabre\Auth;
|
||||
use OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin;
|
||||
use OCA\DAV\Connector\Sabre\MaintenancePlugin;
|
||||
use OCA\DAV\Connector\Sabre\Principal;
|
||||
use Sabre\CalDAV\CalendarRoot;
|
||||
|
||||
$authBackend = new Auth(
|
||||
\OC::$server->getSession(),
|
||||
@@ -65,7 +66,7 @@ $server->addPlugin(new MaintenancePlugin());
|
||||
$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, 'ownCloud'));
|
||||
$server->addPlugin(new \Sabre\CalDAV\Plugin());
|
||||
|
||||
$acl = new \OCA\DAV\Connector\LegacyDAVACL();
|
||||
$acl = new LegacyDAVACL();
|
||||
$server->addPlugin($acl);
|
||||
|
||||
$server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin());
|
||||
|
||||
@@ -61,8 +61,8 @@ class MigrateCalendars extends Command {
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
$this->service->setup();
|
||||
|
||||
if ($input->hasArgument('user')) {
|
||||
$user = $input->getArgument('user');
|
||||
$user = $input->getArgument('user');
|
||||
if (!is_null($user)) {
|
||||
if (!$this->userManager->userExists($user)) {
|
||||
throw new \InvalidArgumentException("User <$user> in unknown.");
|
||||
}
|
||||
|
||||
@@ -61,8 +61,8 @@ class SyncBirthdayCalendar extends Command {
|
||||
* @param OutputInterface $output
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
if ($input->hasArgument('user')) {
|
||||
$user = $input->getArgument('user');
|
||||
$user = $input->getArgument('user');
|
||||
if (!is_null($user)) {
|
||||
if (!$this->userManager->userExists($user)) {
|
||||
throw new \InvalidArgumentException("User <$user> in unknown.");
|
||||
}
|
||||
|
||||
@@ -50,8 +50,7 @@ class BirthdayService {
|
||||
|
||||
$book = $this->cardDavBackEnd->getAddressBookById($addressBookId);
|
||||
$principalUri = $book['principaluri'];
|
||||
$calendarUri = self::BIRTHDAY_CALENDAR_URI;
|
||||
$calendar = $this->ensureCalendarExists($principalUri, $calendarUri, []);
|
||||
$calendar = $this->ensureCalendarExists($principalUri);
|
||||
$objectUri = $book['uri'] . '-' . $cardUri. '.ics';
|
||||
$calendarData = $this->buildBirthdayFromContact($cardData);
|
||||
$existing = $this->calDavBackEnd->getCalendarObject($calendar['id'], $objectUri);
|
||||
@@ -77,27 +76,27 @@ class BirthdayService {
|
||||
public function onCardDeleted($addressBookId, $cardUri) {
|
||||
$book = $this->cardDavBackEnd->getAddressBookById($addressBookId);
|
||||
$principalUri = $book['principaluri'];
|
||||
$calendarUri = self::BIRTHDAY_CALENDAR_URI;
|
||||
$calendar = $this->ensureCalendarExists($principalUri, $calendarUri, []);
|
||||
$calendar = $this->ensureCalendarExists($principalUri);
|
||||
$objectUri = $book['uri'] . '-' . $cardUri. '.ics';
|
||||
$this->calDavBackEnd->deleteCalendarObject($calendar['id'], $objectUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $principal
|
||||
* @param string $id
|
||||
* @param array $properties
|
||||
* @return array|null
|
||||
* @throws \Sabre\DAV\Exception\BadRequest
|
||||
*/
|
||||
public function ensureCalendarExists($principal, $id, $properties) {
|
||||
$book = $this->calDavBackEnd->getCalendarByUri($principal, $id);
|
||||
public function ensureCalendarExists($principal) {
|
||||
$book = $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI);
|
||||
if (!is_null($book)) {
|
||||
return $book;
|
||||
}
|
||||
$this->calDavBackEnd->createCalendar($principal, $id, $properties);
|
||||
$this->calDavBackEnd->createCalendar($principal, self::BIRTHDAY_CALENDAR_URI, [
|
||||
'{DAV:}displayname' => 'Contact birthdays',
|
||||
'{http://apple.com/ns/ical/}calendar-color' => '#FFFFCA',
|
||||
]);
|
||||
|
||||
return $this->calDavBackEnd->getCalendarByUri($principal, $id);
|
||||
return $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,7 +155,9 @@ class BirthdayService {
|
||||
* @param string $user
|
||||
*/
|
||||
public function syncUser($user) {
|
||||
$books = $this->cardDavBackEnd->getAddressBooksForUser('principals/users/'.$user);
|
||||
$principal = 'principals/users/'.$user;
|
||||
$this->ensureCalendarExists($principal);
|
||||
$books = $this->cardDavBackEnd->getAddressBooksForUser($principal);
|
||||
foreach($books as $book) {
|
||||
$cards = $this->cardDavBackEnd->getCards($book['id']);
|
||||
foreach($cards as $card) {
|
||||
|
||||
@@ -138,6 +138,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
||||
* @return array
|
||||
*/
|
||||
function getCalendarsForUser($principalUri) {
|
||||
$principalUriOriginal = $principalUri;
|
||||
$principalUri = $this->convertPrincipal($principalUri, true);
|
||||
$fields = array_values($this->propertyMap);
|
||||
$fields[] = 'id';
|
||||
@@ -184,7 +185,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
||||
$stmt->closeCursor();
|
||||
|
||||
// query for shared calendars
|
||||
$principals = $this->principalBackend->getGroupMembership($principalUri);
|
||||
$principals = $this->principalBackend->getGroupMembership($principalUriOriginal, true);
|
||||
$principals[]= $principalUri;
|
||||
|
||||
$fields = array_values($this->propertyMap);
|
||||
@@ -194,6 +195,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
||||
$fields[] = 'a.components';
|
||||
$fields[] = 'a.principaluri';
|
||||
$fields[] = 'a.transparent';
|
||||
$fields[] = 's.access';
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$result = $query->select($fields)
|
||||
->from('dav_shares', 's')
|
||||
@@ -221,6 +223,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
||||
'{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
|
||||
'{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
|
||||
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $row['principaluri'],
|
||||
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => (int)$row['access'] === Backend::ACCESS_READ,
|
||||
];
|
||||
|
||||
foreach($this->propertyMap as $xmlName=>$dbName) {
|
||||
@@ -815,9 +818,9 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
||||
function getCalendarObjectByUID($principalUri, $uid) {
|
||||
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select([$query->createFunction('c.`uri` AS `calendaruri`'), $query->createFunction('co.`uri` AS `objecturi`')])
|
||||
$query->selectAlias('c.uri', 'calendaruri')->selectAlias('co.uri', 'objecturi')
|
||||
->from('calendarobjects', 'co')
|
||||
->leftJoin('co', 'calendars', 'c', 'co.`calendarid` = c.`id`')
|
||||
->leftJoin('co', 'calendars', 'c', $query->expr()->eq('co.calendarid', 'c.id'))
|
||||
->where($query->expr()->eq('c.principaluri', $query->createNamedParameter($principalUri)))
|
||||
->andWhere($query->expr()->eq('co.uid', $query->createNamedParameter($uid)));
|
||||
|
||||
@@ -1294,7 +1297,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
||||
if (!$componentType) {
|
||||
throw new \Sabre\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
|
||||
}
|
||||
if ($componentType === 'VEVENT') {
|
||||
if ($componentType === 'VEVENT' && $component->DTSTART) {
|
||||
$firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
|
||||
// Finding the last occurence is a bit harder
|
||||
if (!isset($component->RRULE)) {
|
||||
@@ -1333,7 +1336,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
||||
'etag' => md5($calendarData),
|
||||
'size' => strlen($calendarData),
|
||||
'componentType' => $componentType,
|
||||
'firstOccurence' => $firstOccurence,
|
||||
'firstOccurence' => is_null($firstOccurence) ? null : max(0, $firstOccurence),
|
||||
'lastOccurence' => $lastOccurence,
|
||||
'uid' => $uid,
|
||||
];
|
||||
|
||||
@@ -22,10 +22,20 @@
|
||||
namespace OCA\DAV\CalDAV;
|
||||
|
||||
use OCA\DAV\DAV\Sharing\IShareable;
|
||||
use Sabre\CalDAV\Backend\BackendInterface;
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\PropPatch;
|
||||
|
||||
class Calendar extends \Sabre\CalDAV\Calendar implements IShareable {
|
||||
|
||||
public function __construct(BackendInterface $caldavBackend, $calendarInfo) {
|
||||
parent::__construct($caldavBackend, $calendarInfo);
|
||||
|
||||
if ($this->getName() === BirthdayService::BIRTHDAY_CALENDAR_URI) {
|
||||
$this->calendarInfo['{http://sabredav.org/ns}read-only'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the list of shares.
|
||||
*
|
||||
@@ -76,7 +86,31 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable {
|
||||
}
|
||||
|
||||
function getACL() {
|
||||
$acl = parent::getACL();
|
||||
$acl = [
|
||||
[
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->getOwner(),
|
||||
'protected' => true,
|
||||
]];
|
||||
$acl[] = [
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $this->getOwner(),
|
||||
'protected' => true,
|
||||
];
|
||||
if ($this->getOwner() !== parent::getOwner()) {
|
||||
$acl[] = [
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => parent::getOwner(),
|
||||
'protected' => true,
|
||||
];
|
||||
if ($this->canWrite()) {
|
||||
$acl[] = [
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => parent::getOwner(),
|
||||
'protected' => true,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/** @var CalDavBackend $calDavBackend */
|
||||
$calDavBackend = $this->caldavBackend;
|
||||
@@ -84,11 +118,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable {
|
||||
}
|
||||
|
||||
function getChildACL() {
|
||||
$acl = parent::getChildACL();
|
||||
|
||||
/** @var CalDavBackend $calDavBackend */
|
||||
$calDavBackend = $this->caldavBackend;
|
||||
return $calDavBackend->applyShareAcl($this->getResourceId(), $acl);
|
||||
return $this->getACL();
|
||||
}
|
||||
|
||||
function getOwner() {
|
||||
@@ -99,10 +129,6 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable {
|
||||
}
|
||||
|
||||
function delete() {
|
||||
if ($this->getName() === BirthdayService::BIRTHDAY_CALENDAR_URI) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
if (isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal'])) {
|
||||
$principal = 'principal:' . parent::getOwner();
|
||||
$shares = $this->getShares();
|
||||
@@ -122,4 +148,21 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable {
|
||||
}
|
||||
parent::delete();
|
||||
}
|
||||
|
||||
function propPatch(PropPatch $propPatch) {
|
||||
$mutations = $propPatch->getMutations();
|
||||
// If this is a shared calendar, the user can only change the enabled property, to hide it.
|
||||
if (isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal']) && (sizeof($mutations) !== 1 || !isset($mutations['{http://owncloud.org/ns}calendar-enabled']))) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
parent::propPatch($propPatch);
|
||||
}
|
||||
|
||||
private function canWrite() {
|
||||
if (isset($this->calendarInfo['{http://owncloud.org/ns}read-only'])) {
|
||||
return !$this->calendarInfo['{http://owncloud.org/ns}read-only'];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,15 +21,13 @@
|
||||
namespace OCA\DAV\CardDAV;
|
||||
|
||||
use OCA\DAV\DAV\Sharing\IShareable;
|
||||
use Sabre\CardDAV\Card;
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\Exception\NotFound;
|
||||
use Sabre\DAV\PropPatch;
|
||||
|
||||
class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable {
|
||||
|
||||
public function __construct(CardDavBackend $carddavBackend, array $addressBookInfo) {
|
||||
parent::__construct($carddavBackend, $addressBookInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the list of shares.
|
||||
*
|
||||
@@ -73,7 +71,31 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable {
|
||||
}
|
||||
|
||||
function getACL() {
|
||||
$acl = parent::getACL();
|
||||
$acl = [
|
||||
[
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->getOwner(),
|
||||
'protected' => true,
|
||||
]];
|
||||
$acl[] = [
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $this->getOwner(),
|
||||
'protected' => true,
|
||||
];
|
||||
if ($this->getOwner() !== parent::getOwner()) {
|
||||
$acl[] = [
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => parent::getOwner(),
|
||||
'protected' => true,
|
||||
];
|
||||
if ($this->canWrite()) {
|
||||
$acl[] = [
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => parent::getOwner(),
|
||||
'protected' => true,
|
||||
];
|
||||
}
|
||||
}
|
||||
if ($this->getOwner() === 'principals/system/system') {
|
||||
$acl[] = [
|
||||
'privilege' => '{DAV:}read',
|
||||
@@ -82,49 +104,24 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable {
|
||||
];
|
||||
}
|
||||
|
||||
// add the current user
|
||||
if (isset($this->addressBookInfo['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal'])) {
|
||||
$owner = $this->addressBookInfo['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal'];
|
||||
$acl[] = [
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $owner,
|
||||
'protected' => true,
|
||||
];
|
||||
if ($this->addressBookInfo['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only']) {
|
||||
$acl[] = [
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $owner,
|
||||
'protected' => true,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/** @var CardDavBackend $carddavBackend */
|
||||
$carddavBackend = $this->carddavBackend;
|
||||
return $carddavBackend->applyShareAcl($this->getResourceId(), $acl);
|
||||
}
|
||||
|
||||
function getChildACL() {
|
||||
$acl = parent::getChildACL();
|
||||
if ($this->getOwner() === 'principals/system/system') {
|
||||
$acl[] = [
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => '{DAV:}authenticated',
|
||||
'protected' => true,
|
||||
];
|
||||
}
|
||||
|
||||
/** @var CardDavBackend $carddavBackend */
|
||||
$carddavBackend = $this->carddavBackend;
|
||||
return $carddavBackend->applyShareAcl($this->getResourceId(), $acl);
|
||||
return $this->getACL();
|
||||
}
|
||||
|
||||
function getChild($name) {
|
||||
$obj = $this->carddavBackend->getCard($this->getResourceId(), $name);
|
||||
|
||||
$obj = $this->carddavBackend->getCard($this->addressBookInfo['id'], $name);
|
||||
if (!$obj) {
|
||||
throw new NotFound('Card not found');
|
||||
}
|
||||
$obj['acl'] = $this->getChildACL();
|
||||
return new Card($this->carddavBackend, $this->addressBookInfo, $obj);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,10 +159,24 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable {
|
||||
parent::delete();
|
||||
}
|
||||
|
||||
function propPatch(PropPatch $propPatch) {
|
||||
if (isset($this->addressBookInfo['{http://owncloud.org/ns}owner-principal'])) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
parent::propPatch($propPatch);
|
||||
}
|
||||
|
||||
public function getContactsGroups() {
|
||||
/** @var CardDavBackend $cardDavBackend */
|
||||
$cardDavBackend = $this->carddavBackend;
|
||||
|
||||
return $cardDavBackend->collectCardProperties($this->getResourceId(), 'CATEGORIES');
|
||||
}
|
||||
|
||||
private function canWrite() {
|
||||
if (isset($this->addressBookInfo['{http://owncloud.org/ns}read-only'])) {
|
||||
return !$this->addressBookInfo['{http://owncloud.org/ns}read-only'];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Thomas Müller <thomas.mueller@tmit.eu>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\DAV\CardDAV;
|
||||
|
||||
class Card extends \Sabre\CardDAV\Card {
|
||||
|
||||
function getACL() {
|
||||
$acl = parent::getACL();
|
||||
if ($this->getOwner() === 'principals/system/system') {
|
||||
$acl[] = [
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => '{DAV:}authenticated',
|
||||
'protected' => true,
|
||||
];
|
||||
}
|
||||
|
||||
/** @var CardDavBackend $carddavBackend */
|
||||
$carddavBackend = $this->carddavBackend;
|
||||
return $carddavBackend->applyShareAcl($this->getBookId(), $acl);
|
||||
}
|
||||
|
||||
private function getBookId() {
|
||||
return $this->addressBookInfo['id'];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -62,10 +62,6 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
||||
'BDAY', 'UID', 'N', 'FN', 'TITLE', 'ROLE', 'NOTE', 'NICKNAME',
|
||||
'ORG', 'CATEGORIES', 'EMAIL', 'TEL', 'IMPP', 'ADR', 'URL', 'GEO', 'CLOUD');
|
||||
|
||||
const ACCESS_OWNER = 1;
|
||||
const ACCESS_READ_WRITE = 2;
|
||||
const ACCESS_READ = 3;
|
||||
|
||||
/** @var EventDispatcherInterface */
|
||||
private $dispatcher;
|
||||
|
||||
@@ -78,7 +74,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
||||
*/
|
||||
public function __construct(IDBConnection $db,
|
||||
Principal $principalBackend,
|
||||
$dispatcher ) {
|
||||
EventDispatcherInterface $dispatcher = null) {
|
||||
$this->db = $db;
|
||||
$this->principalBackend = $principalBackend;
|
||||
$this->dispatcher = $dispatcher;
|
||||
@@ -103,6 +99,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
||||
* @return array
|
||||
*/
|
||||
function getAddressBooksForUser($principalUri) {
|
||||
$principalUriOriginal = $principalUri;
|
||||
$principalUri = $this->convertPrincipal($principalUri, true);
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select(['id', 'uri', 'displayname', 'principaluri', 'description', 'synctoken'])
|
||||
@@ -126,7 +123,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
||||
$result->closeCursor();
|
||||
|
||||
// query for shared calendars
|
||||
$principals = $this->principalBackend->getGroupMembership($principalUri);
|
||||
$principals = $this->principalBackend->getGroupMembership($principalUriOriginal, true);
|
||||
$principals[]= $principalUri;
|
||||
|
||||
$query = $this->db->getQueryBuilder();
|
||||
@@ -153,7 +150,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
||||
'{http://calendarserver.org/ns/}getctag' => $row['synctoken'],
|
||||
'{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
|
||||
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $row['principaluri'],
|
||||
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => $row['access'] === self::ACCESS_READ,
|
||||
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => (int)$row['access'] === Backend::ACCESS_READ,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,10 @@
|
||||
namespace OCA\DAV\Connector;
|
||||
|
||||
use OCA\DAV\Connector\Sabre\DavAclPlugin;
|
||||
use Sabre\DAV\INode;
|
||||
use Sabre\DAV\PropFind;
|
||||
use Sabre\HTTP\URLUtil;
|
||||
use Sabre\DAVACL\Xml\Property\Principal;
|
||||
|
||||
class LegacyDAVACL extends DavAclPlugin {
|
||||
|
||||
@@ -67,4 +70,16 @@ class LegacyDAVACL extends DavAclPlugin {
|
||||
}
|
||||
return "principals/$name";
|
||||
}
|
||||
|
||||
function propFind(PropFind $propFind, INode $node) {
|
||||
/* Overload current-user-principal */
|
||||
$propFind->handle('{DAV:}current-user-principal', function () {
|
||||
if ($url = parent::getCurrentUserPrincipal()) {
|
||||
return new Principal(Principal::HREF, $url . '/');
|
||||
} else {
|
||||
return new Principal(Principal::UNAUTHENTICATED);
|
||||
}
|
||||
});
|
||||
parent::propFind($propFind, $node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,11 @@ class PublicAuth extends \Sabre\DAV\Auth\Backend\AbstractBasic {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((int)$linkItem['share_type'] === \OCP\Share::SHARE_TYPE_LINK &&
|
||||
$this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') !== 'yes') {
|
||||
$this->share['permissions'] &= ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE);
|
||||
}
|
||||
|
||||
// check if the share is password protected
|
||||
if (isset($linkItem['share_with'])) {
|
||||
if ($linkItem['share_type'] == \OCP\Share::SHARE_TYPE_LINK) {
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
namespace OCA\DAV\Connector\Sabre;
|
||||
|
||||
use Exception;
|
||||
use OC\AppFramework\Http\Request;
|
||||
use OCP\IRequest;
|
||||
use OCP\ISession;
|
||||
use OCP\IUserSession;
|
||||
@@ -48,6 +49,8 @@ class Auth extends AbstractBasic {
|
||||
private $userSession;
|
||||
/** @var IRequest */
|
||||
private $request;
|
||||
/** @var string */
|
||||
private $currentUser;
|
||||
|
||||
/**
|
||||
* @param ISession $session
|
||||
@@ -130,7 +133,46 @@ class Auth extends AbstractBasic {
|
||||
$msg = $e->getMessage();
|
||||
throw new ServiceUnavailable("$class: $msg");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a CSRF check is required on the request
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function requiresCSRFCheck() {
|
||||
// GET requires no check at all
|
||||
if($this->request->getMethod() === 'GET') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Official ownCloud clients require no checks
|
||||
if($this->request->isUserAgent([
|
||||
Request::USER_AGENT_OWNCLOUD_DESKTOP,
|
||||
Request::USER_AGENT_OWNCLOUD_ANDROID,
|
||||
Request::USER_AGENT_OWNCLOUD_IOS,
|
||||
])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If not logged-in no check is required
|
||||
if(!$this->userSession->isLoggedIn()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// POST always requires a check
|
||||
if($this->request->getMethod() === 'POST') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If logged-in AND DAV authenticated no check is required
|
||||
if($this->userSession->isLoggedIn() &&
|
||||
$this->isDavAuthenticated($this->userSession->getUser()->getUID())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
@@ -139,27 +181,33 @@ class Auth extends AbstractBasic {
|
||||
* @throws NotAuthenticated
|
||||
*/
|
||||
private function auth(RequestInterface $request, ResponseInterface $response) {
|
||||
// If request is not GET and not authenticated via WebDAV a requesttoken is required
|
||||
if($this->userSession->isLoggedIn() &&
|
||||
$this->request->getMethod() !== 'GET' &&
|
||||
!$this->isDavAuthenticated($this->userSession->getUser()->getUID())) {
|
||||
if(!$this->request->passesCSRFCheck()) {
|
||||
$forcedLogout = false;
|
||||
if(!$this->request->passesCSRFCheck() &&
|
||||
$this->requiresCSRFCheck()) {
|
||||
// In case of a fail with POST we need to recheck the credentials
|
||||
if($this->request->getMethod() === 'POST') {
|
||||
$forcedLogout = true;
|
||||
} else {
|
||||
$response->setStatus(401);
|
||||
throw new \Sabre\DAV\Exception\NotAuthenticated('CSRF check not passed.');
|
||||
}
|
||||
}
|
||||
|
||||
if (\OC_User::handleApacheAuth() ||
|
||||
//Fix for broken webdav clients
|
||||
($this->userSession->isLoggedIn() && is_null($this->session->get(self::DAV_AUTHENTICATED))) ||
|
||||
//Well behaved clients that only send the cookie are allowed
|
||||
($this->userSession->isLoggedIn() && $this->session->get(self::DAV_AUTHENTICATED) === $this->userSession->getUser()->getUID() && $request->getHeader('Authorization') === null)
|
||||
) {
|
||||
$user = $this->userSession->getUser()->getUID();
|
||||
\OC_Util::setupFS($user);
|
||||
$this->currentUser = $user;
|
||||
$this->session->close();
|
||||
return [true, $this->principalPrefix . $user];
|
||||
if($forcedLogout) {
|
||||
$this->userSession->logout();
|
||||
} else {
|
||||
if (\OC_User::handleApacheAuth() ||
|
||||
//Fix for broken webdav clients
|
||||
($this->userSession->isLoggedIn() && is_null($this->session->get(self::DAV_AUTHENTICATED))) ||
|
||||
//Well behaved clients that only send the cookie are allowed
|
||||
($this->userSession->isLoggedIn() && $this->session->get(self::DAV_AUTHENTICATED) === $this->userSession->getUser()->getUID() && $request->getHeader('Authorization') === null)
|
||||
) {
|
||||
$user = $this->userSession->getUser()->getUID();
|
||||
\OC_Util::setupFS($user);
|
||||
$this->currentUser = $user;
|
||||
$this->session->close();
|
||||
return [true, $this->principalPrefix . $user];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->userSession->isLoggedIn() && in_array('XMLHttpRequest', explode(',', $request->getHeader('X-Requested-With')))) {
|
||||
@@ -169,6 +217,12 @@ class Auth extends AbstractBasic {
|
||||
throw new \Sabre\DAV\Exception\NotAuthenticated('Cannot authenticate over ajax calls');
|
||||
}
|
||||
|
||||
return parent::check($request, $response);
|
||||
$data = parent::check($request, $response);
|
||||
if($data[0] === true) {
|
||||
$startPos = strrpos($data[1], '/') + 1;
|
||||
$user = $this->userSession->getUser()->getUID();
|
||||
$data[1] = substr_replace($data[1], $user, $startPos);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,6 +235,16 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin {
|
||||
$propFind->handle(self::GETETAG_PROPERTYNAME, function() use ($node) {
|
||||
return $node->getEtag();
|
||||
});
|
||||
|
||||
$propFind->handle(self::OWNER_ID_PROPERTYNAME, function() use ($node) {
|
||||
$owner = $node->getOwner();
|
||||
return $owner->getUID();
|
||||
});
|
||||
$propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function() use ($node) {
|
||||
$owner = $node->getOwner();
|
||||
$displayName = $owner->getDisplayName();
|
||||
return $displayName;
|
||||
});
|
||||
}
|
||||
|
||||
if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
|
||||
@@ -267,16 +277,6 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin {
|
||||
return $node->getSize();
|
||||
});
|
||||
}
|
||||
|
||||
$propFind->handle(self::OWNER_ID_PROPERTYNAME, function() use ($node) {
|
||||
$owner = $node->getOwner();
|
||||
return $owner->getUID();
|
||||
});
|
||||
$propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function() use ($node) {
|
||||
$owner = $node->getOwner();
|
||||
$displayName = $owner->getDisplayName();
|
||||
return $displayName;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -230,7 +230,7 @@ abstract class Node implements \Sabre\DAV\INode {
|
||||
if ($this->info->isDeletable()) {
|
||||
$p .= 'D';
|
||||
}
|
||||
if ($this->info->isDeletable()) {
|
||||
if ($this->info->isUpdateable()) {
|
||||
$p .= 'NV'; // Renameable, Moveable
|
||||
}
|
||||
if ($this->info->getType() === \OCP\Files\FileInfo::TYPE_FILE) {
|
||||
|
||||
@@ -132,10 +132,11 @@ class Principal implements BackendInterface {
|
||||
* Returns the list of groups a principal is a member of
|
||||
*
|
||||
* @param string $principal
|
||||
* @param bool $needGroups
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getGroupMembership($principal) {
|
||||
public function getGroupMembership($principal, $needGroups = false) {
|
||||
list($prefix, $name) = URLUtil::splitPath($principal);
|
||||
|
||||
if ($prefix === $this->principalPrefix) {
|
||||
@@ -144,7 +145,7 @@ class Principal implements BackendInterface {
|
||||
throw new Exception('Principal not found');
|
||||
}
|
||||
|
||||
if ($this->hasGroups) {
|
||||
if ($this->hasGroups || $needGroups) {
|
||||
$groups = $this->groupManager->getUserGroups($user);
|
||||
$groups = array_map(function($group) {
|
||||
/** @var IGroup $group */
|
||||
|
||||
@@ -110,6 +110,7 @@ class ServerFactory {
|
||||
if($this->request->isUserAgent([
|
||||
'/WebDAVFS/',
|
||||
'/Microsoft Office OneNote 2013/',
|
||||
'/Microsoft-WebDAV-MiniRedir/',
|
||||
])) {
|
||||
$server->addPlugin(new \OCA\DAV\Connector\Sabre\FakeLockerPlugin());
|
||||
}
|
||||
@@ -136,6 +137,12 @@ class ServerFactory {
|
||||
|
||||
if($this->userSession->isLoggedIn()) {
|
||||
$server->addPlugin(new \OCA\DAV\Connector\Sabre\TagsPlugin($objectTree, $this->tagManager));
|
||||
$server->addPlugin(new \OCA\DAV\Connector\Sabre\SharesPlugin(
|
||||
$objectTree,
|
||||
$this->userSession,
|
||||
$userFolder,
|
||||
\OC::$server->getShareManager()
|
||||
));
|
||||
$server->addPlugin(new \OCA\DAV\Connector\Sabre\CommentPropertiesPlugin(\OC::$server->getCommentsManager(), $this->userSession));
|
||||
$server->addPlugin(new \OCA\DAV\Connector\Sabre\FilesReportPlugin(
|
||||
$objectTree,
|
||||
|
||||
177
apps/dav/lib/connector/sabre/sharesplugin.php
Normal file
177
apps/dav/lib/connector/sabre/sharesplugin.php
Normal file
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Vincent Petry <pvince81@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
namespace OCA\DAV\Connector\Sabre;
|
||||
|
||||
use \Sabre\DAV\PropFind;
|
||||
use \Sabre\DAV\PropPatch;
|
||||
use OCP\IUserSession;
|
||||
use OCP\Share\IShare;
|
||||
use OCA\DAV\Connector\Sabre\ShareTypeList;
|
||||
|
||||
/**
|
||||
* Sabre Plugin to provide share-related properties
|
||||
*/
|
||||
class SharesPlugin extends \Sabre\DAV\ServerPlugin {
|
||||
|
||||
const NS_OWNCLOUD = 'http://owncloud.org/ns';
|
||||
const SHARETYPES_PROPERTYNAME = '{http://owncloud.org/ns}share-types';
|
||||
|
||||
/**
|
||||
* Reference to main server object
|
||||
*
|
||||
* @var \Sabre\DAV\Server
|
||||
*/
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* @var \OCP\Share\IManager
|
||||
*/
|
||||
private $shareManager;
|
||||
|
||||
/**
|
||||
* @var \Sabre\DAV\Tree
|
||||
*/
|
||||
private $tree;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $userId;
|
||||
|
||||
/**
|
||||
* @var \OCP\Files\Folder
|
||||
*/
|
||||
private $userFolder;
|
||||
|
||||
/**
|
||||
* @var IShare[]
|
||||
*/
|
||||
private $cachedShareTypes;
|
||||
|
||||
/**
|
||||
* @param \Sabre\DAV\Tree $tree tree
|
||||
* @param IUserSession $userSession user session
|
||||
* @param \OCP\Files\Folder $userFolder user home folder
|
||||
* @param \OCP\Share\IManager $shareManager share manager
|
||||
*/
|
||||
public function __construct(
|
||||
\Sabre\DAV\Tree $tree,
|
||||
IUserSession $userSession,
|
||||
\OCP\Files\Folder $userFolder,
|
||||
\OCP\Share\IManager $shareManager
|
||||
) {
|
||||
$this->tree = $tree;
|
||||
$this->shareManager = $shareManager;
|
||||
$this->userFolder = $userFolder;
|
||||
$this->userId = $userSession->getUser()->getUID();
|
||||
$this->cachedShareTypes = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* This initializes the plugin.
|
||||
*
|
||||
* This function is called by \Sabre\DAV\Server, after
|
||||
* addPlugin is called.
|
||||
*
|
||||
* This method should set up the required event subscriptions.
|
||||
*
|
||||
* @param \Sabre\DAV\Server $server
|
||||
*/
|
||||
public function initialize(\Sabre\DAV\Server $server) {
|
||||
$server->xml->namespacesMap[self::NS_OWNCLOUD] = 'oc';
|
||||
$server->xml->elementMap[self::SHARETYPES_PROPERTYNAME] = 'OCA\\DAV\\Connector\\Sabre\\ShareTypeList';
|
||||
$server->protectedProperties[] = self::SHARETYPES_PROPERTYNAME;
|
||||
|
||||
$this->server = $server;
|
||||
$this->server->on('propFind', array($this, 'handleGetProperties'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of share types for outgoing shares
|
||||
*
|
||||
* @param \OCP\Files\Node $node file node
|
||||
*
|
||||
* @return int[] array of share types
|
||||
*/
|
||||
private function getShareTypes(\OCP\Files\Node $node) {
|
||||
$shareTypes = [];
|
||||
$requestedShareTypes = [
|
||||
\OCP\Share::SHARE_TYPE_USER,
|
||||
\OCP\Share::SHARE_TYPE_GROUP,
|
||||
\OCP\Share::SHARE_TYPE_LINK,
|
||||
\OCP\Share::SHARE_TYPE_REMOTE
|
||||
];
|
||||
foreach ($requestedShareTypes as $requestedShareType) {
|
||||
// one of each type is enough to find out about the types
|
||||
$shares = $this->shareManager->getSharesBy(
|
||||
$this->userId,
|
||||
$requestedShareType,
|
||||
$node,
|
||||
false,
|
||||
1
|
||||
);
|
||||
if (!empty($shares)) {
|
||||
$shareTypes[] = $requestedShareType;
|
||||
}
|
||||
}
|
||||
return $shareTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds shares to propfind response
|
||||
*
|
||||
* @param PropFind $propFind propfind object
|
||||
* @param \Sabre\DAV\INode $sabreNode sabre node
|
||||
*/
|
||||
public function handleGetProperties(
|
||||
PropFind $propFind,
|
||||
\Sabre\DAV\INode $sabreNode
|
||||
) {
|
||||
if (!($sabreNode instanceof \OCA\DAV\Connector\Sabre\Node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// need prefetch ?
|
||||
if ($sabreNode instanceof \OCA\DAV\Connector\Sabre\Directory
|
||||
&& $propFind->getDepth() !== 0
|
||||
&& !is_null($propFind->getStatus(self::SHARETYPES_PROPERTYNAME))
|
||||
) {
|
||||
$folderNode = $this->userFolder->get($propFind->getPath());
|
||||
$children = $folderNode->getDirectoryListing();
|
||||
|
||||
$this->cachedShareTypes[$folderNode->getId()] = $this->getShareTypes($folderNode);
|
||||
foreach ($children as $childNode) {
|
||||
$this->cachedShareTypes[$childNode->getId()] = $this->getShareTypes($childNode);
|
||||
}
|
||||
}
|
||||
|
||||
$propFind->handle(self::SHARETYPES_PROPERTYNAME, function() use ($sabreNode) {
|
||||
if (isset($this->cachedShareTypes[$sabreNode->getId()])) {
|
||||
$shareTypes = $this->cachedShareTypes[$sabreNode->getId()];
|
||||
} else {
|
||||
$node = $this->userFolder->get($sabreNode->getPath());
|
||||
$shareTypes = $this->getShareTypes($node);
|
||||
}
|
||||
|
||||
return new ShareTypeList($shareTypes);
|
||||
});
|
||||
}
|
||||
}
|
||||
87
apps/dav/lib/connector/sabre/sharetypelist.php
Normal file
87
apps/dav/lib/connector/sabre/sharetypelist.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Vincent Petry <pvince81@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\DAV\Connector\Sabre;
|
||||
|
||||
use Sabre\Xml\Element;
|
||||
use Sabre\Xml\Reader;
|
||||
use Sabre\Xml\Writer;
|
||||
|
||||
/**
|
||||
* ShareTypeList property
|
||||
*
|
||||
* This property contains multiple "share-type" elements, each containing a share type.
|
||||
*/
|
||||
class ShareTypeList implements Element {
|
||||
const NS_OWNCLOUD = 'http://owncloud.org/ns';
|
||||
|
||||
/**
|
||||
* Share types
|
||||
*
|
||||
* @var int[]
|
||||
*/
|
||||
private $shareTypes;
|
||||
|
||||
/**
|
||||
* @param int[] $shareTypes
|
||||
*/
|
||||
public function __construct($shareTypes) {
|
||||
$this->shareTypes = $shareTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the share types
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public function getShareTypes() {
|
||||
return $this->shareTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* The deserialize method is called during xml parsing.
|
||||
*
|
||||
* @param Reader $reader
|
||||
* @return mixed
|
||||
*/
|
||||
static function xmlDeserialize(Reader $reader) {
|
||||
$shareTypes = [];
|
||||
|
||||
foreach ($reader->parseInnerTree() as $elem) {
|
||||
if ($elem['name'] === '{' . self::NS_OWNCLOUD . '}share-type') {
|
||||
$shareTypes[] = (int)$elem['value'];
|
||||
}
|
||||
}
|
||||
return new self($shareTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* The xmlSerialize metod is called during xml writing.
|
||||
*
|
||||
* @param Writer $writer
|
||||
* @return void
|
||||
*/
|
||||
function xmlSerialize(Writer $writer) {
|
||||
foreach ($this->shareTypes as $shareType) {
|
||||
$writer->writeElement('{' . self::NS_OWNCLOUD . '}share-type', $shareType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,6 +190,14 @@ class Backend {
|
||||
'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'],
|
||||
'protected' => true,
|
||||
];
|
||||
} else if ($this->resourceType === 'calendar') {
|
||||
// Allow changing the properties of read only calendars,
|
||||
// so users can change the visibility.
|
||||
$acl[] = [
|
||||
'privilege' => '{DAV:}write-properties',
|
||||
'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'],
|
||||
'protected' => true,
|
||||
];
|
||||
}
|
||||
}
|
||||
return $acl;
|
||||
|
||||
@@ -28,6 +28,11 @@ use Sabre\HTTP\URLUtil;
|
||||
|
||||
class FilesHome implements ICollection {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $principalInfo;
|
||||
|
||||
/**
|
||||
* FilesHome constructor.
|
||||
*
|
||||
|
||||
@@ -23,10 +23,8 @@ namespace OCA\Dav\Migration;
|
||||
|
||||
use OCA\DAV\CardDAV\AddressBook;
|
||||
use OCA\DAV\CardDAV\CardDavBackend;
|
||||
use OCP\ILogger;
|
||||
use Sabre\CardDAV\Plugin;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class MigrateAddressbooks {
|
||||
@@ -37,15 +35,26 @@ class MigrateAddressbooks {
|
||||
/** @var CardDavBackend */
|
||||
private $backend;
|
||||
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
|
||||
/** @var OutputInterface */
|
||||
private $consoleOutput;
|
||||
|
||||
|
||||
/**
|
||||
* @param AddressBookAdapter $adapter
|
||||
* @param CardDavBackend $backend
|
||||
*/
|
||||
function __construct(AddressBookAdapter $adapter,
|
||||
CardDavBackend $backend
|
||||
CardDavBackend $backend,
|
||||
ILogger $logger,
|
||||
OutputInterface $consoleOutput = null
|
||||
) {
|
||||
$this->adapter = $adapter;
|
||||
$this->backend = $backend;
|
||||
$this->logger = $logger;
|
||||
$this->consoleOutput = $consoleOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,7 +89,17 @@ class MigrateAddressbooks {
|
||||
*/
|
||||
private function migrateBook($addressBookId, $newAddressBookId) {
|
||||
$this->adapter->foreachCard($addressBookId, function($card) use ($newAddressBookId) {
|
||||
$this->backend->createCard($newAddressBookId, $card['uri'], $card['carddata']);
|
||||
try {
|
||||
$this->backend->createCard($newAddressBookId, $card['uri'], $card['carddata']);
|
||||
} catch (\Exception $ex) {
|
||||
$eventId = $card['id'];
|
||||
$addressBookId = $card['addressbookid'];
|
||||
$msg = "One event could not be migrated. (id: $eventId, addressbookid: $addressBookId)";
|
||||
$this->logger->logException($ex, ['app' => 'dav', 'message' => $msg]);
|
||||
if (!is_null($this->consoleOutput)) {
|
||||
$this->consoleOutput->writeln($msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -96,11 +115,12 @@ class MigrateAddressbooks {
|
||||
|
||||
$add = array_map(function($s) {
|
||||
$prefix = 'principal:principals/users/';
|
||||
if ($s['share_type'] === 1) {
|
||||
if ((int)$s['share_type'] === 1) {
|
||||
$prefix = 'principal:principals/groups/';
|
||||
}
|
||||
return [
|
||||
'href' => $prefix . $s['share_with']
|
||||
'href' => $prefix . $s['share_with'],
|
||||
'readOnly' => !((int)$s['permissions'] === 31)
|
||||
];
|
||||
}, $shares);
|
||||
|
||||
|
||||
@@ -23,9 +23,7 @@ namespace OCA\Dav\Migration;
|
||||
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\CalDAV\Calendar;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use OCP\ILogger;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class MigrateCalendars {
|
||||
@@ -36,15 +34,25 @@ class MigrateCalendars {
|
||||
/** @var CalDavBackend */
|
||||
private $backend;
|
||||
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
|
||||
/** @var OutputInterface */
|
||||
private $consoleOutput;
|
||||
|
||||
/**
|
||||
* @param CalendarAdapter $adapter
|
||||
* @param CalDavBackend $backend
|
||||
*/
|
||||
function __construct(CalendarAdapter $adapter,
|
||||
CalDavBackend $backend
|
||||
CalDavBackend $backend,
|
||||
ILogger $logger,
|
||||
OutputInterface $consoleOutput = null
|
||||
) {
|
||||
$this->adapter = $adapter;
|
||||
$this->backend = $backend;
|
||||
$this->logger = $logger;
|
||||
$this->consoleOutput = $consoleOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,7 +90,17 @@ class MigrateCalendars {
|
||||
*/
|
||||
private function migrateCalendar($calendarId, $newCalendarId) {
|
||||
$this->adapter->foreachCalendarObject($calendarId, function($calObject) use ($newCalendarId) {
|
||||
$this->backend->createCalendarObject($newCalendarId, $calObject['uri'], $calObject['calendardata']);
|
||||
try {
|
||||
$this->backend->createCalendarObject($newCalendarId, $calObject['uri'], $calObject['calendardata']);
|
||||
} catch (\Exception $ex) {
|
||||
$eventId = $calObject['id'];
|
||||
$calendarId = $calObject['calendarId'];
|
||||
$msg = "One event could not be migrated. (id: $eventId, calendarid: $calendarId)";
|
||||
$this->logger->logException($ex, ['app' => 'dav', 'message' => $msg]);
|
||||
if (!is_null($this->consoleOutput)) {
|
||||
$this->consoleOutput->writeln($msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -98,11 +116,12 @@ class MigrateCalendars {
|
||||
|
||||
$add = array_map(function($s) {
|
||||
$prefix = 'principal:principals/users/';
|
||||
if ($s['share_type'] === 1) {
|
||||
if ((int)$s['share_type'] === 1) {
|
||||
$prefix = 'principal:principals/groups/';
|
||||
}
|
||||
return [
|
||||
'href' => $prefix . $s['share_with']
|
||||
'href' => $prefix . $s['share_with'],
|
||||
'readOnly' => !((int)$s['permissions'] === 31)
|
||||
];
|
||||
}, $shares);
|
||||
|
||||
|
||||
@@ -57,11 +57,11 @@ class CalDavBackendTest extends TestCase {
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(['getPrincipalByPath', 'getGroupMembership'])
|
||||
->getMock();
|
||||
$this->principal->method('getPrincipalByPath')
|
||||
$this->principal->expects($this->any())->method('getPrincipalByPath')
|
||||
->willReturn([
|
||||
'uri' => 'principals/best-friend'
|
||||
]);
|
||||
$this->principal->method('getGroupMembership')
|
||||
$this->principal->expects($this->any())->method('getGroupMembership')
|
||||
->withAnyParameters()
|
||||
->willReturn([self::UNIT_TEST_GROUP]);
|
||||
|
||||
@@ -446,6 +446,21 @@ EOD;
|
||||
$this->assertEquals(0, count($sos));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providesCalDataForGetDenormalizedData
|
||||
*/
|
||||
public function testGetDenormalizedData($expectedFirstOccurance, $calData) {
|
||||
$actual = $this->invokePrivate($this->backend, 'getDenormalizedData', [$calData]);
|
||||
$this->assertEquals($expectedFirstOccurance, $actual['firstOccurence']);
|
||||
}
|
||||
|
||||
public function providesCalDataForGetDenormalizedData() {
|
||||
return [
|
||||
[0, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:413F269B-B51B-46B1-AFB6-40055C53A4DC\r\nDTSTAMP:20160309T095056Z\r\nDTSTART;VALUE=DATE:16040222\r\nDTEND;VALUE=DATE:16040223\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:SUMMARY\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"],
|
||||
[null, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:413F269B-B51B-46B1-AFB6-40055C53A4DC\r\nDTSTAMP:20160309T095056Z\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:SUMMARY\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"]
|
||||
];
|
||||
}
|
||||
|
||||
private function assertAcl($principal, $privilege, $acl) {
|
||||
foreach($acl as $a) {
|
||||
if ($a['principal'] === $principal && $a['privilege'] === $privilege) {
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace OCA\DAV\Tests\Unit\CalDAV;
|
||||
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\CalDAV\Calendar;
|
||||
use Sabre\DAV\PropPatch;
|
||||
use Test\TestCase;
|
||||
|
||||
class CalendarTest extends TestCase {
|
||||
@@ -31,7 +32,7 @@ class CalendarTest extends TestCase {
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject | CalDavBackend $backend */
|
||||
$backend = $this->getMockBuilder('OCA\DAV\CalDAV\CalDavBackend')->disableOriginalConstructor()->getMock();
|
||||
$backend->expects($this->once())->method('updateShares');
|
||||
$backend->method('getShares')->willReturn([
|
||||
$backend->expects($this->any())->method('getShares')->willReturn([
|
||||
['href' => 'principal:user2']
|
||||
]);
|
||||
$calendarInfo = [
|
||||
@@ -51,7 +52,7 @@ class CalendarTest extends TestCase {
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject | CalDavBackend $backend */
|
||||
$backend = $this->getMockBuilder('OCA\DAV\CalDAV\CalDavBackend')->disableOriginalConstructor()->getMock();
|
||||
$backend->expects($this->never())->method('updateShares');
|
||||
$backend->method('getShares')->willReturn([
|
||||
$backend->expects($this->any())->method('getShares')->willReturn([
|
||||
['href' => 'principal:group2']
|
||||
]);
|
||||
$calendarInfo = [
|
||||
@@ -63,4 +64,103 @@ class CalendarTest extends TestCase {
|
||||
$c = new Calendar($backend, $calendarInfo);
|
||||
$c->delete();
|
||||
}
|
||||
|
||||
public function dataPropPatch() {
|
||||
return [
|
||||
[[], true],
|
||||
[[
|
||||
'{http://owncloud.org/ns}calendar-enabled' => true,
|
||||
], false],
|
||||
[[
|
||||
'{DAV:}displayname' => true,
|
||||
], true],
|
||||
[[
|
||||
'{DAV:}displayname' => true,
|
||||
'{http://owncloud.org/ns}calendar-enabled' => true,
|
||||
], true],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataPropPatch
|
||||
*/
|
||||
public function testPropPatch($mutations, $throws) {
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject | CalDavBackend $backend */
|
||||
$backend = $this->getMockBuilder('OCA\DAV\CalDAV\CalDavBackend')->disableOriginalConstructor()->getMock();
|
||||
$calendarInfo = [
|
||||
'{http://owncloud.org/ns}owner-principal' => 'user1',
|
||||
'principaluri' => 'user2',
|
||||
'id' => 666,
|
||||
'uri' => 'default'
|
||||
];
|
||||
$c = new Calendar($backend, $calendarInfo);
|
||||
|
||||
if ($throws) {
|
||||
$this->setExpectedException('\Sabre\DAV\Exception\Forbidden');
|
||||
}
|
||||
$c->propPatch(new PropPatch($mutations));
|
||||
if (!$throws) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providesReadOnlyInfo
|
||||
*/
|
||||
public function testAcl($expectsWrite, $readOnlyValue, $hasOwnerSet) {
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject | CalDavBackend $backend */
|
||||
$backend = $this->getMockBuilder('OCA\DAV\CalDAV\CalDavBackend')->disableOriginalConstructor()->getMock();
|
||||
$backend->expects($this->any())->method('applyShareAcl')->willReturnArgument(1);
|
||||
$calendarInfo = [
|
||||
'principaluri' => 'user2',
|
||||
'id' => 666,
|
||||
'uri' => 'default'
|
||||
];
|
||||
if (!is_null($readOnlyValue)) {
|
||||
$calendarInfo['{http://owncloud.org/ns}read-only'] = $readOnlyValue;
|
||||
}
|
||||
if ($hasOwnerSet) {
|
||||
$calendarInfo['{http://owncloud.org/ns}owner-principal'] = 'user1';
|
||||
}
|
||||
$c = new Calendar($backend, $calendarInfo);
|
||||
$acl = $c->getACL();
|
||||
$childAcl = $c->getChildACL();
|
||||
|
||||
$expectedAcl = [[
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $hasOwnerSet ? 'user1' : 'user2',
|
||||
'protected' => true
|
||||
], [
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $hasOwnerSet ? 'user1' : 'user2',
|
||||
'protected' => true
|
||||
]];
|
||||
if ($hasOwnerSet) {
|
||||
$expectedAcl[] = [
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => 'user2',
|
||||
'protected' => true
|
||||
];
|
||||
if ($expectsWrite) {
|
||||
$expectedAcl[] = [
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => 'user2',
|
||||
'protected' => true
|
||||
];
|
||||
}
|
||||
}
|
||||
$this->assertEquals($expectedAcl, $acl);
|
||||
$this->assertEquals($expectedAcl, $childAcl);
|
||||
}
|
||||
|
||||
public function providesReadOnlyInfo() {
|
||||
return [
|
||||
'read-only property not set' => [true, null, true],
|
||||
'read-only property is false' => [true, false, true],
|
||||
'read-only property is true' => [false, true, true],
|
||||
'read-only property not set and no owner' => [true, null, false],
|
||||
'read-only property is false and no owner' => [true, false, false],
|
||||
'read-only property is true and no owner' => [false, true, false],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace OCA\DAV\Tests\Unit\CardDAV;
|
||||
|
||||
use OCA\DAV\CardDAV\AddressBook;
|
||||
use OCA\DAV\CardDAV\CardDavBackend;
|
||||
use Sabre\DAV\PropPatch;
|
||||
use Test\TestCase;
|
||||
|
||||
class AddressBookTest extends TestCase {
|
||||
@@ -31,7 +32,7 @@ class AddressBookTest extends TestCase {
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject | CardDavBackend $backend */
|
||||
$backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend')->disableOriginalConstructor()->getMock();
|
||||
$backend->expects($this->once())->method('updateShares');
|
||||
$backend->method('getShares')->willReturn([
|
||||
$backend->expects($this->any())->method('getShares')->willReturn([
|
||||
['href' => 'principal:user2']
|
||||
]);
|
||||
$calendarInfo = [
|
||||
@@ -50,7 +51,7 @@ class AddressBookTest extends TestCase {
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject | CardDavBackend $backend */
|
||||
$backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend')->disableOriginalConstructor()->getMock();
|
||||
$backend->expects($this->never())->method('updateShares');
|
||||
$backend->method('getShares')->willReturn([
|
||||
$backend->expects($this->any())->method('getShares')->willReturn([
|
||||
['href' => 'principal:group2']
|
||||
]);
|
||||
$calendarInfo = [
|
||||
@@ -61,4 +62,78 @@ class AddressBookTest extends TestCase {
|
||||
$c = new AddressBook($backend, $calendarInfo);
|
||||
$c->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Sabre\DAV\Exception\Forbidden
|
||||
*/
|
||||
public function testPropPatch() {
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject | CardDavBackend $backend */
|
||||
$backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend')->disableOriginalConstructor()->getMock();
|
||||
$calendarInfo = [
|
||||
'{http://owncloud.org/ns}owner-principal' => 'user1',
|
||||
'principaluri' => 'user2',
|
||||
'id' => 666
|
||||
];
|
||||
$c = new AddressBook($backend, $calendarInfo);
|
||||
$c->propPatch(new PropPatch([]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providesReadOnlyInfo
|
||||
*/
|
||||
public function testAcl($expectsWrite, $readOnlyValue, $hasOwnerSet) {
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject | CardDavBackend $backend */
|
||||
$backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend')->disableOriginalConstructor()->getMock();
|
||||
$backend->expects($this->any())->method('applyShareAcl')->willReturnArgument(1);
|
||||
$calendarInfo = [
|
||||
'principaluri' => 'user2',
|
||||
'id' => 666,
|
||||
'uri' => 'default'
|
||||
];
|
||||
if (!is_null($readOnlyValue)) {
|
||||
$calendarInfo['{http://owncloud.org/ns}read-only'] = $readOnlyValue;
|
||||
}
|
||||
if ($hasOwnerSet) {
|
||||
$calendarInfo['{http://owncloud.org/ns}owner-principal'] = 'user1';
|
||||
}
|
||||
$c = new AddressBook($backend, $calendarInfo);
|
||||
$acl = $c->getACL();
|
||||
$childAcl = $c->getChildACL();
|
||||
|
||||
$expectedAcl = [[
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $hasOwnerSet ? 'user1' : 'user2',
|
||||
'protected' => true
|
||||
], [
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $hasOwnerSet ? 'user1' : 'user2',
|
||||
'protected' => true
|
||||
]];
|
||||
if ($hasOwnerSet) {
|
||||
$expectedAcl[] = [
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => 'user2',
|
||||
'protected' => true
|
||||
];
|
||||
if ($expectsWrite) {
|
||||
$expectedAcl[] = [
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => 'user2',
|
||||
'protected' => true
|
||||
];
|
||||
}
|
||||
}
|
||||
$this->assertEquals($expectedAcl, $acl);
|
||||
$this->assertEquals($expectedAcl, $childAcl);
|
||||
}
|
||||
|
||||
public function providesReadOnlyInfo() {
|
||||
return [
|
||||
'read-only property not set' => [true, null, true],
|
||||
'read-only property is false' => [true, false, true],
|
||||
'read-only property is true' => [false, true, true],
|
||||
'read-only property not set and no owner' => [true, null, false],
|
||||
'read-only property is false and no owner' => [true, false, false],
|
||||
'read-only property is true and no owner' => [false, true, false],
|
||||
];
|
||||
}}
|
||||
|
||||
@@ -198,10 +198,7 @@ class Auth extends TestCase {
|
||||
$this->assertFalse($this->invokePrivate($this->auth, 'validateUserPass', ['MyTestUser', 'MyTestPassword']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Sabre\DAV\Exception\NotAuthenticated
|
||||
* @expectedExceptionMessage CSRF check not passed.
|
||||
*/
|
||||
|
||||
public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenForNonGet() {
|
||||
$request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
|
||||
->disableOriginalConstructor()
|
||||
@@ -210,27 +207,182 @@ class Auth extends TestCase {
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->userSession
|
||||
->expects($this->once())
|
||||
->expects($this->any())
|
||||
->method('isLoggedIn')
|
||||
->will($this->returnValue(true));
|
||||
$this->request
|
||||
->expects($this->any())
|
||||
->method('getMethod')
|
||||
->willReturn('POST');
|
||||
$this->session
|
||||
->expects($this->once())
|
||||
->expects($this->any())
|
||||
->method('get')
|
||||
->with('AUTHENTICATED_TO_DAV_BACKEND')
|
||||
->will($this->returnValue(null));
|
||||
$user = $this->getMockBuilder('\OCP\IUser')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$user->expects($this->once())
|
||||
$user->expects($this->any())
|
||||
->method('getUID')
|
||||
->will($this->returnValue('MyWrongDavUser'));
|
||||
$this->userSession
|
||||
->expects($this->once())
|
||||
->expects($this->any())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($user));
|
||||
$this->request
|
||||
->expects($this->once())
|
||||
->method('passesCSRFCheck')
|
||||
->willReturn(false);
|
||||
|
||||
$expectedResponse = [
|
||||
false,
|
||||
"No 'Authorization: Basic' header found. Either the client didn't send one, or the server is mis-configured",
|
||||
];
|
||||
$response = $this->auth->check($request, $response);
|
||||
$this->assertEquals([true, 'principals/users/MyWrongDavUser'], $response);
|
||||
$this->assertSame($expectedResponse, $response);
|
||||
}
|
||||
|
||||
public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenAndCorrectlyDavAuthenticated() {
|
||||
$request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->userSession
|
||||
->expects($this->any())
|
||||
->method('isLoggedIn')
|
||||
->willReturn(true);
|
||||
$this->request
|
||||
->expects($this->any())
|
||||
->method('getMethod')
|
||||
->willReturn('PROPFIND');
|
||||
$this->request
|
||||
->expects($this->any())
|
||||
->method('isUserAgent')
|
||||
->with([
|
||||
'/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/',
|
||||
'/^Mozilla\/5\.0 \(Android\) ownCloud\-android.*$/',
|
||||
'/^Mozilla\/5\.0 \(iOS\) ownCloud\-iOS.*$/',
|
||||
])
|
||||
->willReturn(false);
|
||||
$this->session
|
||||
->expects($this->any())
|
||||
->method('get')
|
||||
->with('AUTHENTICATED_TO_DAV_BACKEND')
|
||||
->will($this->returnValue('LoggedInUser'));
|
||||
$user = $this->getMockBuilder('\OCP\IUser')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$user->expects($this->any())
|
||||
->method('getUID')
|
||||
->will($this->returnValue('LoggedInUser'));
|
||||
$this->userSession
|
||||
->expects($this->any())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($user));
|
||||
$this->request
|
||||
->expects($this->once())
|
||||
->method('passesCSRFCheck')
|
||||
->willReturn(false);
|
||||
$this->auth->check($request, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Sabre\DAV\Exception\NotAuthenticated
|
||||
* @expectedExceptionMessage CSRF check not passed.
|
||||
*/
|
||||
public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenAndIncorrectlyDavAuthenticated() {
|
||||
$request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->userSession
|
||||
->expects($this->any())
|
||||
->method('isLoggedIn')
|
||||
->willReturn(true);
|
||||
$this->request
|
||||
->expects($this->any())
|
||||
->method('getMethod')
|
||||
->willReturn('PROPFIND');
|
||||
$this->request
|
||||
->expects($this->any())
|
||||
->method('isUserAgent')
|
||||
->with([
|
||||
'/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/',
|
||||
'/^Mozilla\/5\.0 \(Android\) ownCloud\-android.*$/',
|
||||
'/^Mozilla\/5\.0 \(iOS\) ownCloud\-iOS.*$/',
|
||||
])
|
||||
->willReturn(false);
|
||||
$this->session
|
||||
->expects($this->any())
|
||||
->method('get')
|
||||
->with('AUTHENTICATED_TO_DAV_BACKEND')
|
||||
->will($this->returnValue('AnotherUser'));
|
||||
$user = $this->getMockBuilder('\OCP\IUser')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$user->expects($this->any())
|
||||
->method('getUID')
|
||||
->will($this->returnValue('LoggedInUser'));
|
||||
$this->userSession
|
||||
->expects($this->any())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($user));
|
||||
$this->request
|
||||
->expects($this->once())
|
||||
->method('passesCSRFCheck')
|
||||
->willReturn(false);
|
||||
$this->auth->check($request, $response);
|
||||
}
|
||||
|
||||
public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenForNonGetAndDesktopClient() {
|
||||
$request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->userSession
|
||||
->expects($this->any())
|
||||
->method('isLoggedIn')
|
||||
->will($this->returnValue(true));
|
||||
$this->request
|
||||
->expects($this->any())
|
||||
->method('getMethod')
|
||||
->willReturn('POST');
|
||||
$this->request
|
||||
->expects($this->any())
|
||||
->method('isUserAgent')
|
||||
->with([
|
||||
'/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/',
|
||||
'/^Mozilla\/5\.0 \(Android\) ownCloud\-android.*$/',
|
||||
'/^Mozilla\/5\.0 \(iOS\) ownCloud\-iOS.*$/',
|
||||
])
|
||||
->willReturn(true);
|
||||
$this->session
|
||||
->expects($this->any())
|
||||
->method('get')
|
||||
->with('AUTHENTICATED_TO_DAV_BACKEND')
|
||||
->will($this->returnValue(null));
|
||||
$user = $this->getMockBuilder('\OCP\IUser')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$user->expects($this->any())
|
||||
->method('getUID')
|
||||
->will($this->returnValue('MyWrongDavUser'));
|
||||
$this->userSession
|
||||
->expects($this->any())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($user));
|
||||
$this->request
|
||||
->expects($this->once())
|
||||
->method('passesCSRFCheck')
|
||||
->willReturn(false);
|
||||
|
||||
$this->auth->check($request, $response);
|
||||
}
|
||||
|
||||
public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenForGet() {
|
||||
@@ -241,26 +393,26 @@ class Auth extends TestCase {
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->userSession
|
||||
->expects($this->exactly(2))
|
||||
->expects($this->any())
|
||||
->method('isLoggedIn')
|
||||
->will($this->returnValue(true));
|
||||
$this->session
|
||||
->expects($this->once())
|
||||
->expects($this->any())
|
||||
->method('get')
|
||||
->with('AUTHENTICATED_TO_DAV_BACKEND')
|
||||
->will($this->returnValue(null));
|
||||
$user = $this->getMockBuilder('\OCP\IUser')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$user->expects($this->once())
|
||||
$user->expects($this->any())
|
||||
->method('getUID')
|
||||
->will($this->returnValue('MyWrongDavUser'));
|
||||
$this->userSession
|
||||
->expects($this->once())
|
||||
->expects($this->any())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($user));
|
||||
$this->request
|
||||
->expects($this->once())
|
||||
->expects($this->any())
|
||||
->method('getMethod')
|
||||
->willReturn('GET');
|
||||
|
||||
@@ -268,7 +420,6 @@ class Auth extends TestCase {
|
||||
$this->assertEquals([true, 'principals/users/MyWrongDavUser'], $response);
|
||||
}
|
||||
|
||||
|
||||
public function testAuthenticateAlreadyLoggedInWithCsrfTokenForGet() {
|
||||
$request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
|
||||
->disableOriginalConstructor()
|
||||
@@ -277,22 +428,22 @@ class Auth extends TestCase {
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->userSession
|
||||
->expects($this->exactly(2))
|
||||
->expects($this->any())
|
||||
->method('isLoggedIn')
|
||||
->will($this->returnValue(true));
|
||||
$this->session
|
||||
->expects($this->exactly(2))
|
||||
->expects($this->any())
|
||||
->method('get')
|
||||
->with('AUTHENTICATED_TO_DAV_BACKEND')
|
||||
->will($this->returnValue(null));
|
||||
$user = $this->getMockBuilder('\OCP\IUser')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$user->expects($this->exactly(2))
|
||||
$user->expects($this->any())
|
||||
->method('getUID')
|
||||
->will($this->returnValue('MyWrongDavUser'));
|
||||
$this->userSession
|
||||
->expects($this->exactly(2))
|
||||
->expects($this->any())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($user));
|
||||
$this->request
|
||||
@@ -368,6 +519,10 @@ class Auth extends TestCase {
|
||||
->method('get')
|
||||
->with('AUTHENTICATED_TO_DAV_BACKEND')
|
||||
->will($this->returnValue('MyTestUser'));
|
||||
$this->request
|
||||
->expects($this->once())
|
||||
->method('getMethod')
|
||||
->willReturn('GET');
|
||||
$httpRequest
|
||||
->expects($this->atLeastOnce())
|
||||
->method('getHeader')
|
||||
@@ -407,15 +562,15 @@ class Auth extends TestCase {
|
||||
$user = $this->getMockBuilder('\OCP\IUser')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$user->expects($this->exactly(2))
|
||||
$user->expects($this->exactly(3))
|
||||
->method('getUID')
|
||||
->will($this->returnValue('MyTestUser'));
|
||||
$this->userSession
|
||||
->expects($this->exactly(2))
|
||||
->expects($this->exactly(3))
|
||||
->method('getUser')
|
||||
->will($this->returnValue($user));
|
||||
$response = $this->auth->check($server->httpRequest, $server->httpResponse);
|
||||
$this->assertEquals([true, 'principals/users/username'], $response);
|
||||
$this->assertEquals([true, 'principals/users/MyTestUser'], $response);
|
||||
}
|
||||
|
||||
public function testAuthenticateInvalidCredentials() {
|
||||
|
||||
@@ -109,8 +109,6 @@ class FilesPlugin extends \Test\TestCase {
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public function testGetPropertiesForFile() {
|
||||
$node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File');
|
||||
|
||||
@@ -165,6 +163,56 @@ class FilesPlugin extends \Test\TestCase {
|
||||
$this->assertEquals(array(self::SIZE_PROPERTYNAME), $propFind->get404Properties());
|
||||
}
|
||||
|
||||
public function testGetPropertiesForFileHome() {
|
||||
$node = $this->createTestNode('\OCA\DAV\Files\FilesHome');
|
||||
|
||||
$propFind = new \Sabre\DAV\PropFind(
|
||||
'/dummyPath',
|
||||
array(
|
||||
self::GETETAG_PROPERTYNAME,
|
||||
self::FILEID_PROPERTYNAME,
|
||||
self::INTERNAL_FILEID_PROPERTYNAME,
|
||||
self::SIZE_PROPERTYNAME,
|
||||
self::PERMISSIONS_PROPERTYNAME,
|
||||
self::DOWNLOADURL_PROPERTYNAME,
|
||||
self::OWNER_ID_PROPERTYNAME,
|
||||
self::OWNER_DISPLAY_NAME_PROPERTYNAME
|
||||
),
|
||||
0
|
||||
);
|
||||
|
||||
$user = $this->getMockBuilder('\OC\User\User')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$user->expects($this->never())->method('getUID');
|
||||
$user->expects($this->never())->method('getDisplayName');
|
||||
$node->expects($this->never())->method('getDirectDownload');
|
||||
$node->expects($this->never())->method('getOwner');
|
||||
$node->expects($this->never())->method('getSize');
|
||||
|
||||
$this->plugin->handleGetProperties(
|
||||
$propFind,
|
||||
$node
|
||||
);
|
||||
|
||||
$this->assertEquals(null, $propFind->get(self::GETETAG_PROPERTYNAME));
|
||||
$this->assertEquals(null, $propFind->get(self::FILEID_PROPERTYNAME));
|
||||
$this->assertEquals(null, $propFind->get(self::INTERNAL_FILEID_PROPERTYNAME));
|
||||
$this->assertEquals(null, $propFind->get(self::SIZE_PROPERTYNAME));
|
||||
$this->assertEquals(null, $propFind->get(self::PERMISSIONS_PROPERTYNAME));
|
||||
$this->assertEquals(null, $propFind->get(self::DOWNLOADURL_PROPERTYNAME));
|
||||
$this->assertEquals(null, $propFind->get(self::OWNER_ID_PROPERTYNAME));
|
||||
$this->assertEquals(null, $propFind->get(self::OWNER_DISPLAY_NAME_PROPERTYNAME));
|
||||
$this->assertEquals(['{DAV:}getetag',
|
||||
'{http://owncloud.org/ns}id',
|
||||
'{http://owncloud.org/ns}fileid',
|
||||
'{http://owncloud.org/ns}size',
|
||||
'{http://owncloud.org/ns}permissions',
|
||||
'{http://owncloud.org/ns}downloadURL',
|
||||
'{http://owncloud.org/ns}owner-id',
|
||||
'{http://owncloud.org/ns}owner-display-name'
|
||||
], $propFind->get404Properties());
|
||||
}
|
||||
|
||||
public function testGetPropertiesStorageNotAvailable() {
|
||||
$node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File');
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ class Node extends \Test\TestCase {
|
||||
array(\OCP\Constants::PERMISSION_ALL, 'file', true, false, 'SRDNVW'),
|
||||
array(\OCP\Constants::PERMISSION_ALL, 'file', true, true, 'SRMDNVW'),
|
||||
array(\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE, 'file', true, false, 'SDNVW'),
|
||||
array(\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_UPDATE, 'file', false, false, 'RDNV'),
|
||||
array(\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_DELETE, 'file', false, false, 'RW'),
|
||||
array(\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_UPDATE, 'file', false, false, 'RD'),
|
||||
array(\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_DELETE, 'file', false, false, 'RNVW'),
|
||||
array(\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE, 'file', false, false, 'RDNVW'),
|
||||
array(\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE, 'dir', false, false, 'RDNV'),
|
||||
);
|
||||
|
||||
259
apps/dav/tests/unit/connector/sabre/sharesplugin.php
Normal file
259
apps/dav/tests/unit/connector/sabre/sharesplugin.php
Normal file
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Vincent Petry <pvince81@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
namespace OCA\DAV\Tests\Unit\Connector\Sabre;
|
||||
|
||||
class SharesPlugin extends \Test\TestCase {
|
||||
|
||||
const SHARETYPES_PROPERTYNAME = \OCA\DAV\Connector\Sabre\SharesPlugin::SHARETYPES_PROPERTYNAME;
|
||||
|
||||
/**
|
||||
* @var \Sabre\DAV\Server
|
||||
*/
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* @var \Sabre\DAV\Tree
|
||||
*/
|
||||
private $tree;
|
||||
|
||||
/**
|
||||
* @var \OCP\Share\IManager
|
||||
*/
|
||||
private $shareManager;
|
||||
|
||||
/**
|
||||
* @var \OCP\Files\Folder
|
||||
*/
|
||||
private $userFolder;
|
||||
|
||||
/**
|
||||
* @var \OCA\DAV\Connector\Sabre\SharesPlugin
|
||||
*/
|
||||
private $plugin;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->server = new \Sabre\DAV\Server();
|
||||
$this->tree = $this->getMockBuilder('\Sabre\DAV\Tree')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->shareManager = $this->getMock('\OCP\Share\IManager');
|
||||
$user = $this->getMock('\OCP\IUser');
|
||||
$user->expects($this->once())
|
||||
->method('getUID')
|
||||
->will($this->returnValue('user1'));
|
||||
$userSession = $this->getMock('\OCP\IUserSession');
|
||||
$userSession->expects($this->once())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($user));
|
||||
|
||||
$this->userFolder = $this->getMock('\OCP\Files\Folder');
|
||||
|
||||
$this->plugin = new \OCA\DAV\Connector\Sabre\SharesPlugin(
|
||||
$this->tree,
|
||||
$userSession,
|
||||
$this->userFolder,
|
||||
$this->shareManager
|
||||
);
|
||||
$this->plugin->initialize($this->server);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider sharesGetPropertiesDataProvider
|
||||
*/
|
||||
public function testGetProperties($shareTypes) {
|
||||
$sabreNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$sabreNode->expects($this->any())
|
||||
->method('getId')
|
||||
->will($this->returnValue(123));
|
||||
$sabreNode->expects($this->once())
|
||||
->method('getPath')
|
||||
->will($this->returnValue('/subdir'));
|
||||
|
||||
// node API nodes
|
||||
$node = $this->getMock('\OCP\Files\Folder');
|
||||
|
||||
$this->userFolder->expects($this->once())
|
||||
->method('get')
|
||||
->with('/subdir')
|
||||
->will($this->returnValue($node));
|
||||
|
||||
$this->shareManager->expects($this->any())
|
||||
->method('getSharesBy')
|
||||
->with(
|
||||
$this->equalTo('user1'),
|
||||
$this->anything(),
|
||||
$this->anything(),
|
||||
$this->equalTo(false),
|
||||
$this->equalTo(1)
|
||||
)
|
||||
->will($this->returnCallback(function($userId, $requestedShareType, $node, $flag, $limit) use ($shareTypes){
|
||||
if (in_array($requestedShareType, $shareTypes)) {
|
||||
return ['dummyshare'];
|
||||
}
|
||||
return [];
|
||||
}));
|
||||
|
||||
$propFind = new \Sabre\DAV\PropFind(
|
||||
'/dummyPath',
|
||||
[self::SHARETYPES_PROPERTYNAME],
|
||||
0
|
||||
);
|
||||
|
||||
$this->plugin->handleGetProperties(
|
||||
$propFind,
|
||||
$sabreNode
|
||||
);
|
||||
|
||||
$result = $propFind->getResultForMultiStatus();
|
||||
|
||||
$this->assertEmpty($result[404]);
|
||||
unset($result[404]);
|
||||
$this->assertEquals($shareTypes, $result[200][self::SHARETYPES_PROPERTYNAME]->getShareTypes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider sharesGetPropertiesDataProvider
|
||||
*/
|
||||
public function testPreloadThenGetProperties($shareTypes) {
|
||||
$sabreNode1 = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$sabreNode1->expects($this->any())
|
||||
->method('getId')
|
||||
->will($this->returnValue(111));
|
||||
$sabreNode1->expects($this->never())
|
||||
->method('getPath');
|
||||
$sabreNode2 = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$sabreNode2->expects($this->any())
|
||||
->method('getId')
|
||||
->will($this->returnValue(222));
|
||||
$sabreNode2->expects($this->never())
|
||||
->method('getPath');
|
||||
|
||||
$sabreNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$sabreNode->expects($this->any())
|
||||
->method('getId')
|
||||
->will($this->returnValue(123));
|
||||
// never, because we use getDirectoryListing from the Node API instead
|
||||
$sabreNode->expects($this->never())
|
||||
->method('getChildren');
|
||||
$sabreNode->expects($this->any())
|
||||
->method('getPath')
|
||||
->will($this->returnValue('/subdir'));
|
||||
|
||||
// node API nodes
|
||||
$node = $this->getMock('\OCP\Files\Folder');
|
||||
$node->expects($this->any())
|
||||
->method('getId')
|
||||
->will($this->returnValue(123));
|
||||
$node1 = $this->getMock('\OCP\Files\File');
|
||||
$node1->expects($this->any())
|
||||
->method('getId')
|
||||
->will($this->returnValue(111));
|
||||
$node2 = $this->getMock('\OCP\Files\File');
|
||||
$node2->expects($this->any())
|
||||
->method('getId')
|
||||
->will($this->returnValue(222));
|
||||
$node->expects($this->once())
|
||||
->method('getDirectoryListing')
|
||||
->will($this->returnValue([$node1, $node2]));
|
||||
|
||||
$this->userFolder->expects($this->once())
|
||||
->method('get')
|
||||
->with('/subdir')
|
||||
->will($this->returnValue($node));
|
||||
|
||||
$this->shareManager->expects($this->any())
|
||||
->method('getSharesBy')
|
||||
->with(
|
||||
$this->equalTo('user1'),
|
||||
$this->anything(),
|
||||
$this->anything(),
|
||||
$this->equalTo(false),
|
||||
$this->equalTo(1)
|
||||
)
|
||||
->will($this->returnCallback(function($userId, $requestedShareType, $node, $flag, $limit) use ($shareTypes){
|
||||
if ($node->getId() === 111 && in_array($requestedShareType, $shareTypes)) {
|
||||
return ['dummyshare'];
|
||||
}
|
||||
|
||||
return [];
|
||||
}));
|
||||
|
||||
// simulate sabre recursive PROPFIND traversal
|
||||
$propFindRoot = new \Sabre\DAV\PropFind(
|
||||
'/subdir',
|
||||
[self::SHARETYPES_PROPERTYNAME],
|
||||
1
|
||||
);
|
||||
$propFind1 = new \Sabre\DAV\PropFind(
|
||||
'/subdir/test.txt',
|
||||
[self::SHARETYPES_PROPERTYNAME],
|
||||
0
|
||||
);
|
||||
$propFind2 = new \Sabre\DAV\PropFind(
|
||||
'/subdir/test2.txt',
|
||||
[self::SHARETYPES_PROPERTYNAME],
|
||||
0
|
||||
);
|
||||
|
||||
$this->plugin->handleGetProperties(
|
||||
$propFindRoot,
|
||||
$sabreNode
|
||||
);
|
||||
$this->plugin->handleGetProperties(
|
||||
$propFind1,
|
||||
$sabreNode1
|
||||
);
|
||||
$this->plugin->handleGetProperties(
|
||||
$propFind2,
|
||||
$sabreNode2
|
||||
);
|
||||
|
||||
$result = $propFind1->getResultForMultiStatus();
|
||||
|
||||
$this->assertEmpty($result[404]);
|
||||
unset($result[404]);
|
||||
$this->assertEquals($shareTypes, $result[200][self::SHARETYPES_PROPERTYNAME]->getShareTypes());
|
||||
}
|
||||
|
||||
function sharesGetPropertiesDataProvider() {
|
||||
return [
|
||||
[[]],
|
||||
[[\OCP\Share::SHARE_TYPE_USER]],
|
||||
[[\OCP\Share::SHARE_TYPE_GROUP]],
|
||||
[[\OCP\Share::SHARE_TYPE_LINK]],
|
||||
[[\OCP\Share::SHARE_TYPE_REMOTE]],
|
||||
[[\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP]],
|
||||
[[\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK]],
|
||||
[[\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_LINK]],
|
||||
[[\OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK]],
|
||||
[[\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_REMOTE]],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -22,21 +22,32 @@ namespace OCA\DAV\Tests\Unit\Migration;
|
||||
|
||||
use OCA\DAV\CardDAV\CardDavBackend;
|
||||
use OCA\Dav\Migration\AddressBookAdapter;
|
||||
use OCP\ILogger;
|
||||
use Test\TestCase;
|
||||
|
||||
class MigrateAddressbookTest extends TestCase {
|
||||
|
||||
public function testMigration() {
|
||||
/** @var AddressBookAdapter | \PHPUnit_Framework_MockObject_MockObject $adapter */
|
||||
$adapter = $this->mockAdapter();
|
||||
$adapter = $this->mockAdapter([
|
||||
['share_type' => '1', 'share_with' => 'users', 'permissions' => '31'],
|
||||
['share_type' => '2', 'share_with' => 'adam', 'permissions' => '1'],
|
||||
]);
|
||||
|
||||
/** @var CardDavBackend | \PHPUnit_Framework_MockObject_MockObject $cardDav */
|
||||
$cardDav = $this->getMockBuilder('\OCA\Dav\CardDAV\CardDAVBackend')->disableOriginalConstructor()->getMock();
|
||||
$cardDav->method('createAddressBook')->willReturn(666);
|
||||
$cardDav->expects($this->any())->method('createAddressBook')->willReturn(666);
|
||||
$cardDav->expects($this->any())->method('getAddressBookById')->willReturn([]);
|
||||
$cardDav->expects($this->once())->method('createAddressBook')->with('principals/users/test01', 'test_contacts');
|
||||
$cardDav->expects($this->once())->method('createCard')->with(666, '63f0dd6c-39d5-44be-9d34-34e7a7441fc2.vcf', 'BEGIN:VCARD');
|
||||
$cardDav->expects($this->once())->method('updateShares')->with($this->anything(), [
|
||||
['href' => 'principal:principals/groups/users', 'readOnly' => false],
|
||||
['href' => 'principal:principals/users/adam', 'readOnly' => true]
|
||||
]);
|
||||
/** @var ILogger $logger */
|
||||
$logger = $this->getMockBuilder('\OCP\ILogger')->disableOriginalConstructor()->getMock();
|
||||
|
||||
$m = new \OCA\Dav\Migration\MigrateAddressbooks($adapter, $cardDav);
|
||||
$m = new \OCA\Dav\Migration\MigrateAddressbooks($adapter, $cardDav, $logger, null);
|
||||
$m->migrateForUser('test01');
|
||||
}
|
||||
|
||||
@@ -45,7 +56,7 @@ class MigrateAddressbookTest extends TestCase {
|
||||
*/
|
||||
private function mockAdapter($shares = []) {
|
||||
$adapter = $this->getMockBuilder('\OCA\Dav\Migration\AddressBookAdapter')->disableOriginalConstructor()->getMock();
|
||||
$adapter->method('foreachBook')->willReturnCallback(function ($user, \Closure $callBack) {
|
||||
$adapter->expects($this->any())->method('foreachBook')->willReturnCallback(function ($user, \Closure $callBack) {
|
||||
$callBack([
|
||||
'id' => 0,
|
||||
'userid' => $user,
|
||||
@@ -56,14 +67,14 @@ class MigrateAddressbookTest extends TestCase {
|
||||
'active' => 1
|
||||
]);
|
||||
});
|
||||
$adapter->method('foreachCard')->willReturnCallback(function ($addressBookId, \Closure $callBack) {
|
||||
$adapter->expects($this->any())->method('foreachCard')->willReturnCallback(function ($addressBookId, \Closure $callBack) {
|
||||
$callBack([
|
||||
'userid' => $addressBookId,
|
||||
'uri' => '63f0dd6c-39d5-44be-9d34-34e7a7441fc2.vcf',
|
||||
'carddata' => 'BEGIN:VCARD'
|
||||
]);
|
||||
});
|
||||
$adapter->method('getShares')->willReturn($shares);
|
||||
$adapter->expects($this->any())->method('getShares')->willReturn($shares);
|
||||
return $adapter;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,32 +22,42 @@ namespace OCA\DAV\Tests\Unit\Migration;
|
||||
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\Dav\Migration\CalendarAdapter;
|
||||
use OCP\ILogger;
|
||||
use Test\TestCase;
|
||||
|
||||
class MigrateCalendarTest extends TestCase {
|
||||
|
||||
public function testMigration() {
|
||||
/** @var CalendarAdapter | \PHPUnit_Framework_MockObject_MockObject $adapter */
|
||||
$adapter = $this->mockAdapter();
|
||||
$adapter = $this->mockAdapter([
|
||||
['share_type' => '1', 'share_with' => 'users', 'permissions' => '31'],
|
||||
['share_type' => '2', 'share_with' => 'adam', 'permissions' => '1'],
|
||||
]);
|
||||
|
||||
/** @var CalDavBackend | \PHPUnit_Framework_MockObject_MockObject $cardDav */
|
||||
$cardDav = $this->getMockBuilder('\OCA\Dav\CalDAV\CalDAVBackend')->disableOriginalConstructor()->getMock();
|
||||
$cardDav->method('createCalendar')->willReturn(666);
|
||||
$cardDav->expects($this->any())->method('createCalendar')->willReturn(666);
|
||||
$cardDav->expects($this->once())->method('createCalendar')->with('principals/users/test01', 'test_contacts');
|
||||
$cardDav->expects($this->once())->method('createCalendarObject')->with(666, '63f0dd6c-39d5-44be-9d34-34e7a7441fc2.ics', 'BEGIN:VCARD');
|
||||
$cardDav->expects($this->once())->method('updateShares')->with($this->anything(), [
|
||||
['href' => 'principal:principals/groups/users', 'readOnly' => false],
|
||||
['href' => 'principal:principals/users/adam', 'readOnly' => true]
|
||||
]);
|
||||
/** @var ILogger $logger */
|
||||
$logger = $this->getMockBuilder('\OCP\ILogger')->disableOriginalConstructor()->getMock();
|
||||
|
||||
$m = new \OCA\Dav\Migration\MigrateCalendars($adapter, $cardDav);
|
||||
$m = new \OCA\Dav\Migration\MigrateCalendars($adapter, $cardDav, $logger, null);
|
||||
$m->migrateForUser('test01');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private function mockAdapter($shares = []) {
|
||||
private function mockAdapter($shares = [], $calData = 'BEGIN:VCARD') {
|
||||
$adapter = $this->getMockBuilder('\OCA\Dav\Migration\CalendarAdapter')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$adapter->method('foreachCalendar')->willReturnCallback(function ($user, \Closure $callBack) {
|
||||
$adapter->expects($this->any())->method('foreachCalendar')->willReturnCallback(function ($user, \Closure $callBack) {
|
||||
$callBack([
|
||||
// calendarorder | calendarcolor | timezone | components
|
||||
'id' => 0,
|
||||
@@ -62,15 +72,14 @@ class MigrateCalendarTest extends TestCase {
|
||||
'components' => 'VEVENT,VTODO,VJOURNAL'
|
||||
]);
|
||||
});
|
||||
$adapter->method('foreachCalendarObject')->willReturnCallback(function ($addressBookId, \Closure $callBack) {
|
||||
$adapter->expects($this->any())->method('foreachCalendarObject')->willReturnCallback(function ($addressBookId, \Closure $callBack) use ($calData) {
|
||||
$callBack([
|
||||
'userid' => $addressBookId,
|
||||
'uri' => '63f0dd6c-39d5-44be-9d34-34e7a7441fc2.ics',
|
||||
'calendardata' => 'BEGIN:VCARD'
|
||||
'calendardata' => $calData
|
||||
]);
|
||||
});
|
||||
$adapter->method('getShares')->willReturn($shares);
|
||||
$adapter->expects($this->any())->method('getShares')->willReturn($shares);
|
||||
return $adapter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ OC.L10N.register(
|
||||
"Enable password recovery:" : "Die Passwort-Wiederherstellung aktivieren:",
|
||||
"Enabling this option will allow you to reobtain access to your encrypted files in case of password loss" : "Durch die Aktivierung dieser Option haben Sie die Möglichkeit, wieder auf Ihre verschlüsselten Dateien zugreifen zu können, wenn Sie Ihr Passwort verloren haben.",
|
||||
"Enabled" : "Aktiviert",
|
||||
"Disabled" : "Deaktiviert"
|
||||
"Disabled" : "Deaktiviert",
|
||||
"one-time password for server-side-encryption" : "Einmalpasswort für Serverseitige Verschlüsselung"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user