Compare commits

...

186 Commits

Author SHA1 Message Date
blizzz
684bd274b8 Merge pull request #34468 from nextcloud/release/25.0.0_rc4
25.0.0 RC4
2022-10-13 20:51:47 +02:00
Pytal
7f498d0fa0 Merge pull request #34587 from nextcloud/backport/34461/stable25
[stable25] Fix missing background on upgrade
2022-10-13 11:26:44 -07:00
John Molakvoæ
7c4a5b0dd1 Merge pull request #34588 from nextcloud/stable25-theming-default-system-value
[stable25]  Use default system primary
2022-10-13 20:16:56 +02:00
John Molakvoæ
08227ca28d Adjust testing
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2022-10-13 18:04:01 +02:00
Arthur Schiwon
1309459fee graceful background image handling
- fallback to background image from old location
- migrate background images to new location as insensitive job

Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
2022-10-13 18:01:48 +02:00
Joas Schilling
37cca49630 Fix migration parameter handling
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-10-13 18:01:43 +02:00
Arthur Schiwon
982534349a fix querybuilder instance recuse
...and execute delete query

Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
2022-10-13 18:01:38 +02:00
Christopher Ng
61d2937bf4 Delete theming background preferences
Signed-off-by: Christopher Ng <chrng8@gmail.com>
2022-10-13 18:01:34 +02:00
Joas Schilling
5c7613fd67 New code being new
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-10-13 18:01:28 +02:00
Joas Schilling
10cf013e19 Fix SQL errors
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-10-13 18:01:15 +02:00
Christopher Ng
df218bc4db Fix missing background on upgrade
Signed-off-by: Christopher Ng <chrng8@gmail.com>
2022-10-13 18:01:05 +02:00
John Molakvoæ
f17f24ffad Fix primary and debounce to avoid infinite loop
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
2022-10-13 18:00:01 +02:00
John Molakvoæ (skjnldsv)
b3832bcd05 Allow to pick custom colour
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
2022-10-13 17:59:57 +02:00
John Molakvoæ (skjnldsv)
a7211e6ab0 Use default system primary
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
2022-10-13 17:59:48 +02:00
Simon L
faab6c8072 Merge pull request #34573 from nextcloud/backport/34571/stable25
[stable25] Fix avatar menu icons in darkmode
2022-10-13 14:28:54 +02:00
Julius Härtl
ae6a8d5cf5 Merge pull request #34568 from nextcloud/backport/34544/stable25
[stable25] More flexible date validation
2022-10-13 11:47:33 +02:00
Joas Schilling
6c47b26102 Fix avatar menu icons in darkmode
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-10-13 09:24:04 +00:00
blizzz
2a42dd4f4e Merge pull request #34564 from nextcloud/backport/34559/stable25
[stable25] Require token for local editing
2022-10-13 10:07:05 +02:00
Vincent Petry
1fb7567294 Merge pull request #34566 from nextcloud/backport/34558/stable25
[stable25] fix reading updater token for web updater
2022-10-13 09:21:26 +02:00
Simon L
1360771be8 Merge pull request #34567 from nextcloud/backport/34545/stable25
[stable25] Disable translucency on high contrast themes
2022-10-13 08:02:23 +02:00
Joas Schilling
7da871437b Bump version
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-10-13 07:45:15 +02:00
Carl Schwan
1dd634c0e6 More flexible date validation
Fix #34542

Signed-off-by: Carl Schwan <carl@carlschwan.eu>
2022-10-12 23:14:24 +00:00
szaimen
5e2c5e5fb6 fix color-main-background-rgb
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-10-12 23:02:01 +00:00
Carl Schwan
c2510a481e Disable translucency on high contrast themes
Fix #34514

Signed-off-by: Carl Schwan <carl@carlschwan.eu>
2022-10-12 23:02:01 +00:00
blizzz
39938297ee Merge pull request #34459 from nextcloud/backport/31947/stable25
[stable25] Fix Error: Undefined index: redirect_url
2022-10-12 23:02:20 +02:00
Arthur Schiwon
267f3896ae fix reading updater token for web updater
the previously called endpoint returns exactly one value, no JSON
structure

Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
2022-10-12 20:34:22 +00:00
Joas Schilling
be005877ff Also throttle on expiration
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-10-12 17:52:13 +00:00
Joas Schilling
c48b15fb70 Fix psalm error on migration
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-10-12 17:52:13 +00:00
Joas Schilling
ec230f9290 Add a background job to delete expired tokens
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-10-12 17:52:13 +00:00
Joas Schilling
1db6d96e94 Also remove token when we successfully validated
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-10-12 17:52:13 +00:00
Joas Schilling
1d02efdd8b Require a token for "Edit locally"
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-10-12 17:52:13 +00:00
Vincent Petry
8e89c281a5 Merge pull request #34551 from nextcloud/backport/34537/stable25
[stable25] Avoid allocating too much memory for the buffer on s3 uploads
2022-10-12 14:49:35 +02:00
Vincent Petry
ad0405a18b Merge pull request #34547 from nextcloud/backport/34299/stable25
[stable25] Add color variables for text on blurred background
2022-10-12 12:49:16 +02:00
Julius Härtl
775e4e023d Avoid allocating too much memory for the buffer
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-10-12 08:30:58 +00:00
Simon L
be7b7f5d13 Merge pull request #34543 from nextcloud/backport/34473/stable25
[stable25] Fix excessive increase of cachebuster value
2022-10-12 07:54:14 +02:00
Christopher Ng
8caffb784f Add color variables for text on blurred background
Signed-off-by: Christopher Ng <chrng8@gmail.com>
2022-10-12 01:50:17 +00:00
blizzz
ce7bfc6b33 Merge pull request #34531 from nextcloud/backport/34500/stable25
[stable25] Fix password length limitation
2022-10-11 23:31:44 +02:00
blizzz
c98334fdac Merge pull request #34446 from nextcloud/backport/34401/stable25
[stable25] File reference: fallback icon to file type if no preview
2022-10-11 23:30:37 +02:00
Christopher Ng
f477a22404 Fix excessive increase of cachebuster value
Signed-off-by: Christopher Ng <chrng8@gmail.com>
2022-10-11 19:13:55 +00:00
Joas Schilling
cecd778c2b Fix password length limitation
Signed-off-by: Joas Schilling <coding@schilljs.com>
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2022-10-11 16:55:20 +00:00
Simon L
b9b201cc9a Merge pull request #34524 from nextcloud/backport/34513/stable25
[stable25] do not show editlocally on mobile
2022-10-11 16:19:33 +02:00
Joas Schilling
b885e44dde Merge pull request #34526 from nextcloud/backport/34522/stable25
[stable25] Update security.txt expires field
2022-10-11 15:06:58 +02:00
Joas Schilling
4beffc4209 Merge pull request #34525 from nextcloud/backport/34517/stable25
[stable25] Update ca-cert bundle
2022-10-11 12:52:42 +02:00
Joas Schilling
4cbfeec38e Update security.txt expires field
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-10-11 08:07:39 +00:00
nextcloud-command
66b1cf3a19 Update CA certificate bundle
Signed-off-by: GitHub <noreply@github.com>
2022-10-11 08:07:37 +00:00
szaimen
873ba714ae do not show editlocally on mobile
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-10-11 08:04:49 +00:00
blizzz
9068bf5788 Merge pull request #34505 from nextcloud/backport/30608/stable25
[stable25] Harden disk_free_space check in CheckSetupController
2022-10-10 23:28:46 +02:00
blizzz
0644eab947 Merge pull request #34453 from nextcloud/backport/33737/stable25
[stable25] Makes untrusted domain error on info
2022-10-10 23:28:01 +02:00
Simon L
116897fc90 Merge pull request #34494 from nextcloud/backport/33915/stable25
[stable25] Replace vue2-datepicker with native date picker for share expiration dates
2022-10-10 22:39:10 +02:00
acsfer
061833b83d Harden some PHP functions
To avoid things like https://github.com/nextcloud/server/issues/26034

Signed-off-by: Louis Chemineau <louis@chmn.me>
2022-10-10 18:44:52 +00:00
julia.kirschenheuter
e50f95eab6 Replace moment.js date with Date Object.
Replace vue2-datepicker with native date picker for expiration date.

Signed-off-by: julia.kirschenheuter <julia.kirschenheuter@nextcloud.com>
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2022-10-10 07:37:15 +00:00
Arthur Schiwon
8fcb1c1d76 25.0.0 RC4
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
2022-10-07 12:44:57 +02:00
Andy Xheli
263e3e829e Fix Error: Undefined index: redirect_url
Signed-off-by: Andy Xheli <axheli@axtsolutions.com>

Should fix Undefined index: redirect_url at core/templates/twofactorsetupselection.php #21968
2022-10-06 21:57:24 +00:00
Andy Xheli
1921685e4e Makes untrusted domain error on info
Signed-off-by: Andy Xheli <axheli@axtsolutions.com>

Since e6d9ef2e38 was applied logs get filled up with Trusted domain error. "X.X.X.X tried to access using "X.X.X.X" as host alot of users missed important errors do toe6d9ef2e38   please see https://github.com/nextcloud/server/issues/32599

This should fix. 
https://github.com/nextcloud/server/issues/32599#event-7281164903


Signed-off-by: Andy Xheli <axheli@axtsolutions.com>
2022-10-06 16:28:30 +00:00
blizzz
402143d4e8 Merge pull request #34394 from nextcloud/release/25.0.0_rc3
25.0.0 RC3
2022-10-06 14:11:40 +02:00
Julien Veyssier
4807c23e09 File reference: use the file type icon as fallback when no preview provider for this type
Signed-off-by: Julien Veyssier <eneiluj@posteo.net>
2022-10-06 04:33:51 +00:00
Simon L
0c6b5cde95 Merge pull request #34432 from nextcloud/backport/34298/stable25
[stable25] Use color preset of shipped background as primary color
2022-10-05 13:18:41 +02:00
blizzz
ab959249d3 Merge pull request #34434 from nextcloud/backport/32635/stable25
[stable25] Fix User profile picture when performing the search
2022-10-05 13:10:55 +02:00
blizzz
c4a6f5c7b2 Merge pull request #34370 from nextcloud/backport/34364/stable25
[stable25] also use updatedir for cleanup of backups
2022-10-05 13:08:46 +02:00
blizzz
0612e008d4 Merge pull request #34405 from nextcloud/backport/34302/stable25
[stable25] Fix: Prevent deadlocks during mtime/size/etag propagation
2022-10-05 12:52:29 +02:00
Andy Xheli
47e950dbee Update ContactsStoreTest.php
Signed-off-by: Andy Xheli <axheli@axtsolutions.com>
2022-10-05 08:42:09 +00:00
Andy Xheli
36473a3f63 Update lib/private/Contacts/ContactsMenu/ContactsStore.php
Signed-off-by: Andy Xheli <axheli@axtsolutions.com>

Tested no issues.

Co-authored-by: Pytal <24800714+Pytal@users.noreply.github.com>
Signed-off-by: Andy Xheli <axheli@axtsolutions.com>
2022-10-05 08:42:09 +00:00
Andy Xheli
f4fc7daf73 Update ContactsStore.php
Signed-off-by: Andy Xheli <axheli@axtsolutions.com>
2022-10-05 08:42:08 +00:00
Andy Xheli
31e5e102c1 Apply suggestions from code review
Signed-off-by: Andy Xheli <axheli@axtsolutions.com>

Co-authored-by: Carl Schwan <carl@carlschwan.eu>
Signed-off-by: Andy Xheli <axheli@axtsolutions.com>
2022-10-05 08:42:08 +00:00
Andy Xheli
511ade9497 Fix User profile picture when performing the search
Signed-off-by: Andy Xheli <axheli@axtsolutions.com>

Before 
![image](https://user-images.githubusercontent.com/59488153/140980158-b9108161-57ab-48b4-ae6f-98ec4e72775a.png)

After




Fix for #31065
2022-10-05 08:42:07 +00:00
Christopher Ng
d1dc38dc1f Use brand color for background only and keep accessible color as color primary
Signed-off-by: Christopher Ng <chrng8@gmail.com>
2022-10-05 08:03:16 +00:00
Christopher Ng
fad56d683c Use color preset of shipped background as primary color
Signed-off-by: Christopher Ng <chrng8@gmail.com>
2022-10-05 08:03:16 +00:00
blizzz
c765dac633 Merge pull request #34409 from nextcloud/backport/33566/stable25
[stable25] trigger a rescan when trying to fopen a file that exists in cache but not on disk
2022-10-04 12:43:10 +02:00
blizzz
bbd972b2ca Merge pull request #34413 from nextcloud/backport/34350/stable25
[stable25] Add a capability for the reference API and expose the regex to clients
2022-10-03 21:28:08 +02:00
Julius Härtl
a4bfea7f37 Merge pull request #34408 from nextcloud/backport/34396/stable25
[stable25] Fix positioning of the public page footer
2022-10-03 21:22:35 +02:00
Joas Schilling
7b841c060d Add a capability for the reference API and expose the regex to clients
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-10-03 16:27:05 +00:00
Robin Appelman
0a2744a998 trigger a rescan when trying to fopen a file that exists in cache but not on disk
Signed-off-by: Robin Appelman <robin@icewind.nl>
2022-10-03 15:28:30 +00:00
Robin Appelman
c26e17104c add test for trying to fopen a file which no longer exists on disk
Signed-off-by: Robin Appelman <robin@icewind.nl>
2022-10-03 15:28:29 +00:00
szaimen
1f0245e68f fix footer
Signed-off-by: szaimen <szaimen@e.mail.de>
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2022-10-03 15:12:33 +00:00
Julius Härtl
a3febe2a40 Fix positioning of the public page footer
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-10-03 15:12:33 +00:00
raul
7ed5679f2c Fix unencrypted size calculation for files created before the Encryption storage is enabled
Signed-off-by: raul <raul@nextcloud.com>
2022-10-03 14:29:01 +00:00
raul
380aaef96e Fix: Prevent deadlocks during mtime/size/etag propagation
Signed-off-by: raul <raul@nextcloud.com>
2022-10-03 14:29:01 +00:00
blizzz
1b4c82156d Merge pull request #34382 from nextcloud/backport/34339/stable25
[stable25] Fix URLs on reference resolving
2022-10-03 11:18:26 +02:00
blizzz
07ac47812b Merge pull request #34380 from nextcloud/backport/34289/stable25
[stable25] Url encode the web dav url for copying
2022-10-03 11:18:03 +02:00
blizzz
46180dd9f3 Merge pull request #34374 from nextcloud/backport/34219/stable25
[stable25] Add fallback options for contacts array
2022-10-03 11:17:38 +02:00
blizzz
d847a89436 Merge pull request #34371 from nextcloud/backport/34343/stable25
[stable25] Fix missing maintenance mode header for OCS request
2022-10-03 11:16:50 +02:00
blizzz
eafcc4e3a1 Merge pull request #34357 from nextcloud/backport/34353/stable25
[stable25] Reference url can always fallback to the link itself
2022-10-03 11:15:39 +02:00
Arthur Schiwon
51c249d9ab 25.0.0 RC3
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
2022-10-03 11:07:20 +02:00
Anna Larch
b1617bca39 Add fallback options for contacts array
when the UID or FN is null, the email address / dsiplay name will be used instead

Signed-off-by: Anna Larch <anna@nextcloud.com>
2022-10-02 12:44:04 +00:00
Joas Schilling
14a59c461b Fix URLs on reference resolving
The vue-richtext app currently sends leading spaces if they are in the text.

Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-10-02 12:43:22 +00:00
Anna Larch
a2d8bd4c11 Url encode the web dav url for copying
Signed-off-by: Anna Larch <anna@nextcloud.com>
2022-10-02 12:32:25 +00:00
Simon L
115058720a Merge pull request #34373 from nextcloud/backport/34331/stable25
[stable25] fix public view
2022-10-02 11:17:09 +02:00
nextcloud-command
65144258d2 Compile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2022-10-01 22:24:53 +00:00
szaimen
7b2f226ce6 fix public view - attempt 2
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-10-01 22:24:53 +00:00
Christoph Wurst
ac5ec1af7d Fix missing maintenance mode header for OCS request
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
2022-10-01 19:46:34 +00:00
blizzz
34ca126b37 Merge pull request #34365 from nextcloud/backport/34363/stable25
[stable25] Update OpenDyslexia to v0.91.12
2022-10-01 21:39:23 +02:00
blizzz
02d07e14b3 Merge pull request #34354 from nextcloud/backport/34348/stable25
[stable25] Fix renaming in shared with you file list
2022-10-01 20:08:40 +02:00
Arthur Schiwon
61def5e276 also use updatedir for cleanup of backups
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
2022-10-01 17:29:44 +00:00
John Molakvoæ
ffecfc6441 Merge pull request #34367 from nextcloud/backport/34323/stable25
[stable25] Update theming screenshots
2022-10-01 18:22:24 +02:00
Simon L
94b86b22ce Merge pull request #34356 from nextcloud/backport/34333/stable25
[stable25] fix the tertiary button text
2022-10-01 18:15:55 +02:00
John Molakvoæ (skjnldsv)
cfd01baf04 Update theming screenshots
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
2022-10-01 15:47:36 +00:00
Simon L
036af3556c Merge pull request #34332 from nextcloud/backport/34327/stable25
[stable25] Fix starred favourite icon
2022-10-01 17:45:13 +02:00
Joas Schilling
a3b96282ed Update opendyslexia
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-10-01 15:24:55 +00:00
Julien Veyssier
56abd79050 reference url can always fallback to the link itself
Signed-off-by: Julien Veyssier <eneiluj@posteo.net>
2022-09-30 22:47:07 +00:00
szaimen
5267da34b1 fix test
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-09-30 18:37:17 +00:00
szaimen
446e495c59 fix the tertiary button text
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-09-30 18:37:17 +00:00
Vincent Petry
cae61e00f6 Fix renaming in shared with you file list
Signed-off-by: Vincent Petry <vincent@nextcloud.com>
2022-09-30 17:56:21 +00:00
blizzz
5660589ca9 Merge pull request #34340 from nextcloud/backport/34328/stable25
[stable25] Correctly handle Redis::keys returning false
2022-09-30 12:55:30 +02:00
Côme Chilliet
8aed08e058 Correctly handle Redis::keys returning false
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
2022-09-30 08:19:15 +00:00
Simon L
f2d7d13579 Merge pull request #34335 from nextcloud/backport/34321/stable25
[stable25] Fix invisible status
2022-09-30 09:18:11 +02:00
Joas Schilling
4dea095a76 Merge pull request #34337 from nextcloud/backport/34322/stable25
[stable25] Only match http(s) URLs for references
2022-09-30 09:13:09 +02:00
Joas Schilling
2ea723d1e1 Adjust tests
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-09-30 06:11:46 +00:00
Joas Schilling
019ffa2ec0 Only match http(s) URLs for references
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-09-30 06:11:45 +00:00
nextcloud-command
885b55b7af Compile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2022-09-29 21:09:21 +00:00
John Molakvoæ (skjnldsv)
467a0eaee2 Fix invisible status
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
2022-09-29 21:09:21 +00:00
John Molakvoæ (skjnldsv)
77da30a906 Fix starred favourite icon
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
2022-09-29 14:23:25 +00:00
blizzz
ce1dddcc64 Merge pull request #34276 from nextcloud/release/25.0.0_rc2
25.0.0 RC2
2022-09-29 15:20:44 +02:00
blizzz
b84eb26f72 Merge pull request #34325 from nextcloud/backport/34313/stable25
[stable25] unbundle files_videoplayer and clean up a little
2022-09-29 15:19:30 +02:00
blizzz
b6fd615bf7 Merge pull request #34320 from nextcloud/backport/34308/stable25
[stable25] Fix user status emoji picking
2022-09-29 13:49:26 +02:00
Arthur Schiwon
68284b87f2 unbundle files_videoplayer and clean up a little
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
2022-09-29 11:27:33 +00:00
blizzz
904ac63310 Merge pull request #34316 from nextcloud/backport/34215/stable25
[stable25] Add cache header for image endpoint if link previews
2022-09-29 12:16:56 +02:00
blizzz
1f27bd153b Merge pull request #34310 from nextcloud/backport/34306/stable25
[stable25] fix default value for updatedirectory setting
2022-09-29 12:13:36 +02:00
nextcloud-command
92a4ce9f3b Compile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2022-09-28 23:25:51 +00:00
John Molakvoæ (skjnldsv)
729b78708f Fix user status emoji picking
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
2022-09-28 23:25:50 +00:00
Simon L
8b34878771 Merge pull request #34319 from nextcloud/backport/34311/stable25
[stable25] Remove unwanted settings button background
2022-09-29 01:21:41 +02:00
John Molakvoæ (skjnldsv)
a5c5740e84 Remove unwanted settings button background
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2022-09-28 22:38:22 +00:00
blizzz
ab51e4ef24 Merge pull request #34317 from nextcloud/backport/34287/stable25
[stable25] fix highcontrast theme
2022-09-28 21:53:04 +02:00
blizzz
d442a3c1be Merge pull request #34315 from nextcloud/backport/34204/stable25
[stable25] Fix various theming bugs
2022-09-28 21:52:27 +02:00
szaimen
6ac50c26ef fix highcontrast theme icons
Signed-off-by: szaimen <szaimen@e.mail.de>
2022-09-28 18:38:24 +00:00
Julius Härtl
4aeb701b87 Add cache header for image endpoint if link previews
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-09-28 18:29:45 +00:00
Christopher Ng
9c073fd760 Fix various theming bugs
Signed-off-by: Christopher Ng <chrng8@gmail.com>
2022-09-28 18:01:41 +00:00
Arthur Schiwon
413610ede4 fix default value for updatedirectory setting
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
2022-09-28 13:56:48 +00:00
blizzz
c12f26867d Merge pull request #34285 from nextcloud/backport/34263/stable25
[stable25] Add endpoint to fetch a cachable reference data
2022-09-28 10:23:37 +02:00
blizzz
847535ee88 Merge pull request #34295 from nextcloud/backport/34288/stable25
[stable25] Fix focussing to internal link after copy
2022-09-27 23:20:22 +02:00
blizzz
d9c07af32a Merge pull request #34294 from nextcloud/backport/34292/stable25
[stable25] Fix grid view button in public page
2022-09-27 23:19:01 +02:00
blizzz
e85870afa7 Merge pull request #34293 from nextcloud/backport/34272/stable25
[stable25] Add primary color presets for shipped backgrounds
2022-09-27 23:18:28 +02:00
blizzz
7bdaa9fa04 Merge pull request #34286 from nextcloud/backport/34134/stable25
[stable25] l10n: Fix text string
2022-09-27 23:17:49 +02:00
blizzz
9c21bda303 Merge pull request #34221 from nextcloud/backport/34212/stable25
[stable25] Fix loading custom background
2022-09-27 23:08:13 +02:00
Vincent Petry
3bb4d01a7c Update apps/files_sharing/src/components/SharingEntryInternal.vue
Co-authored-by: Joas Schilling <213943+nickvergessen@users.noreply.github.com>
Signed-off-by: Vincent Petry <vincent@nextcloud.com>
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2022-09-27 17:11:17 +00:00
Vincent Petry
99cb56cda2 Fix focussing to internal link after copy
Since a recent change to NcActions it's not possible to add a "ref"
attribute on a NcAction* component.

As a workaround, a ref was added on the NcActions parent component
instead.

Signed-off-by: Vincent Petry <vincent@nextcloud.com>
2022-09-27 17:11:16 +00:00
Vincent Petry
a85d1cad6a Fix grid view button in public page
Signed-off-by: Vincent Petry <vincent@nextcloud.com>
2022-09-27 17:07:09 +00:00
Christopher Ng
6965d4b4af Add preset primary colors for shipped backgrounds
Signed-off-by: Christopher Ng <chrng8@gmail.com>
2022-09-27 16:24:52 +00:00
Joas Schilling
4441416245 Merge pull request #34213 from nextcloud/backport/34191/stable25
[stable25] DashBoard : Allow up to 5 widgets side by side
2022-09-27 17:10:29 +02:00
blizzz
96423a1172 Merge pull request #34251 from nextcloud/backport/34222/stable25
[stable25] Fix install page
2022-09-27 16:58:17 +02:00
Joas Schilling
2d86948c34 Merge pull request #34270 from nextcloud/backport/34260/stable25
[stable25] Remove unneeded and problematic vue-cli-plugin-unit-test
2022-09-27 12:34:59 +02:00
Valdnet
dbc421580e l10n: Correct text string
Signed-off-by: Valdnet <47037905+Valdnet@users.noreply.github.com>
2022-09-27 09:58:44 +00:00
Julius Härtl
89f8179e64 Add endpoint to fetch a cachable reference data
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-09-27 09:58:13 +00:00
Vincent Petry
bc9f3d3d4c Remove unneeded and problematic vue-cli-plugin-unit-test
It was causing issues during dependency upgrades and wasn't really used
except for its preset definition.

Signed-off-by: Vincent Petry <vincent@nextcloud.com>
2022-09-27 10:45:00 +02:00
Vincent Petry
9fe747f2c5 Fix loading custom background
Fix the route for loading the custom background.
Because the controller is UserThemeController, so the route path is
not "theming" but "userTheme".

Signed-off-by: Vincent Petry <vincent@nextcloud.com>
2022-09-27 08:24:41 +00:00
Vincent Petry
6f8b3916ad Merge pull request #34257 from nextcloud/backport/34247/stable25
[stable25] Bump nth-check and @vue/cli-service
2022-09-27 09:14:32 +02:00
Arthur Schiwon
82e9af439e 25.0.0 RC2
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
2022-09-27 08:25:00 +02:00
Vincent Petry
c471c8d6de Merge pull request #34268 from nextcloud/dependabot/npm_and_yarn/stable25/moment-2.29.4
[stable25] Bump moment from 2.29.3 to 2.29.4
2022-09-26 21:28:26 +02:00
Vincent Petry
42ca267d83 Fix install page
- Use normal labels instead of hiding them
- Fix position of the eye button (this is still very hacky)
- Define width on the box and not on the text fields

This really needs to be ported to vue

Signed-off-by: Carl Schwan <carl@carlschwan.eu>
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2022-09-26 19:01:35 +00:00
Joas Schilling
981da3d49f Recompile
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-09-26 19:32:19 +02:00
Joas Schilling
7083596dc1 Bump moment from 2.29.3 to 2.29.4
Bumps [moment](https://github.com/moment/moment) from 2.29.3 to 2.29.4.
- [Release notes](https://github.com/moment/moment/releases)
- [Changelog](https://github.com/moment/moment/blob/develop/CHANGELOG.md)
- [Commits](moment/moment@2.29.3...2.29.4)

---
updated-dependencies:
- dependency-name: moment
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-09-26 19:31:55 +02:00
Joas Schilling
ac2e94f121 Merge pull request #34258 from nextcloud/backport/33887/stable25
[stable25] Bump moment-timezone from 0.5.34 to 0.5.37
2022-09-26 19:22:40 +02:00
Joas Schilling
de295ba8dd Recompile
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-09-26 15:21:20 +02:00
Joas Schilling
5d078383b6 Merge pull request #34237 from nextcloud/backport/34220/stable25
[stable25] Make phpcs happy and remove unused setup thing
2022-09-26 15:07:48 +02:00
Joas Schilling
e938bbec19 Merge pull request #34253 from nextcloud/backport/34249/stable25
[stable25] Move Reference class to public namespace
2022-09-26 15:07:32 +02:00
Joas Schilling
1f0f227b43 Bump moment-timezone from 0.5.34 to 0.5.37
Bumps [moment-timezone](https://github.com/moment/moment-timezone) from 0.5.34 to 0.5.37.
- [Release notes](https://github.com/moment/moment-timezone/releases)
- [Changelog](https://github.com/moment/moment-timezone/blob/develop/changelog.md)
- [Commits](https://github.com/moment/moment-timezone/compare/0.5.34...0.5.37)

---
updated-dependencies:
- dependency-name: moment-timezone
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-09-26 12:42:41 +00:00
dependabot[bot]
b2fa292ee8 Bump nth-check and @vue/cli-service
Bumps [nth-check](https://github.com/fb55/nth-check) and [@vue/cli-service](https://github.com/vuejs/vue-cli/tree/HEAD/packages/@vue/cli-service). These dependencies needed to be updated together.

Updates `nth-check` from 1.0.2 to 2.1.1
- [Release notes](https://github.com/fb55/nth-check/releases)
- [Commits](https://github.com/fb55/nth-check/compare/v1.0.2...v2.1.1)

Updates `@vue/cli-service` from 4.5.17 to 5.0.8
- [Release notes](https://github.com/vuejs/vue-cli/releases)
- [Changelog](https://github.com/vuejs/vue-cli/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/vuejs/vue-cli/commits/v5.0.8/packages/@vue/cli-service)

---
updated-dependencies:
- dependency-name: nth-check
  dependency-type: indirect
- dependency-name: "@vue/cli-service"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-26 12:40:09 +00:00
Joas Schilling
cc6b12a166 Add since
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-09-26 10:44:24 +00:00
Julius Härtl
b45d342654 Move Reference class to public namespace
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-09-26 10:44:24 +00:00
Jérôme Herbinet
e43814f74d DashBoard : Allow up to 5 widgets side by side
Signed-off-by: Jérôme Herbinet <j.herbinet@protonmail.ch>

Signed-off-by: Jérôme Herbinet <33763786+Jerome-Herbinet@users.noreply.github.com>
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2022-09-26 06:07:05 +00:00
Joas Schilling
611b920e3d Merge pull request #34236 from nextcloud/backport/34210/stable25
[stable25] Fixing missing theming variables
2022-09-26 06:38:47 +02:00
Joas Schilling
c25d61c7b8 Fix CS completely
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-09-26 06:38:01 +02:00
Joas Schilling
4be6d23d00 Merge pull request #34187 from nextcloud/bugfix/noid/fix-talk-testing
[stable25] Fix Talk checkout in video verification test
2022-09-26 06:13:03 +02:00
Vincent Petry
f2bd15229a Make phpcs happy and remove unused setup thing
Signed-off-by: Vincent Petry <vincent@nextcloud.com>
2022-09-24 05:57:45 +00:00
John Molakvoæ
a703818ee7 Refactor primary computation and fix a few missing theme parity variables
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
2022-09-24 05:38:47 +00:00
John Molakvoæ
5ee61a5fc6 Disable background blur on highcontrast
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
2022-09-24 05:38:47 +00:00
John Molakvoæ
019e85d92c Fixing dark primary element variables
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
2022-09-24 05:38:47 +00:00
blizzz
1fae3ae4ae Merge pull request #34185 from nextcloud/release/25.0.0_rc_1
25.0.0 RC 1
2022-09-22 18:12:42 +02:00
blizzz
c6c4328e2a Merge pull request #34196 from nextcloud/backport/34190/stable25
[stable25] remove CoreBundle as we now ship the lat app that was referenced there
2022-09-22 18:12:26 +02:00
Arthur Schiwon
aff9302638 update autoloader
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
2022-09-22 17:36:13 +02:00
Arthur Schiwon
1115199ae3 remove getDefaultInstallationBundle
- because all apps are shipped now it was returning an empty result

Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
2022-09-22 17:36:09 +02:00
Arthur Schiwon
86b787d21e remove InstallCoreBundle repair step
- core bundle was empty and thus removed
- all former apps installed this way are shipped

Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
2022-09-22 17:36:05 +02:00
Arthur Schiwon
359a37fd10 remove Core Bundle
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
2022-09-22 17:36:00 +02:00
blizzz
079a8c0e0f Merge pull request #34192 from nextcloud/move-background-clearing-stable25
[stable25] Move avatar clearing in the background
2022-09-22 17:33:43 +02:00
Vincent Petry
42bc4a0b2a Merge pull request #34195 from nextcloud/backport/34160/stable25
[stable25] Detect weird local ips
2022-09-22 17:32:22 +02:00
Côme Chilliet
205760a3aa Fix idn_to_utf8 stub signature
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
2022-09-22 16:39:58 +02:00
Côme Chilliet
2948697257 Update 3rdparty to master
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
2022-09-22 16:39:50 +02:00
Côme Chilliet
3c47caf08b Fix tests for nested v4 in v6
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
2022-09-22 16:39:42 +02:00
Côme Chilliet
515e05cf16 Use new dependency to normalize IPs
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
2022-09-22 16:39:34 +02:00
Côme Chilliet
060230eec7 Add mlocati/ip-lib dependency
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
2022-09-22 16:39:22 +02:00
Côme Chilliet
b2a893abad Add missing urldecode and idn_to_utf8 calls to local address checker
The call to idn_to_utf8 call is actually to apply normalization

Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
2022-09-22 16:39:14 +02:00
Côme Chilliet
95dcc610fc Harden tests for local IP detection in URLs
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
2022-09-22 16:38:57 +02:00
Carl Schwan
a5a8f4e9ef Move avatar clearing in the background
Signed-off-by: Carl Schwan <carl@carlschwan.eu>
(cherry picked from commit e21e53670c60b74ca2c49c01bf510ccdc5115b0d)
2022-09-22 15:42:13 +02:00
blizzz
9366ec0fb8 Merge pull request #34184 from nextcloud/backport/34183/stable25
[stable25] Bumped shipped apps list
2022-09-22 12:56:07 +02:00
Joas Schilling
bfaa31af61 Fix Talk checkout in video verification test
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-09-22 11:49:28 +02:00
Arthur Schiwon
063aac8ebc 25.0.0 RC 1
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
2022-09-22 11:21:54 +02:00
Joas Schilling
773826f9e1 Bumped shipped apps list
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-09-22 11:21:09 +02:00
182 changed files with 7727 additions and 29581 deletions

View File

@@ -1214,7 +1214,7 @@ steps:
commands:
# JavaScript files are not used in integration tests so it is not needed to
# build them.
- git clone --depth 1 https://github.com/nextcloud/spreed apps/spreed
- git clone --depth 1 --branch stable25 https://github.com/nextcloud/spreed apps/spreed
- name: integration-sharing-v1-video-verification
image: ghcr.io/nextcloud/continuous-integration-integration-php7.4:latest
commands:

View File

@@ -88,7 +88,7 @@
</template>
<script>
import { generateUrl, imagePath } from '@nextcloud/router'
import { generateUrl } from '@nextcloud/router'
import { getCurrentUser } from '@nextcloud/auth'
import { loadState } from '@nextcloud/initial-state'
import axios from '@nextcloud/axios'
@@ -99,13 +99,11 @@ import Pencil from 'vue-material-design-icons/Pencil.vue'
import Vue from 'vue'
import isMobile from './mixins/isMobile.js'
import { getBackgroundUrl } from './helpers/getBackgroundUrl.js'
const panels = loadState('dashboard', 'panels')
const firstRun = loadState('dashboard', 'firstRun')
const background = loadState('theming', 'background')
const backgroundVersion = loadState('theming', 'backgroundVersion')
const themingDefaultBackground = loadState('theming', 'themingDefaultBackground')
const shippedBackgroundList = loadState('theming', 'shippedBackgrounds')
@@ -155,19 +153,6 @@ export default {
}
},
computed: {
backgroundImage() {
return getBackgroundUrl(this.background, backgroundVersion, this.themingDefaultBackground)
},
backgroundStyle() {
if ((this.background === 'default' && this.themingDefaultBackground === 'backgroundColor')
|| this.background.match(/#[0-9A-Fa-f]{6}/g)) {
return null
}
return {
backgroundImage: this.background === 'default' ? 'var(--image-main-background)' : `url('${this.backgroundImage}')`,
}
},
greeting() {
const time = this.timer.getHours()
@@ -286,17 +271,6 @@ export default {
// document.body.removeAttribute('data-theme-light')
// document.body.setAttribute('data-theme-dark', 'true')
}
const themeElements = [document.documentElement, document.querySelector('#header'), document.querySelector('body')]
for (const element of themeElements) {
if (this.background === 'default') {
element.style.setProperty('--image-main-background', `url('${imagePath('core', 'app-background.jpg')}')`)
} else if (this.background.match(/#[0-9A-Fa-f]{6}/g)) {
element.style.setProperty('--image-main-background', undefined)
} else {
element.style.setProperty('--image-main-background', this.backgroundStyle.backgroundImage)
}
}
},
/**
* Method to register panels that will be called by the integrating apps
@@ -441,7 +415,7 @@ export default {
.panels {
width: auto;
margin: auto;
max-width: 1500px;
max-width: 1800px;
display: flex;
justify-content: center;
flex-direction: row;

View File

@@ -1,49 +0,0 @@
/**
* @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net>
*
* @author Avior <florian.bouillon@delta-wings.net>
* @author Julien Veyssier <eneiluj@posteo.net>
* @author Julius Härtl <jus@bitgrid.net>
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
import { generateUrl } from '@nextcloud/router'
import { prefixWithBaseUrl } from './prefixWithBaseUrl.js'
export const getBackgroundUrl = (background, time = 0, themingDefaultBackground = '') => {
const enabledThemes = window.OCA?.Theming?.enabledThemes || []
const isDarkTheme = (enabledThemes.length === 0 || enabledThemes[0] === 'default')
? window.matchMedia('(prefers-color-scheme: dark)').matches
: enabledThemes.join('').indexOf('dark') !== -1
if (background === 'default') {
if (themingDefaultBackground && themingDefaultBackground !== 'backgroundColor') {
return generateUrl('/apps/theming/image/background') + '?v=' + window.OCA.Theming.cacheBuster
}
if (isDarkTheme) {
return prefixWithBaseUrl('eduardo-neves-pedra-azul.jpg')
}
return prefixWithBaseUrl('kamil-porembinski-clouds.jpg')
} else if (background === 'custom') {
return generateUrl('/apps/theming/background') + '?v=' + time
}
return prefixWithBaseUrl(background)
}

View File

@@ -1,25 +0,0 @@
/**
* @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
import { generateFilePath } from '@nextcloud/router'
export const prefixWithBaseUrl = (url) => generateFilePath('theming', '', 'img/background/') + url

View File

@@ -1151,7 +1151,7 @@ class FileTest extends TestCase {
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
'permissions' => \OCP\Constants::PERMISSION_ALL,
'type' => FileInfo::TYPE_FOLDER,
'type' => FileInfo::TYPE_FILE,
], null);
$file = new \OCA\DAV\Connector\Sabre\File($view, $info);
@@ -1172,7 +1172,7 @@ class FileTest extends TestCase {
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
'permissions' => \OCP\Constants::PERMISSION_ALL,
'type' => FileInfo::TYPE_FOLDER,
'type' => FileInfo::TYPE_FILE,
], null);
$file = new \OCA\DAV\Connector\Sabre\File($view, $info);

View File

@@ -5,7 +5,7 @@
<name>Files</name>
<summary>File Management</summary>
<description>File Management</description>
<version>1.20.0</version>
<version>1.20.1</version>
<licence>agpl</licence>
<author>Robin Appelman</author>
<author>Vincent Petry</author>
@@ -26,6 +26,7 @@
<job>OCA\Files\BackgroundJob\DeleteOrphanedItems</job>
<job>OCA\Files\BackgroundJob\CleanupFileLocks</job>
<job>OCA\Files\BackgroundJob\CleanupDirectEditingTokens</job>
<job>OCA\Files\BackgroundJob\DeleteExpiredOpenLocalEditor</job>
</background-jobs>
<commands>

View File

@@ -37,6 +37,8 @@ declare(strict_types=1);
*/
namespace OCA\Files\AppInfo;
use OCA\Files\Controller\OpenLocalEditorController;
/** @var Application $application */
$application = \OC::$server->query(Application::class);
$application->registerRoutes(
@@ -169,6 +171,18 @@ $application->registerRoutes(
'url' => '/api/v1/transferownership/{id}',
'verb' => 'DELETE',
],
[
/** @see OpenLocalEditorController::create() */
'name' => 'OpenLocalEditor#create',
'url' => '/api/v1/openlocaleditor',
'verb' => 'POST',
],
[
/** @see OpenLocalEditorController::validate() */
'name' => 'OpenLocalEditor#validate',
'url' => '/api/v1/openlocaleditor/{token}',
'verb' => 'POST',
],
],
]
);

View File

@@ -20,6 +20,7 @@ return array(
'OCA\\Files\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php',
'OCA\\Files\\BackgroundJob\\CleanupDirectEditingTokens' => $baseDir . '/../lib/BackgroundJob/CleanupDirectEditingTokens.php',
'OCA\\Files\\BackgroundJob\\CleanupFileLocks' => $baseDir . '/../lib/BackgroundJob/CleanupFileLocks.php',
'OCA\\Files\\BackgroundJob\\DeleteExpiredOpenLocalEditor' => $baseDir . '/../lib/BackgroundJob/DeleteExpiredOpenLocalEditor.php',
'OCA\\Files\\BackgroundJob\\DeleteOrphanedItems' => $baseDir . '/../lib/BackgroundJob/DeleteOrphanedItems.php',
'OCA\\Files\\BackgroundJob\\ScanFiles' => $baseDir . '/../lib/BackgroundJob/ScanFiles.php',
'OCA\\Files\\BackgroundJob\\TransferOwnership' => $baseDir . '/../lib/BackgroundJob/TransferOwnership.php',
@@ -35,9 +36,12 @@ return array(
'OCA\\Files\\Controller\\ApiController' => $baseDir . '/../lib/Controller/ApiController.php',
'OCA\\Files\\Controller\\DirectEditingController' => $baseDir . '/../lib/Controller/DirectEditingController.php',
'OCA\\Files\\Controller\\DirectEditingViewController' => $baseDir . '/../lib/Controller/DirectEditingViewController.php',
'OCA\\Files\\Controller\\OpenLocalEditorController' => $baseDir . '/../lib/Controller/OpenLocalEditorController.php',
'OCA\\Files\\Controller\\TemplateController' => $baseDir . '/../lib/Controller/TemplateController.php',
'OCA\\Files\\Controller\\TransferOwnershipController' => $baseDir . '/../lib/Controller/TransferOwnershipController.php',
'OCA\\Files\\Controller\\ViewController' => $baseDir . '/../lib/Controller/ViewController.php',
'OCA\\Files\\Db\\OpenLocalEditor' => $baseDir . '/../lib/Db/OpenLocalEditor.php',
'OCA\\Files\\Db\\OpenLocalEditorMapper' => $baseDir . '/../lib/Db/OpenLocalEditorMapper.php',
'OCA\\Files\\Db\\TransferOwnership' => $baseDir . '/../lib/Db/TransferOwnership.php',
'OCA\\Files\\Db\\TransferOwnershipMapper' => $baseDir . '/../lib/Db/TransferOwnershipMapper.php',
'OCA\\Files\\DirectEditingCapabilities' => $baseDir . '/../lib/DirectEditingCapabilities.php',
@@ -48,6 +52,7 @@ return array(
'OCA\\Files\\Listener\\LegacyLoadAdditionalScriptsAdapter' => $baseDir . '/../lib/Listener/LegacyLoadAdditionalScriptsAdapter.php',
'OCA\\Files\\Listener\\LoadSidebarListener' => $baseDir . '/../lib/Listener/LoadSidebarListener.php',
'OCA\\Files\\Migration\\Version11301Date20191205150729' => $baseDir . '/../lib/Migration/Version11301Date20191205150729.php',
'OCA\\Files\\Migration\\Version12101Date20221011153334' => $baseDir . '/../lib/Migration/Version12101Date20221011153334.php',
'OCA\\Files\\Notification\\Notifier' => $baseDir . '/../lib/Notification/Notifier.php',
'OCA\\Files\\Search\\FilesSearchProvider' => $baseDir . '/../lib/Search/FilesSearchProvider.php',
'OCA\\Files\\Service\\DirectEditingService' => $baseDir . '/../lib/Service/DirectEditingService.php',

View File

@@ -35,6 +35,7 @@ class ComposerStaticInitFiles
'OCA\\Files\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php',
'OCA\\Files\\BackgroundJob\\CleanupDirectEditingTokens' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupDirectEditingTokens.php',
'OCA\\Files\\BackgroundJob\\CleanupFileLocks' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupFileLocks.php',
'OCA\\Files\\BackgroundJob\\DeleteExpiredOpenLocalEditor' => __DIR__ . '/..' . '/../lib/BackgroundJob/DeleteExpiredOpenLocalEditor.php',
'OCA\\Files\\BackgroundJob\\DeleteOrphanedItems' => __DIR__ . '/..' . '/../lib/BackgroundJob/DeleteOrphanedItems.php',
'OCA\\Files\\BackgroundJob\\ScanFiles' => __DIR__ . '/..' . '/../lib/BackgroundJob/ScanFiles.php',
'OCA\\Files\\BackgroundJob\\TransferOwnership' => __DIR__ . '/..' . '/../lib/BackgroundJob/TransferOwnership.php',
@@ -50,9 +51,12 @@ class ComposerStaticInitFiles
'OCA\\Files\\Controller\\ApiController' => __DIR__ . '/..' . '/../lib/Controller/ApiController.php',
'OCA\\Files\\Controller\\DirectEditingController' => __DIR__ . '/..' . '/../lib/Controller/DirectEditingController.php',
'OCA\\Files\\Controller\\DirectEditingViewController' => __DIR__ . '/..' . '/../lib/Controller/DirectEditingViewController.php',
'OCA\\Files\\Controller\\OpenLocalEditorController' => __DIR__ . '/..' . '/../lib/Controller/OpenLocalEditorController.php',
'OCA\\Files\\Controller\\TemplateController' => __DIR__ . '/..' . '/../lib/Controller/TemplateController.php',
'OCA\\Files\\Controller\\TransferOwnershipController' => __DIR__ . '/..' . '/../lib/Controller/TransferOwnershipController.php',
'OCA\\Files\\Controller\\ViewController' => __DIR__ . '/..' . '/../lib/Controller/ViewController.php',
'OCA\\Files\\Db\\OpenLocalEditor' => __DIR__ . '/..' . '/../lib/Db/OpenLocalEditor.php',
'OCA\\Files\\Db\\OpenLocalEditorMapper' => __DIR__ . '/..' . '/../lib/Db/OpenLocalEditorMapper.php',
'OCA\\Files\\Db\\TransferOwnership' => __DIR__ . '/..' . '/../lib/Db/TransferOwnership.php',
'OCA\\Files\\Db\\TransferOwnershipMapper' => __DIR__ . '/..' . '/../lib/Db/TransferOwnershipMapper.php',
'OCA\\Files\\DirectEditingCapabilities' => __DIR__ . '/..' . '/../lib/DirectEditingCapabilities.php',
@@ -63,6 +67,7 @@ class ComposerStaticInitFiles
'OCA\\Files\\Listener\\LegacyLoadAdditionalScriptsAdapter' => __DIR__ . '/..' . '/../lib/Listener/LegacyLoadAdditionalScriptsAdapter.php',
'OCA\\Files\\Listener\\LoadSidebarListener' => __DIR__ . '/..' . '/../lib/Listener/LoadSidebarListener.php',
'OCA\\Files\\Migration\\Version11301Date20191205150729' => __DIR__ . '/..' . '/../lib/Migration/Version11301Date20191205150729.php',
'OCA\\Files\\Migration\\Version12101Date20221011153334' => __DIR__ . '/..' . '/../lib/Migration/Version12101Date20221011153334.php',
'OCA\\Files\\Notification\\Notifier' => __DIR__ . '/..' . '/../lib/Notification/Notifier.php',
'OCA\\Files\\Search\\FilesSearchProvider' => __DIR__ . '/..' . '/../lib/Search/FilesSearchProvider.php',
'OCA\\Files\\Service\\DirectEditingService' => __DIR__ . '/..' . '/../lib/Service/DirectEditingService.php',

View File

@@ -973,8 +973,7 @@ table.dragshadow td.size {
background-image: none;
}
.files-filestable .filename .favorite-mark .icon-starred {
/* $dir is the app name, so we add this to the icon var to avoid conflicts between apps */
background-image: var(--icon-star-dark-yellow);
background-image: var(--icon-starred-yellow) !important;
}
.files-filestable .filename .action .icon.hidden,

File diff suppressed because one or more lines are too long

View File

@@ -874,7 +874,7 @@ table.dragshadow td.size {
background-image: none;
}
& .icon-starred {
@include icon-color('star-dark', 'actions', variables.$color-yellow, 1, true);
background-image: var(--icon-starred-yellow) !important;
}
}

View File

@@ -973,8 +973,7 @@ table.dragshadow td.size {
background-image: none;
}
.files-filestable .filename .favorite-mark .icon-starred {
/* $dir is the app name, so we add this to the icon var to avoid conflicts between apps */
background-image: var(--icon-star-dark-yellow);
background-image: var(--icon-starred-yellow) !important;
}
.files-filestable .filename .action .icon.hidden,

File diff suppressed because one or more lines are too long

View File

@@ -114,13 +114,13 @@
OCA.Files.FileList.MultiSelectMenuActions.ToggleSelectionModeAction,
{
name: 'delete',
displayName: t('files', 'Delete'),
displayName: t('files', 'Delete'),
iconClass: 'icon-delete',
order: 99,
},
{
name: 'tags',
displayName: 'Tags',
displayName: t('files', 'Tags'),
iconClass: 'icon-tag',
order: 100,
},

View File

@@ -710,29 +710,31 @@
}
});
this.registerAction({
name: 'EditLocally',
displayName: function(context) {
var locked = context.$file.data('locked');
if (!locked) {
return t('files', 'Edit locally');
}
},
mime: 'all',
order: -23,
icon: function(filename, context) {
var locked = context.$file.data('locked');
if (!locked) {
return OC.imagePath('files', 'computer.svg')
}
},
permissions: OC.PERMISSION_UPDATE,
actionHandler: function (filename, context) {
var dir = context.dir || context.fileList.getCurrentDirectory();
var path = dir === '/' ? dir + filename : dir + '/' + filename;
context.fileList.openLocalClient(path);
},
});
if (!/Android|iPhone|iPad|iPod/i.test(navigator.userAgent)) {
this.registerAction({
name: 'EditLocally',
displayName: function(context) {
var locked = context.$file.data('locked');
if (!locked) {
return t('files', 'Edit locally');
}
},
mime: 'all',
order: -23,
icon: function(filename, context) {
var locked = context.$file.data('locked');
if (!locked) {
return OC.imagePath('files', 'computer.svg')
}
},
permissions: OC.PERMISSION_UPDATE,
actionHandler: function (filename, context) {
var dir = context.dir || context.fileList.getCurrentDirectory();
var path = dir === '/' ? dir + filename : dir + '/' + filename;
context.fileList.openLocalClient(path);
},
});
}
this.registerAction({
name: 'Open',

View File

@@ -2808,12 +2808,23 @@
},
openLocalClient: function(path) {
var scheme = 'nc://';
var command = 'open';
var uid = OC.getCurrentUser().uid;
var url = scheme + command + '/' + uid + '@' + window.location.host + OC.encodePath(path);
var link = OC.linkToOCS('apps/files/api/v1', 2) + 'openlocaleditor?format=json';
window.location.href = url;
$.post(link, {
path
})
.success(function(result) {
var scheme = 'nc://';
var command = 'open';
var uid = OC.getCurrentUser().uid;
var url = scheme + command + '/' + uid + '@' + window.location.host + OC.encodePath(path);
url += '?token=' + result.ocs.data.token;
window.location.href = url;
})
.fail(function() {
OC.Notification.show(t('files', 'Failed to redirect to client'))
})
},
/**

View File

@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com>
*
* @author Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files\BackgroundJob;
use OCA\Files\Controller\OpenLocalEditorController;
use OCA\Files\Db\OpenLocalEditorMapper;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJob;
use OCP\BackgroundJob\TimedJob;
/**
* Delete all expired "Open local editor" token
*/
class DeleteExpiredOpenLocalEditor extends TimedJob {
protected OpenLocalEditorMapper $mapper;
public function __construct(
ITimeFactory $time,
OpenLocalEditorMapper $mapper
) {
parent::__construct($time);
$this->mapper = $mapper;
// Run every 12h
$this->interval = 12 * 3600;
$this->setTimeSensitivity(IJob::TIME_INSENSITIVE);
}
/**
* Makes the background job do its work
*
* @param array $argument unused argument
*/
public function run($argument): void {
$this->mapper->deleteExpiredTokens($this->time->getTime());
}
}

View File

@@ -0,0 +1,138 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com>
*
* @author Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files\Controller;
use OCA\Files\Db\OpenLocalEditor;
use OCA\Files\Db\OpenLocalEditorMapper;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\DB\Exception;
use OCP\IRequest;
use OCP\Security\ISecureRandom;
use Psr\Log\LoggerInterface;
class OpenLocalEditorController extends OCSController {
public const TOKEN_LENGTH = 128;
public const TOKEN_DURATION = 600; // 10 Minutes
public const TOKEN_RETRIES = 50;
protected ITimeFactory $timeFactory;
protected OpenLocalEditorMapper $mapper;
protected ISecureRandom $secureRandom;
protected LoggerInterface $logger;
protected ?string $userId;
public function __construct(
string $appName,
IRequest $request,
ITimeFactory $timeFactory,
OpenLocalEditorMapper $mapper,
ISecureRandom $secureRandom,
LoggerInterface $logger,
?string $userId
) {
parent::__construct($appName, $request);
$this->timeFactory = $timeFactory;
$this->mapper = $mapper;
$this->secureRandom = $secureRandom;
$this->logger = $logger;
$this->userId = $userId;
}
/**
* @NoAdminRequired
* @UserRateThrottle(limit=10, period=120)
*/
public function create(string $path): DataResponse {
$pathHash = sha1($path);
$entity = new OpenLocalEditor();
$entity->setUserId($this->userId);
$entity->setPathHash($pathHash);
$entity->setExpirationTime($this->timeFactory->getTime() + self::TOKEN_DURATION); // Expire in 10 minutes
for ($i = 1; $i <= self::TOKEN_RETRIES; $i++) {
$token = $this->secureRandom->generate(self::TOKEN_LENGTH, ISecureRandom::CHAR_ALPHANUMERIC);
$entity->setToken($token);
try {
$this->mapper->insert($entity);
return new DataResponse([
'userId' => $this->userId,
'pathHash' => $pathHash,
'expirationTime' => $entity->getExpirationTime(),
'token' => $entity->getToken(),
]);
} catch (Exception $e) {
if ($e->getCode() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
// Only retry on unique constraint violation
throw $e;
}
}
}
$this->logger->error('Giving up after ' . self::TOKEN_RETRIES . ' retries to generate a unique local editor token for path hash: ' . $pathHash);
return new DataResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
}
/**
* @NoAdminRequired
* @BruteForceProtection(action=openLocalEditor)
*/
public function validate(string $path, string $token): DataResponse {
$pathHash = sha1($path);
try {
$entity = $this->mapper->verifyToken($this->userId, $pathHash, $token);
} catch (DoesNotExistException $e) {
$response = new DataResponse([], Http::STATUS_NOT_FOUND);
$response->throttle(['userId' => $this->userId, 'pathHash' => $pathHash]);
return $response;
}
$this->mapper->delete($entity);
if ($entity->getExpirationTime() <= $this->timeFactory->getTime()) {
$response = new DataResponse([], Http::STATUS_NOT_FOUND);
$response->throttle(['userId' => $this->userId, 'pathHash' => $pathHash]);
return $response;
}
return new DataResponse([
'userId' => $this->userId,
'pathHash' => $pathHash,
'expirationTime' => $entity->getExpirationTime(),
'token' => $entity->getToken(),
]);
}
}

View File

@@ -266,7 +266,7 @@ class ViewController extends Controller {
$nav->assign('quota', $storageInfo['quota']);
$nav->assign('usage_relative', $storageInfo['relative']);
$nav->assign('webdav_url', \OCP\Util::linkToRemote('dav/files/' . $user));
$nav->assign('webdav_url', \OCP\Util::linkToRemote('dav/files/' . rawurlencode($user)));
$contentItems = [];

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com>
*
* @author Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files\Db;
use OCP\AppFramework\Db\Entity;
/**
* @method void setUserId(string $userId)
* @method string getUserId()
* @method void setPathHash(string $pathHash)
* @method string getPathHash()
* @method void setExpirationTime(int $expirationTime)
* @method int getExpirationTime()
* @method void setToken(string $token)
* @method string getToken()
*/
class OpenLocalEditor extends Entity {
/** @var string */
protected $userId;
/** @var string */
protected $pathHash;
/** @var int */
protected $expirationTime;
/** @var string */
protected $token;
public function __construct() {
$this->addType('userId', 'string');
$this->addType('pathHash', 'string');
$this->addType('expirationTime', 'integer');
$this->addType('token', 'string');
}
}

View File

@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com>
*
* @author Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files\Db;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\Exception;
use OCP\IDBConnection;
class OpenLocalEditorMapper extends QBMapper {
public function __construct(IDBConnection $db) {
parent::__construct($db, 'open_local_editor', OpenLocalEditor::class);
}
/**
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
* @throws Exception
*/
public function verifyToken(string $userId, string $pathHash, string $token): OpenLocalEditor {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)))
->andWhere($qb->expr()->eq('path_hash', $qb->createNamedParameter($pathHash)))
->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)));
return $this->findEntity($qb);
}
public function deleteExpiredTokens(int $time): void {
$qb = $this->db->getQueryBuilder();
$qb->delete($this->getTableName())
->where($qb->expr()->lt('expiration_time', $qb->createNamedParameter($time)));
$qb->executeStatement();
}
}

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com>
*
* @author Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files\Migration;
use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
class Version12101Date20221011153334 extends SimpleMigrationStep {
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$table = $schema->createTable('open_local_editor');
$table->addColumn('id',Types::BIGINT, [
'autoincrement' => true,
'notnull' => true,
'length' => 20,
'unsigned' => true,
]);
$table->addColumn('user_id', Types::STRING, [
'notnull' => true,
'length' => 64,
]);
$table->addColumn('path_hash', Types::STRING, [
'notnull' => true,
'length' => 64,
]);
$table->addColumn('expiration_time', Types::BIGINT, [
'notnull' => true,
'unsigned' => true,
]);
$table->addColumn('token', Types::STRING, [
'notnull' => true,
'length' => 128,
]);
$table->setPrimaryKey(['id']);
$table->addUniqueIndex(['user_id', 'path_hash', 'token'], 'openlocal_user_path_token');
return $schema;
}
}

View File

@@ -47,13 +47,17 @@
}
#imgframe img {
max-height: calc(100vh - var(--header-height) - 65px - 200px) !important;
max-height: calc(100vh - var(--header-height) - 65px - 200px - 16px) !important;
max-width: 100% !important;
width: unset !important;
}
#imgframe :not(#viewer) img {
min-width: 100px;
}
#imgframe video {
max-height: calc(100vh - var(--header-height) - 65px - 200px);
max-height: calc(100vh - var(--header-height) - 65px - 200px - 16px);
}
#imgframe audio {
@@ -94,8 +98,8 @@
max-height: 100%;
}
.app-files_sharing #app-content {
max-height: calc(100vh - var(--header-height) - 65px);
.app-files_sharing #app-content footer {
position: sticky !important;
}
/* fix multiselect bar offset on shared page */

View File

@@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../../core/css/variables.scss","public.scss"],"names":[],"mappings":";AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACKA;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EAEI;;;AAGJ;EACI;;;AAGJ;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;;;AAGD;EACC;;;AAID;EACI;;;AAGJ;AACA;EACC;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;AACA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;AACA;EACC;;;AAED;AAAA;AAAA;EAGC;EACA;;;AAED;AAAA;AAAA;AAGC;EACA;;;AAGD;EACC;;;AAIA;EACC;;;AAIF;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;AAAA;EAEC;EACA;EACA;;;AAGD;EACC;EACA;EACA;;AACA;EACC;EACA;;;AAIF;EACC;EACA;;;AAKD;EAII;IACC;;;AAQL;EAGG;IACC","file":"public.css"}
{"version":3,"sourceRoot":"","sources":["../../../core/css/variables.scss","public.scss"],"names":[],"mappings":";AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACMA;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACI;;;AAGJ;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EAEI;;;AAGJ;EACI;;;AAGJ;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;;;AAGD;EACC;;;AAID;EACC;;;AAGD;AACA;EACC;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;AACA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;AACA;EACC;;;AAED;AAAA;AAAA;EAGC;EACA;;;AAED;AAAA;AAAA;AAGC;EACA;;;AAGD;EACC;;;AAIA;EACC;;;AAIF;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;AAAA;EAEC;EACA;EACA;;;AAGD;EACC;EACA;EACA;;AACA;EACC;EACA;;;AAIF;EACC;EACA;;;AAKD;EAII;IACC;;;AAQL;EAGG;IACC","file":"public.css"}

View File

@@ -1,6 +1,7 @@
@use 'variables';
$footer-height: 65px;
$footer-padding-height: 16px;
$download-button-section-height: 200px;
#preview {
@@ -30,13 +31,17 @@ $download-button-section-height: 200px;
}
#imgframe img {
max-height: calc(100vh - var(--header-height) - #{$footer-height} - #{$download-button-section-height}) !important;
max-height: calc(100vh - var(--header-height) - #{$footer-height} - #{$download-button-section-height} - #{$footer-padding-height}) !important;
max-width: 100% !important;
width: unset !important;
}
#imgframe :not(#viewer) img {
min-width: 100px;
}
#imgframe video {
max-height: calc(100vh - var(--header-height) - #{$footer-height} - #{$download-button-section-height});
max-height: calc(100vh - var(--header-height) - #{$footer-height} - #{$download-button-section-height} - #{$footer-padding-height});
}
#imgframe audio {
@@ -78,9 +83,9 @@ $download-button-section-height: 200px;
max-height: 100%;
}
// Fix footer overlapping with app-content
.app-files_sharing #app-content {
max-height: calc(100vh - var(--header-height) - #{$footer-height});
.app-files_sharing #app-content footer {
position: sticky !important;
}
/* fix multiselect bar offset on shared page */

View File

@@ -47,13 +47,17 @@
}
#imgframe img {
max-height: calc(100vh - var(--header-height) - 65px - 200px) !important;
max-height: calc(100vh - var(--header-height) - 65px - 200px - 16px) !important;
max-width: 100% !important;
width: unset !important;
}
#imgframe :not(#viewer) img {
min-width: 100px;
}
#imgframe video {
max-height: calc(100vh - var(--header-height) - 65px - 200px);
max-height: calc(100vh - var(--header-height) - 65px - 200px - 16px);
}
#imgframe audio {
@@ -94,8 +98,8 @@
max-height: 100%;
}
.app-files_sharing #app-content {
max-height: calc(100vh - var(--header-height) - 65px);
.app-files_sharing #app-content footer {
position: sticky !important;
}
/* fix multiselect bar offset on shared page */

View File

@@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../../core/css/variables.scss","public.scss","mobile.scss"],"names":[],"mappings":";AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACKA;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EAEI;;;AAGJ;EACI;;;AAGJ;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;;;AAGD;EACC;;;AAID;EACI;;;AAGJ;AACA;EACC;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;AACA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;AACA;EACC;;;AAED;AAAA;AAAA;EAGC;EACA;;;AAED;AAAA;AAAA;AAGC;EACA;;;AAGD;EACC;;;AAIA;EACC;;;AAIF;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;AAAA;EAEC;EACA;EACA;;;AAGD;EACC;EACA;EACA;;AACA;EACC;EACA;;;AAIF;EACC;EACA;;;AAKD;EAII;IACC;;;AAQL;EAGG;IACC;;;ADpQJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AEEA;AAEA;EACA;IACC;;;AAGD;EACA;AAAA;AAAA;AAAA;IAIC;;;AAGD;EACA;IACC;;;AAGD;EACA;IACC;IACA;;;AAED;EACA;IACC;;;AAGD;EACA;IACC;;;AAED;EACA;IACC;;;AAGD;EACA;IACC;IACA;IACA;IACA;;;EAGD;IACI;IACA;;;EAEJ;IACC;;;EAGD;IACC","file":"publicView.css"}
{"version":3,"sourceRoot":"","sources":["../../../core/css/variables.scss","public.scss","mobile.scss"],"names":[],"mappings":";AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACMA;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACI;;;AAGJ;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EAEI;;;AAGJ;EACI;;;AAGJ;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;;;AAGD;EACC;;;AAID;EACC;;;AAGD;AACA;EACC;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;AACA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;AACA;EACC;;;AAED;AAAA;AAAA;EAGC;EACA;;;AAED;AAAA;AAAA;AAGC;EACA;;;AAGD;EACC;;;AAIA;EACC;;;AAIF;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;AAAA;EAEC;EACA;EACA;;;AAGD;EACC;EACA;EACA;;AACA;EACC;EACA;;;AAIF;EACC;EACA;;;AAKD;EAII;IACC;;;AAQL;EAGG;IACC;;;ADzQJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AEEA;AAEA;EACA;IACC;;;AAGD;EACA;AAAA;AAAA;AAAA;IAIC;;;AAGD;EACA;IACC;;;AAGD;EACA;IACC;IACA;;;AAED;EACA;IACC;;;AAGD;EACA;IACC;;;AAED;EACA;IACC;;;AAGD;EACA;IACC;IACA;IACA;IACA;;;EAGD;IACI;IACA;;;EAEJ;IACC;;;EAGD;IACC","file":"publicView.css"}

View File

@@ -62,6 +62,11 @@ OCA.Sharing.PublicApp = {
// file list mode ?
if ($el.find('.files-filestable').length) {
// Toggle for grid view
this.$showGridView = $('input#showgridview');
this.$showGridView.on('change', _.bind(this._onGridviewChange, this));
$('#view-toggle').tooltip({placement: 'bottom', trigger: 'hover'});
var filesClient = new OC.Files.Client({
host: OC.getHost(),
port: OC.getPort(),
@@ -364,6 +369,26 @@ OCA.Sharing.PublicApp = {
}
},
/**
* Toggle showing gridview by default or not
*
* @returns {undefined}
*/
_onGridviewChange: function() {
const isGridView = this.$showGridView.is(':checked');
this.$showGridView.next('#view-toggle')
.removeClass('icon-toggle-filelist icon-toggle-pictures')
.addClass(isGridView ? 'icon-toggle-filelist' : 'icon-toggle-pictures')
this.$showGridView.next('#view-toggle').attr(
'data-original-title',
isGridView ? t('files', 'Show list view') : t('files', 'Show grid view'),
)
if (this.fileList) {
this.fileList.setGridView(isGridView);
}
},
_onDirectoryChanged: function (e) {
OC.Util.History.pushState({
// arghhhh, why is this not called "dir" !?

View File

@@ -179,11 +179,6 @@
// storage info like free space / used space
},
updateRow: function($tr, fileInfo, options) {
// no-op, suppress re-rendering
return $tr
},
reload: function() {
this.showMask()
if (this._reloadCall) {

View File

@@ -1555,7 +1555,7 @@ class ShareAPIController extends OCSController {
*/
private function parseDate(string $expireDate): \DateTime {
try {
$date = new \DateTime($expireDate);
$date = new \DateTime(trim($expireDate, "\""));
} catch (\Exception $e) {
throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
}

View File

@@ -95,20 +95,15 @@
</NcActionCheckbox>
<NcActionInput v-if="hasExpirationDate"
ref="expireDate"
v-tooltip.auto="{
content: errors.expireDate,
show: errors.expireDate,
trigger: 'manual'
}"
:is-native-picker="true"
:hide-label="true"
:class="{ error: errors.expireDate}"
:disabled="saving"
:lang="lang"
:value="share.expireDate"
value-type="format"
icon="icon-calendar-dark"
type="date"
:disabled-date="disabledDate"
@update:value="onExpirationChange">
:min="dateTomorrow"
:max="dateMaxEnforced"
@input="onExpirationChange">
{{ t('files_sharing', 'Enter a date') }}
</NcActionInput>
@@ -380,21 +375,20 @@ export default {
},
set(enabled) {
this.share.expireDate = enabled
? this.config.defaultInternalExpirationDateString !== ''
? this.config.defaultInternalExpirationDateString
: moment().format('YYYY-MM-DD')
? this.config.defaultInternalExpirationDate !== ''
? this.config.defaultInternalExpirationDate
: new Date()
: ''
},
},
dateMaxEnforced() {
if (!this.isRemote) {
return this.config.isDefaultInternalExpireDateEnforced
&& moment().add(1 + this.config.defaultInternalExpireDate, 'days')
} else {
return this.config.isDefaultRemoteExpireDateEnforced
&& moment().add(1 + this.config.defaultRemoteExpireDate, 'days')
if (!this.isRemote && this.config.isDefaultInternalExpireDateEnforced) {
return new Date(new Date().setDate(new Date().getDate() + 1 + this.config.defaultInternalExpireDate))
} else if (this.config.isDefaultRemoteExpireDateEnforced) {
return new Date(new Date().setDate(new Date().getDate() + 1 + this.config.defaultRemoteExpireDate))
}
return null
},
/**

View File

@@ -1,15 +1,15 @@
<template>
<ul>
<SharingEntrySimple class="sharing-entry__internal"
<SharingEntrySimple ref="shareEntrySimple"
class="sharing-entry__internal"
:title="t('files_sharing', 'Internal link')"
:subtitle="internalLinkSubtitle">
<template #avatar>
<div class="avatar-external icon-external-white" />
</template>
<NcActionLink ref="copyButton"
:href="internalLink"
<NcActionLink :href="internalLink"
:aria-label="t('files_sharing', 'Copy internal link to clipboard')"
target="_blank"
:icon="copied && copySuccess ? 'icon-checkmark-color' : 'icon-clippy'"
@@ -84,8 +84,8 @@ export default {
async copyLink() {
try {
await this.$copyText(this.internalLink)
// focus and show the tooltip
this.$refs.copyButton.$el.focus()
// focus and show the tooltip (note: cannot set ref on NcActionLink)
this.$refs.shareEntrySimple.$refs.actionsComponent.$el.focus()
this.copySuccess = true
this.copied = true
} catch (error) {

View File

@@ -98,20 +98,13 @@
</NcActionText>
<NcActionInput v-if="pendingExpirationDate"
v-model="share.expireDate"
v-tooltip.auto="{
content: errors.expireDate,
show: errors.expireDate,
trigger: 'manual',
defaultContainer: '#app-sidebar'
}"
class="share-link-expire-date"
:disabled="saving"
:lang="lang"
icon=""
:is-native-picker="true"
:hide-label="true"
type="date"
value-type="format"
:disabled-date="disabledDate">
:min="dateTomorrow"
:max="dateMaxEnforced">
<!-- let's not submit when picked, the user
might want to still edit or copy the password -->
{{ t('files_sharing', 'Enter a date') }}
@@ -220,22 +213,16 @@
</NcActionCheckbox>
<NcActionInput v-if="hasExpirationDate"
ref="expireDate"
v-tooltip.auto="{
content: errors.expireDate,
show: errors.expireDate,
trigger: 'manual',
defaultContainer: '#app-sidebar'
}"
:is-native-picker="true"
:hide-label="true"
class="share-link-expire-date"
:class="{ error: errors.expireDate}"
:disabled="saving"
:lang="lang"
:value="share.expireDate"
value-type="format"
icon="icon-calendar-dark"
type="date"
:disabled-date="disabledDate"
@update:value="onExpirationChange">
:min="dateTomorrow"
:max="dateMaxEnforced"
@input="onExpirationChange">
{{ t('files_sharing', 'Enter a date') }}
</NcActionInput>
@@ -435,20 +422,22 @@ export default {
|| !!this.share.expireDate
},
set(enabled) {
let dateString = moment(this.config.defaultExpirationDateString)
if (!dateString.isValid()) {
dateString = moment()
let defaultExpirationDate = this.config.defaultExpirationDate
if (!defaultExpirationDate) {
defaultExpirationDate = new Date()
}
this.share.state.expiration = enabled
? dateString.format('YYYY-MM-DD')
? defaultExpirationDate
: ''
console.debug('Expiration date status', enabled, this.share.expireDate)
},
},
dateMaxEnforced() {
return this.config.isDefaultExpireDateEnforced
&& moment().add(1 + this.config.defaultExpireDate, 'days')
if (this.config.isDefaultExpireDateEnforced) {
return new Date(new Date().setDate(new Date().getDate() + 1 + this.config.defaultExpireDate))
}
return null
},
/**
@@ -631,7 +620,7 @@ export default {
if (this.config.isDefaultExpireDateEnforced) {
// default is empty string if not set
// expiration is the share object key, not expireDate
shareDefaults.expiration = this.config.defaultExpirationDateString
shareDefaults.expiration = this.config.defaultExpirationDate
}
if (this.config.enableLinkPasswordByDefault) {
shareDefaults.password = await GeneratePassword()

View File

@@ -29,7 +29,8 @@
{{ subtitle }}
</p>
</div>
<NcActions v-if="$slots['default']"
<NcActions ref="actionsComponent"
v-if="$slots['default']"
class="sharing-entry__actions"
menu-align="right"
:aria-expanded="ariaExpandedValue">

View File

@@ -97,7 +97,7 @@ export default {
},
dateTomorrow() {
return moment().add(1, 'days')
return new Date(new Date().setDate(new Date().getDate() + 1))
},
// Datepicker language
@@ -142,7 +142,7 @@ export default {
}
}
if (share.expirationDate) {
const date = moment(share.expirationDate)
const date = share.expirationDate
if (!date.isValid()) {
return false
}
@@ -151,16 +151,12 @@ export default {
},
/**
* ActionInput can be a little tricky to work with.
* Since we expect a string and not a Date,
* we need to process the value here
* Save given value to expireDate and trigger queueUpdate
*
* @param {Date} date js date to be parsed by moment.js
* @param {Date} date
*/
onExpirationChange(date) {
// format to YYYY-MM-DD
const value = moment(date).format('YYYY-MM-DD')
this.share.expireDate = value
this.share.expireDate = date
this.queueUpdate('expireDate')
},
@@ -318,17 +314,5 @@ export default {
debounceQueueUpdate: debounce(function(property) {
this.queueUpdate(property)
}, 500),
/**
* Returns which dates are disabled for the datepicker
*
* @param {Date} date date to check
* @return {boolean}
*/
disabledDate(date) {
const dateMoment = moment(date)
return (this.dateTomorrow && dateMoment.isBefore(this.dateTomorrow, 'day'))
|| (this.dateMaxEnforced && dateMoment.isSameOrAfter(this.dateMaxEnforced, 'day'))
},
},
}

View File

@@ -248,9 +248,9 @@ export default class Share {
}
/**
* Get the expiration date as a string format
* Get the expiration date
*
* @return {string}
* @return {Date|null}
* @readonly
* @memberof Share
*/
@@ -259,10 +259,9 @@ export default class Share {
}
/**
* Set the expiration date as a string format
* e.g. YYYY-MM-DD
* Set the expiration date
*
* @param {string} date the share expiration date
* @param {Date|null} date the share expiration date
* @memberof Share
*/
set expireDate(date) {

View File

@@ -60,57 +60,45 @@ export default class Config {
}
/**
* Get the default link share expiration date as string
* Get the default link share expiration date
*
* @return {string}
* @return {Date|null}
* @readonly
* @memberof Config
*/
get defaultExpirationDateString() {
let expireDateString = ''
get defaultExpirationDate() {
if (this.isDefaultExpireDateEnabled) {
const date = window.moment.utc()
const expireAfterDays = this.defaultExpireDate
date.add(expireAfterDays, 'days')
expireDateString = date.format('YYYY-MM-DD')
return new Date(new Date().setDate(new Date().getDate() + this.defaultExpireDate))
}
return expireDateString
return null
}
/**
* Get the default internal expiration date as string
* Get the default internal expiration date
*
* @return {string}
* @return {Date|null}
* @readonly
* @memberof Config
*/
get defaultInternalExpirationDateString() {
let expireDateString = ''
get defaultInternalExpirationDate() {
if (this.isDefaultInternalExpireDateEnabled) {
const date = window.moment.utc()
const expireAfterDays = this.defaultInternalExpireDate
date.add(expireAfterDays, 'days')
expireDateString = date.format('YYYY-MM-DD')
return new Date(new Date().setDate(new Date().getDate() + this.defaultInternalExpireDate))
}
return expireDateString
return null
}
/**
* Get the default remote expiration date as string
* Get the default remote expiration date
*
* @return {string}
* @return {Date|null}
* @readonly
* @memberof Config
*/
get defaultRemoteExpirationDateString() {
let expireDateString = ''
if (this.isDefaultRemoteExpireDateEnabled) {
const date = window.moment.utc()
const expireAfterDays = this.defaultRemoteExpireDate
date.add(expireAfterDays, 'days')
expireDateString = date.format('YYYY-MM-DD')
return new Date(new Date().setDate(new Date().getDate() + this.defaultRemoteExpireDate))
}
return expireDateString
return null
}
/**
@@ -190,6 +178,17 @@ export default class Config {
return OC.appConfig.core.defaultInternalExpireDateEnabled === true
}
/**
* Is there a default expiration date for new remote shares ?
*
* @return {boolean}
* @readonly
* @memberof Config
*/
get isDefaultRemoteExpireDateEnabled() {
return OC.appConfig.core.defaultRemoteExpireDateEnabled === true
}
/**
* Are users on this server allowed to send shares to other servers ?
*

View File

@@ -389,6 +389,9 @@ class UsersController extends AUserData {
}
$generatePasswordResetToken = false;
if (strlen($password) > 469) {
throw new OCSException('Invalid password value', 101);
}
if ($password === '') {
if ($email === '') {
throw new OCSException('To send a password link to the user an email address is required.', 108);
@@ -882,6 +885,9 @@ class UsersController extends AUserData {
break;
case self::USER_FIELD_PASSWORD:
try {
if (strlen($value) > 469) {
throw new OCSException('Invalid password value', 102);
}
if (!$targetUser->canChangePassword()) {
throw new OCSException('Setting the password is not supported by the users backend', 103);
}

View File

@@ -379,7 +379,7 @@ class CheckSetupController extends Controller {
return true;
}
// there are two different memcached modules for PHP
// there are two different memcache modules for PHP
// we only support memcached and not memcache
// https://code.google.com/p/memcached/wiki/PHPClientComparison
return !(!extension_loaded('memcached') && extension_loaded('memcache'));
@@ -392,7 +392,7 @@ class CheckSetupController extends Controller {
*/
private function isSettimelimitAvailable() {
if (function_exists('set_time_limit')
&& strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
&& strpos(ini_get('disable_functions'), 'set_time_limit') === false) {
return true;
}
@@ -819,12 +819,12 @@ Raw output
$tempPath = sys_get_temp_dir();
if (!is_dir($tempPath)) {
$this->logger->error('Error while checking the temporary PHP path - it was not properly set to a directory. value: ' . $tempPath);
$this->logger->error('Error while checking the temporary PHP path - it was not properly set to a directory. Returned value: ' . $tempPath);
return false;
}
$freeSpaceInTemp = disk_free_space($tempPath);
$freeSpaceInTemp = function_exists('disk_free_space') ? disk_free_space($tempPath) : false;
if ($freeSpaceInTemp === false) {
$this->logger->error('Error while checking the available disk space of temporary PHP path - no free disk space returned. temporary path: ' . $tempPath);
$this->logger->error('Error while checking the available disk space of temporary PHP path or no free disk space returned. Temporary path: ' . $tempPath);
return false;
}

View File

@@ -38,10 +38,11 @@ class SecurityTxtHandler implements IHandler {
}
$response = "Contact: https://hackerone.com/nextcloud
Expires: 2021-12-31T23:00:00.000Z
Expires: 2023-04-31T23:00:00.000Z
Acknowledgments: https://hackerone.com/nextcloud/thanks
Acknowledgments: https://github.com/nextcloud/security-advisories/security/advisories
Policy: https://hackerone.com/nextcloud";
Policy: https://hackerone.com/nextcloud
Preferred-Languages: en";
return new GenericResponse(new TextPlainResponse($response, 200));
}

View File

@@ -56,6 +56,7 @@
ref="newuserpassword"
v-model="newUser.password"
:minlength="minPasswordLength"
:maxlength="469"
:placeholder="t('settings', 'Password')"
:required="newUser.mailAddress===''"
autocapitalize="none"

View File

@@ -31,6 +31,9 @@
<pre-migration>
<step>OCA\Theming\Migration\MigrateUserConfig</step>
</pre-migration>
<post-migration>
<step>OCA\Theming\Migration\InitBackgroundImagesMigration</step>
</post-migration>
</repair-steps>
<commands>

View File

@@ -1,5 +1,6 @@
:root {
--color-main-background: #ffffff;
--color-main-background-not-plain: #0082c9;
--color-main-background-rgb: 255,255,255;
--color-main-background-translucent: rgba(var(--color-main-background-rgb), .97);
--color-main-background-blur: rgba(var(--color-main-background-rgb), .8);
@@ -10,23 +11,10 @@
--color-background-darker: #dbdbdb;
--color-placeholder-light: #e6e6e6;
--color-placeholder-dark: #cccccc;
--color-primary: #0082c9;
--color-primary-text: #ffffff;
--color-primary-hover: #329bd3;
--color-primary-light: #e5f2f9;
--color-primary-light-text: #0082c9;
--color-primary-light-hover: #dbe7ee;
--color-primary-text-dark: #ededed;
--color-primary-element: #0082c9;
--color-primary-element-text: #ffffff;
--color-primary-element-hover: #329bd3;
--color-primary-element-light: #e5f2f9;
--color-primary-element-light-text: #0082c9;
--color-primary-element-light-hover: #dbe7ee;
--color-primary-element-text-dark: #ededed;
--gradient-primary-background: linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-hover) 100%);
--color-main-text: #222222;
--color-text-maxcontrast: #767676;
--color-text-maxcontrast-default: #767676;
--color-text-maxcontrast-background-blur: #646464;
--color-text-light: #222222;
--color-text-lighter: #767676;
--color-scrollbar: rgba(34,34,34, .15);
@@ -68,4 +56,20 @@
--background-invert-if-dark: no;
--background-invert-if-bright: invert(100%);
--image-main-background: url('/core/img/app-background.jpg');
--color-primary: #00639a;
--color-primary-default: #0082c9;
--color-primary-text: #ffffff;
--color-primary-hover: #3282ae;
--color-primary-light: #e5eff4;
--color-primary-light-text: #00273d;
--color-primary-light-hover: #dbe4e9;
--color-primary-text-dark: #ededed;
--color-primary-element: #00639a;
--color-primary-element-text: #ffffff;
--color-primary-element-hover: #3282ae;
--color-primary-element-light: #e5eff4;
--color-primary-element-light-text: #00273d;
--color-primary-element-light-hover: #dbe4e9;
--color-primary-element-text-dark: #ededed;
--gradient-primary-background: linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-hover) 100%);
}

View File

@@ -89,7 +89,7 @@
margin-top: 10px;
margin-bottom: 20px;
cursor: pointer;
background-color: var(--color-primary);
background-color: var(--color-primary-default);
background-image: var(--image-background, var(--image-background-plain, url("../../../core/img/app-background.jpg"), linear-gradient(40deg, #0082c9 0%, #30b6ff 100%)));
}
#theming #theming-preview #theming-preview-logo {

View File

@@ -100,7 +100,7 @@
margin-top: 10px;
margin-bottom: 20px;
cursor: pointer;
background-color: var(--color-primary);
background-color: var(--color-primary-default);
background-image: var(--image-background, var(--image-background-plain, url('../../../core/img/app-background.jpg'), linear-gradient(40deg, #0082c9 0%, #30b6ff 100%)));
#theming-preview-logo {
@@ -145,4 +145,4 @@
svg, img {
transition: 500ms filter linear;
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 422 KiB

After

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 369 KiB

After

Width:  |  Height:  |  Size: 410 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 382 KiB

After

Width:  |  Height:  |  Size: 428 KiB

View File

@@ -34,6 +34,7 @@ use OCA\Theming\AppInfo\Application;
use OCA\Theming\ITheme;
use OCA\Theming\Service\BackgroundService;
use OCA\Theming\Service\ThemesService;
use OCA\Theming\ThemingDefaults;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
@@ -53,6 +54,7 @@ class UserThemeController extends OCSController {
private IConfig $config;
private IUserSession $userSession;
private ThemesService $themesService;
private ThemingDefaults $themingDefaults;
private BackgroundService $backgroundService;
/**
@@ -63,11 +65,13 @@ class UserThemeController extends OCSController {
IConfig $config,
IUserSession $userSession,
ThemesService $themesService,
ThemingDefaults $themingDefaults,
BackgroundService $backgroundService) {
parent::__construct($appName, $request);
$this->config = $config;
$this->userSession = $userSession;
$this->themesService = $themesService;
$this->themingDefaults = $themingDefaults;
$this->backgroundService = $backgroundService;
$this->userId = $userSession->getUser()->getUID();
}
@@ -177,6 +181,8 @@ class UserThemeController extends OCSController {
}
$currentVersion++;
$this->config->setUserValue($this->userId, Application::APP_ID, 'backgroundVersion', (string)$currentVersion);
// FIXME replace with user-specific cachebuster increase https://github.com/nextcloud/server/issues/34472
$this->themingDefaults->increaseCacheBuster();
return new JSONResponse([
'type' => $type,
'value' => $value,

View File

@@ -0,0 +1,107 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace OCA\Theming\Jobs;
use OCA\Theming\AppInfo\Application;
use OCP\App\IAppManager;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJobList;
use OCP\BackgroundJob\QueuedJob;
use OCP\Files\AppData\IAppDataFactory;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\IConfig;
class MigrateBackgroundImages extends QueuedJob {
public const TIME_SENSITIVE = 0;
private IConfig $config;
private IAppManager $appManager;
private IAppDataFactory $appDataFactory;
private IJobList $jobList;
public function __construct(ITimeFactory $time, IAppDataFactory $appDataFactory, IConfig $config, IAppManager $appManager, IJobList $jobList) {
parent::__construct($time);
$this->config = $config;
$this->appManager = $appManager;
$this->appDataFactory = $appDataFactory;
$this->jobList = $jobList;
}
protected function run($argument): void {
if (!$this->appManager->isEnabledForUser('dashboard')) {
return;
}
$themingData = $this->appDataFactory->get(Application::APP_ID);
$dashboardData = $this->appDataFactory->get('dashboard');
$userIds = $this->config->getUsersForUserValue('theming', 'background', 'custom');
$notSoFastMode = \count($userIds) > 5000;
$reTrigger = false;
$processed = 0;
foreach ($userIds as $userId) {
try {
// precondition
if ($notSoFastMode) {
if ($this->config->getUserValue($userId, 'theming', 'background-migrated', '0') === '1') {
// already migrated
continue;
}
$reTrigger = true;
}
// migration
$file = $dashboardData->getFolder($userId)->getFile('background.jpg');
try {
$targetDir = $themingData->getFolder($userId);
} catch (NotFoundException $e) {
$targetDir = $themingData->newFolder($userId);
}
if (!$targetDir->fileExists('background.jpg')) {
$targetDir->newFile('background.jpg', $file->getContent());
}
$file->delete();
} catch (NotFoundException|NotPermittedException $e) {
}
// capture state
if ($notSoFastMode) {
$this->config->setUserValue($userId, 'theming', 'background-migrated', '1');
$processed++;
}
if ($processed > 4999) {
break;
}
}
if ($reTrigger) {
$this->jobList->add(self::class);
}
}
}

View File

@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace OCA\Theming\Migration;
use OCA\Theming\Jobs\MigrateBackgroundImages;
use OCP\BackgroundJob\IJobList;
use OCP\Migration\IOutput;
class InitBackgroundImagesMigration implements \OCP\Migration\IRepairStep {
private IJobList $jobList;
public function __construct(IJobList $jobList) {
$this->jobList = $jobList;
}
public function getName() {
return 'Initialize migration of background images from dashboard to theming app';
}
public function run(IOutput $output) {
$this->jobList->add(MigrateBackgroundImages::class);
}
}

View File

@@ -30,6 +30,7 @@ namespace OCA\Theming\Service;
use InvalidArgumentException;
use OC\User\NoUserException;
use OCA\Theming\AppInfo\Application;
use OCP\Files\AppData\IAppDataFactory;
use OCP\Files\File;
use OCP\Files\IAppData;
use OCP\Files\IRootFolder;
@@ -44,92 +45,111 @@ use OCP\PreConditionNotMetException;
class BackgroundService {
// true when the background is bright and need dark icons
public const THEMING_MODE_DARK = 'dark';
public const DEFAULT_COLOR = '#0082c9';
public const DEFAULT_ACCESSIBLE_COLOR = '#00639a';
public const SHIPPED_BACKGROUNDS = [
'anatoly-mikhaltsov-butterfly-wing-scale.jpg' => [
'attribution' => 'Butterfly wing scale (Anatoly Mikhaltsov, CC BY-SA)',
'attribution_url' => 'https://commons.wikimedia.org/wiki/File:%D0%A7%D0%B5%D1%88%D1%83%D0%B9%D0%BA%D0%B8_%D0%BA%D1%80%D1%8B%D0%BB%D0%B0_%D0%B1%D0%B0%D0%B1%D0%BE%D1%87%D0%BA%D0%B8.jpg',
'primary_color' => '#a53c17',
],
'bernie-cetonia-aurata-take-off-composition.jpg' => [
'attribution' => 'Cetonia aurata take off composition (Bernie, Public Domain)',
'attribution_url' => 'https://commons.wikimedia.org/wiki/File:Cetonia_aurata_take_off_composition_05172009.jpg',
'theming' => self::THEMING_MODE_DARK,
'primary_color' => '#56633d',
],
'dejan-krsmanovic-ribbed-red-metal.jpg' => [
'attribution' => 'Ribbed red metal (Dejan Krsmanovic, CC BY)',
'attribution_url' => 'https://www.flickr.com/photos/dejankrsmanovic/42971456774/',
'primary_color' => '#9c4236',
],
'eduardo-neves-pedra-azul.jpg' => [
'attribution' => 'Pedra azul milky way (Eduardo Neves, CC BY-SA)',
'attribution_url' => 'https://commons.wikimedia.org/wiki/File:Pedra_Azul_Milky_Way.jpg',
'primary_color' => '#4f6071',
],
'european-space-agency-barents-bloom.jpg' => [
'attribution' => 'Barents bloom (European Space Agency, CC BY-SA)',
'attribution_url' => 'https://www.esa.int/ESA_Multimedia/Images/2016/08/Barents_bloom',
'primary_color' => '#396475',
],
'hannes-fritz-flippity-floppity.jpg' => [
'attribution' => 'Flippity floppity (Hannes Fritz, CC BY-SA)',
'attribution_url' => 'http://hannes.photos/flippity-floppity',
'primary_color' => '#98415a',
],
'hannes-fritz-roulette.jpg' => [
'attribution' => 'Roulette (Hannes Fritz, CC BY-SA)',
'attribution_url' => 'http://hannes.photos/roulette',
'primary_color' => '#845334',
],
'hannes-fritz-sea-spray.jpg' => [
'attribution' => 'Sea spray (Hannes Fritz, CC BY-SA)',
'attribution_url' => 'http://hannes.photos/sea-spray',
'primary_color' => '#4f6071',
],
'kamil-porembinski-clouds.jpg' => [
'attribution' => 'Clouds (Kamil Porembiński, CC BY-SA)',
'attribution_url' => 'https://www.flickr.com/photos/paszczak000/8715851521/',
'primary_color' => self::DEFAULT_COLOR,
],
'bernard-spragg-new-zealand-fern.jpg' => [
'attribution' => 'New zealand fern (Bernard Spragg, CC0)',
'attribution_url' => 'https://commons.wikimedia.org/wiki/File:NZ_Fern.(Blechnum_chambersii)_(11263534936).jpg',
'primary_color' => '#316b26',
],
'rawpixel-pink-tapioca-bubbles.jpg' => [
'attribution' => 'Pink tapioca bubbles (Rawpixel, CC BY)',
'attribution_url' => 'https://www.flickr.com/photos/byrawpixel/27665140298/in/photostream/',
'theming' => self::THEMING_MODE_DARK,
'primary_color' => '#7b4e7e',
],
'nasa-waxing-crescent-moon.jpg' => [
'attribution' => 'Waxing crescent moon (NASA, Public Domain)',
'attribution_url' => 'https://www.nasa.gov/image-feature/a-waxing-crescent-moon',
'primary_color' => '#005ac1',
],
'tommy-chau-already.jpg' => [
'attribution' => 'Cityscape (Tommy Chau, CC BY)',
'attribution_url' => 'https://www.flickr.com/photos/90975693@N05/16910999368',
'primary_color' => '#6a2af4',
],
'tommy-chau-lion-rock-hill.jpg' => [
'attribution' => 'Lion rock hill (Tommy Chau, CC BY)',
'attribution_url' => 'https://www.flickr.com/photos/90975693@N05/17136440246',
'theming' => self::THEMING_MODE_DARK,
'primary_color' => '#7f4f70',
],
'lali-masriera-yellow-bricks.jpg' => [
'attribution' => 'Yellow bricks (Lali Masriera, CC BY)',
'attribution_url' => 'https://www.flickr.com/photos/visualpanic/3982464447',
'theming' => self::THEMING_MODE_DARK,
]
'primary_color' => '#7f5700',
],
];
private IRootFolder $rootFolder;
private IAppData $appData;
private IConfig $config;
private string $userId;
private IAppDataFactory $appDataFactory;
public function __construct(
IRootFolder $rootFolder,
IAppData $appData,
IConfig $config,
?string $userId
IRootFolder $rootFolder,
IAppDataFactory $appDataFactory,
IConfig $config,
?string $userId
) {
if ($userId === null) {
return;
}
$this->rootFolder = $rootFolder;
$this->appData = $appData;
$this->appData = $appDataFactory->get(Application::APP_ID);
$this->config = $config;
$this->userId = $userId;
$this->appDataFactory = $appDataFactory;
}
public function setDefaultBackground(): void {
@@ -176,6 +196,11 @@ class BackgroundService {
try {
return $this->getAppDataFolder()->getFile('background.jpg');
} catch (NotFoundException | NotPermittedException $e) {
try {
// Fallback can be removed in 26
$dashboardFolder = $this->appDataFactory->get('dashboard');
return $dashboardFolder->getFolder($this->userId)->getFile('background.jpg');
} catch (\Throwable $t) {}
}
}
return null;

View File

@@ -55,6 +55,7 @@ class JSDataService implements \JsonSerializable {
'url' => $this->themingDefaults->getBaseUrl(),
'slogan' => $this->themingDefaults->getSlogan(),
'color' => $this->themingDefaults->getColorPrimary(),
'defaultColor' => $this->themingDefaults->getDefaultColorPrimary(),
'imprintUrl' => $this->themingDefaults->getImprintUrl(),
'privacyUrl' => $this->themingDefaults->getPrivacyUrl(),
'inverted' => $this->util->invertTextColor($this->themingDefaults->getColorPrimary()),

View File

@@ -87,9 +87,9 @@ class ThemesService {
}
/** @var ITheme[] */
$themes = array_map(function($themeId) {
$themes = array_filter(array_map(function($themeId) {
return $this->getThemes()[$themeId];
}, $themesIds);
}, $themesIds));
// Filtering all themes with the same type
$filteredThemes = array_filter($themes, function(ITheme $t) use ($theme) {

View File

@@ -75,7 +75,7 @@ class Admin implements IDelegatedSettings {
'name' => $this->themingDefaults->getEntity(),
'url' => $this->themingDefaults->getBaseUrl(),
'slogan' => $this->themingDefaults->getSlogan(),
'color' => $this->themingDefaults->getColorPrimary(),
'color' => $this->themingDefaults->getDefaultColorPrimary(),
'uploadLogoRoute' => $this->urlGenerator->linkToRoute('theming.Theming.uploadImage'),
'canThemeIcons' => $this->imageManager->shouldReplaceIcons(),
'iconDocs' => $this->urlGenerator->linkToDocs('admin-theming-icons'),

View File

@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com>
*
* @author Joas Schilling <coding@schilljs.com>
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Theming\Themes;
use OCA\Theming\Util;
trait CommonThemeTrait {
public Util $util;
/**
* Generate primary-related variables
* This is shared between multiple themes because colorMainBackground and colorMainText
* will change in between.
*/
protected function generatePrimaryVariables(string $colorMainBackground, string $colorMainText): array {
$colorPrimaryLight = $this->util->mix($this->primaryColor, $colorMainBackground, -80);
$colorPrimaryElement = $this->util->elementColor($this->primaryColor);
$colorPrimaryElementLight = $this->util->mix($colorPrimaryElement, $colorMainBackground, -80);
// primary related colours
return [
'--color-primary' => $this->primaryColor,
'--color-primary-default' => $this->defaultPrimaryColor,
'--color-primary-text' => $this->util->invertTextColor($this->primaryColor) ? '#000000' : '#ffffff',
'--color-primary-hover' => $this->util->mix($this->primaryColor, $colorMainBackground, 60),
'--color-primary-light' => $colorPrimaryLight,
'--color-primary-light-text' => $this->util->mix($this->primaryColor, $this->util->invertTextColor($colorPrimaryLight) ? '#000000' : '#ffffff', -20),
'--color-primary-light-hover' => $this->util->mix($colorPrimaryLight, $colorMainText, 90),
'--color-primary-text-dark' => $this->util->darken($this->util->invertTextColor($this->primaryColor) ? '#000000' : '#ffffff', 7),
// used for buttons, inputs...
'--color-primary-element' => $colorPrimaryElement,
'--color-primary-element-text' => $this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff',
'--color-primary-element-hover' => $this->util->mix($colorPrimaryElement, $colorMainBackground, 60),
'--color-primary-element-light' => $colorPrimaryElementLight,
'--color-primary-element-light-text' => $this->util->mix($colorPrimaryElement, $this->util->invertTextColor($colorPrimaryElementLight) ? '#000000' : '#ffffff', -20),
'--color-primary-element-light-hover' => $this->util->mix($colorPrimaryElementLight, $colorMainText, 90),
'--color-primary-element-text-dark' => $this->util->darken($this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff', 7),
// to use like this: background-image: var(--gradient-primary-background);
'--gradient-primary-background' => 'linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-hover) 100%)',
];
}
}

View File

@@ -49,42 +49,51 @@ class DarkHighContrastTheme extends DarkTheme implements ITheme {
}
/**
* Try to keep this consistent with HighContrastTheme
* Keep this consistent with other HighContrast Themes
*/
public function getCSSVariables(): array {
$variables = parent::getCSSVariables();
$defaultVariables = parent::getCSSVariables();
$colorMainText = '#ffffff';
$colorMainBackground = '#000000';
$colorMainBackgroundRGB = join(',', $this->util->hexToRGB($colorMainBackground));
$variables['--color-main-background'] = $colorMainBackground;
$variables['--color-main-background-translucent'] = 'rgba(var(--color-main-background-rgb), .1)';
$variables['--color-main-text'] = $colorMainText;
return array_merge(
$defaultVariables,
$this->generatePrimaryVariables($colorMainBackground, $colorMainText),
[
'--color-main-background' => $colorMainBackground,
'--color-main-background-rgb' => $colorMainBackgroundRGB,
'--color-main-background-translucent' => 'rgba(var(--color-main-background-rgb), 1)',
'--color-main-text' => $colorMainText,
$variables['--color-background-dark'] = $this->util->lighten($colorMainBackground, 30);
$variables['--color-background-darker'] = $this->util->lighten($colorMainBackground, 30);
'--color-background-dark' => $this->util->lighten($colorMainBackground, 30),
'--color-background-darker' => $this->util->lighten($colorMainBackground, 30),
$variables['--color-placeholder-light'] = $this->util->lighten($colorMainBackground, 30);
$variables['--color-placeholder-dark'] = $this->util->lighten($colorMainBackground, 45);
'--color-main-background-blur' => $colorMainBackground,
'--filter-background-blur' => 'none',
$variables['--color-text-maxcontrast'] = $colorMainText;
$variables['--color-text-light'] = $colorMainText;
$variables['--color-text-lighter'] = $colorMainText;
'--color-placeholder-light' => $this->util->lighten($colorMainBackground, 30),
'--color-placeholder-dark' => $this->util->lighten($colorMainBackground, 45),
$variables['--color-scrollbar'] = $this->util->lighten($colorMainBackground, 35);
'--color-text-maxcontrast' => $colorMainText,
'--color-text-maxcontrast-background-blur' => $colorMainText,
'--color-text-light' => $colorMainText,
'--color-text-lighter' => $colorMainText,
// used for the icon loading animation
$variables['--color-loading-light'] = '#000000';
$variables['--color-loading-dark'] = '#dddddd';
'--color-scrollbar' => $this->util->lighten($colorMainBackground, 35),
// used for the icon loading animation
'--color-loading-light' => '#000000',
'--color-loading-dark' => '#dddddd',
$variables['--color-box-shadow-rgb'] = 'var(--color-main-text)';
$variables['--color-box-shadow'] = 'var(--color-main-text)';
'--color-box-shadow-rgb' => $colorMainText,
'--color-box-shadow' => $colorMainText,
$variables['--color-border'] = $this->util->lighten($colorMainBackground, 50);
$variables['--color-border-dark'] = $this->util->lighten($colorMainBackground, 50);
return $variables;
'--color-border' => $this->util->lighten($colorMainBackground, 50),
'--color-border-dark' => $this->util->lighten($colorMainBackground, 50),
]
);
}
public function getCustomCss(): string {

View File

@@ -54,45 +54,47 @@ class DarkTheme extends DefaultTheme implements ITheme {
$colorMainText = '#D8D8D8';
$colorMainBackground = '#171717';
$colorMainBackgroundRGB = join(',', $this->util->hexToRGB($colorMainBackground));
$colorTextMaxcontrast = $this->util->darken($colorMainText, 30);
$colorBoxShadow = $this->util->darken($colorMainBackground, 70);
$colorBoxShadowRGB = join(',', $this->util->hexToRGB($colorBoxShadow));
$colorPrimaryLight = $this->util->mix($this->primaryColor, $colorMainBackground, -80);
return array_merge($defaultVariables, [
'--color-main-text' => $colorMainText,
'--color-main-background' => $colorMainBackground,
'--color-main-background-rgb' => $colorMainBackgroundRGB,
return array_merge(
$defaultVariables,
$this->generatePrimaryVariables($colorMainBackground, $colorMainText),
[
'--color-main-text' => $colorMainText,
'--color-main-background' => $colorMainBackground,
'--color-main-background-rgb' => $colorMainBackgroundRGB,
'--color-scrollbar' => $this->util->lighten($colorMainBackground, 15),
'--color-scrollbar' => $this->util->lighten($colorMainBackground, 15),
'--color-background-hover' => $this->util->lighten($colorMainBackground, 4),
'--color-background-dark' => $this->util->lighten($colorMainBackground, 7),
'--color-background-darker' => $this->util->lighten($colorMainBackground, 14),
'--color-background-hover' => $this->util->lighten($colorMainBackground, 4),
'--color-background-dark' => $this->util->lighten($colorMainBackground, 7),
'--color-background-darker' => $this->util->lighten($colorMainBackground, 14),
'--color-placeholder-light' => $this->util->lighten($colorMainBackground, 10),
'--color-placeholder-dark' => $this->util->lighten($colorMainBackground, 20),
'--color-placeholder-light' => $this->util->lighten($colorMainBackground, 10),
'--color-placeholder-dark' => $this->util->lighten($colorMainBackground, 20),
'--color-primary-hover' => $this->util->mix($this->primaryColor, $colorMainBackground, 60),
'--color-primary-light' => $colorPrimaryLight,
'--color-primary-light-hover' => $this->util->mix($colorPrimaryLight, $colorMainText, 90),
'--color-primary-element' => $this->util->elementColor($this->primaryColor, false),
'--color-primary-element-hover' => $this->util->mix($this->util->elementColor($this->primaryColor, false), $colorMainBackground, 80),
'--color-primary-element-light' => $this->util->lighten($this->util->elementColor($this->primaryColor, false), 15),
'--color-text-maxcontrast' => $colorTextMaxcontrast,
'--color-text-maxcontrast-default' => $colorTextMaxcontrast,
'--color-text-maxcontrast-background-blur' => $this->util->lighten($colorTextMaxcontrast, 2),
'--color-text-light' => $this->util->darken($colorMainText, 10),
'--color-text-lighter' => $this->util->darken($colorMainText, 20),
'--color-text-maxcontrast' => $this->util->darken($colorMainText, 30),
'--color-text-light' => $this->util->darken($colorMainText, 10),
'--color-text-lighter' => $this->util->darken($colorMainText, 20),
// used for the icon loading animation
'--color-loading-light' => '#777',
'--color-loading-dark' => '#CCC',
'--color-loading-light' => '#777',
'--color-loading-dark' => '#CCC',
'--color-box-shadow' => $colorBoxShadow,
'--color-box-shadow-rgb' => $colorBoxShadowRGB,
'--color-box-shadow-rgb' => $colorBoxShadowRGB,
'--color-border' => $this->util->lighten($colorMainBackground, 7),
'--color-border-dark' => $this->util->lighten($colorMainBackground, 14),
'--color-border' => $this->util->lighten($colorMainBackground, 7),
'--color-border-dark' => $this->util->lighten($colorMainBackground, 14),
'--background-invert-if-dark' => 'invert(100%)',
'--background-invert-if-bright' => 'no',
]);
'--background-invert-if-dark' => 'invert(100%)',
'--background-invert-if-bright' => 'no',
]
);
}
}

View File

@@ -27,6 +27,7 @@ namespace OCA\Theming\Themes;
use OCA\Theming\AppInfo\Application;
use OCA\Theming\ImageManager;
use OCA\Theming\ITheme;
use OCA\Theming\Service\BackgroundService;
use OCA\Theming\ThemingDefaults;
use OCA\Theming\Util;
use OCP\App\IAppManager;
@@ -37,29 +38,41 @@ use OCP\IUserSession;
use OCP\Server;
class DefaultTheme implements ITheme {
use CommonThemeTrait;
public Util $util;
public ThemingDefaults $themingDefaults;
public IUserSession $userSession;
public IURLGenerator $urlGenerator;
public ImageManager $imageManager;
public IConfig $config;
public IL10N $l;
public string $defaultPrimaryColor;
public string $primaryColor;
public function __construct(Util $util,
ThemingDefaults $themingDefaults,
IUserSession $userSession,
IURLGenerator $urlGenerator,
ImageManager $imageManager,
IConfig $config,
IL10N $l) {
$this->util = $util;
$this->themingDefaults = $themingDefaults;
$this->userSession = $userSession;
$this->urlGenerator = $urlGenerator;
$this->imageManager = $imageManager;
$this->config = $config;
$this->l = $l;
$this->defaultPrimaryColor = $this->themingDefaults->getDefaultColorPrimary();
$this->primaryColor = $this->themingDefaults->getColorPrimary();
// Override default defaultPrimaryColor if set to improve accessibility
if ($this->primaryColor === BackgroundService::DEFAULT_COLOR) {
$this->primaryColor = BackgroundService::DEFAULT_ACCESSIBLE_COLOR;
}
}
public function getId(): string {
@@ -89,20 +102,18 @@ class DefaultTheme implements ITheme {
public function getCSSVariables(): array {
$colorMainText = '#222222';
$colorMainTextRgb = join(',', $this->util->hexToRGB($colorMainText));
$colorTextMaxcontrast = $this->util->lighten($colorMainText, 33);
$colorMainBackground = '#ffffff';
$colorMainBackgroundRGB = join(',', $this->util->hexToRGB($colorMainBackground));
$colorBoxShadow = $this->util->darken($colorMainBackground, 70);
$colorBoxShadowRGB = join(',', $this->util->hexToRGB($colorBoxShadow));
$colorPrimaryLight = $this->util->mix($this->primaryColor, $colorMainBackground, -80);
$colorPrimaryElement = $this->util->elementColor($this->primaryColor);
$colorPrimaryElementLight = $this->util->mix($colorPrimaryElement, $colorMainBackground, -80);
$hasCustomLogoHeader = $this->imageManager->hasImage('logo') || $this->imageManager->hasImage('logoheader');
$hasCustomPrimaryColour = !empty($this->config->getAppValue(Application::APP_ID, 'color'));
$variables = [
'--color-main-background' => $colorMainBackground,
'--color-main-background-not-plain' => $this->themingDefaults->getColorPrimary(),
'--color-main-background-rgb' => $colorMainBackgroundRGB,
'--color-main-background-translucent' => 'rgba(var(--color-main-background-rgb), .97)',
'--color-main-background-blur' => 'rgba(var(--color-main-background-rgb), .8)',
@@ -119,28 +130,11 @@ class DefaultTheme implements ITheme {
'--color-placeholder-light' => $this->util->darken($colorMainBackground, 10),
'--color-placeholder-dark' => $this->util->darken($colorMainBackground, 20),
// primary related colours
'--color-primary' => $this->primaryColor,
'--color-primary-text' => $this->util->invertTextColor($this->primaryColor) ? '#000000' : '#ffffff',
'--color-primary-hover' => $this->util->mix($this->primaryColor, $colorMainBackground, 60),
'--color-primary-light' => $colorPrimaryLight,
'--color-primary-light-text' => $this->primaryColor,
'--color-primary-light-hover' => $this->util->mix($colorPrimaryLight, $colorMainText, 90),
'--color-primary-text-dark' => $this->util->darken($this->util->invertTextColor($this->primaryColor) ? '#000000' : '#ffffff', 7),
// used for buttons, inputs...
'--color-primary-element' => $colorPrimaryElement,
'--color-primary-element-text' => $this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff',
'--color-primary-element-hover' => $this->util->mix($colorPrimaryElement, $colorMainBackground, 60),
'--color-primary-element-light' => $colorPrimaryElementLight,
'--color-primary-element-light-text' => $colorPrimaryElement,
'--color-primary-element-light-hover' => $this->util->mix($colorPrimaryElementLight, $colorMainText, 90),
'--color-primary-element-text-dark' => $this->util->darken($this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff', 7),
// to use like this: background-image: var(--gradient-primary-background);
'--gradient-primary-background' => 'linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-hover) 100%)',
// max contrast for WCAG compliance
'--color-main-text' => $colorMainText,
'--color-text-maxcontrast' => $this->util->lighten($colorMainText, 33),
'--color-text-maxcontrast' => $colorTextMaxcontrast,
'--color-text-maxcontrast-default' => $colorTextMaxcontrast,
'--color-text-maxcontrast-background-blur' => $this->util->darken($colorTextMaxcontrast, 7),
'--color-text-light' => $colorMainText,
'--color-text-lighter' => $this->util->lighten($colorMainText, 33),
@@ -211,6 +205,9 @@ class DefaultTheme implements ITheme {
'--image-main-background' => "url('" . $this->urlGenerator->imagePath('core', 'app-background.jpg') . "')",
];
// Primary variables
$variables = array_merge($variables, $this->generatePrimaryVariables($colorMainBackground, $colorMainText));
$backgroundDeleted = $this->config->getAppValue(Application::APP_ID, 'backgroundMime', '') === 'backgroundColor';
// If primary as background has been request or if we have a custom primary colour
// let's not define the background image
@@ -239,15 +236,17 @@ class DefaultTheme implements ITheme {
}
$appManager = Server::get(IAppManager::class);
$userSession = Server::get(IUserSession::class);
$user = $userSession->getUser();
$user = $this->userSession->getUser();
if ($appManager->isEnabledForUser(Application::APP_ID) && $user !== null) {
$themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background', 'default');
if ($themingBackground === 'custom') {
$variables['--image-main-background'] = "url('" . $this->urlGenerator->linkToRouteAbsolute('theming.theming.getBackground') . "')";
} elseif ($themingBackground !== 'default' && substr($themingBackground, 0, 1) !== '#') {
$variables['--image-main-background'] = "url('" . $this->urlGenerator->linkToRouteAbsolute('theming.userTheme.getBackground') . "')";
} elseif (isset(BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground])) {
$variables['--image-main-background'] = "url('" . $this->urlGenerator->linkTo(Application::APP_ID, "/img/background/$themingBackground") . "')";
} elseif (substr($themingBackground, 0, 1) === '#') {
unset($variables['--image-main-background']);
$variables['--color-main-background-plain'] = $this->themingDefaults->getColorPrimary();
}
}

View File

@@ -29,7 +29,7 @@ use OCA\Theming\ITheme;
class HighContrastTheme extends DefaultTheme implements ITheme {
public function getId(): string {
return 'highcontrast';
return 'light-highcontrast';
}
public function getMediaQuery(): string {
@@ -48,41 +48,52 @@ class HighContrastTheme extends DefaultTheme implements ITheme {
return $this->l->t('A high contrast mode to ease your navigation. Visual quality will be reduced but clarity will be increased.');
}
/**
* Keep this consistent with other HighContrast Themes
*/
public function getCSSVariables(): array {
$variables = parent::getCSSVariables();
$defaultVariables = parent::getCSSVariables();
$colorMainText = '#000000';
$colorMainBackground = '#ffffff';
$colorMainBackgroundRGB = join(',', $this->util->hexToRGB($colorMainBackground));
$variables['--color-main-background'] = $colorMainBackground;
$variables['--color-main-background-translucent'] = 'rgba(var(--color-main-background-rgb), .1)';
$variables['--color-main-text'] = $colorMainText;
return array_merge(
$defaultVariables,
$this->generatePrimaryVariables($colorMainBackground, $colorMainText),
[
'--color-main-background' => $colorMainBackground,
'--color-main-background-rgb' => $colorMainBackgroundRGB,
'--color-main-background-translucent' => 'rgba(var(--color-main-background-rgb), 1)',
'--color-main-text' => $colorMainText,
$variables['--color-background-dark'] = $this->util->darken($colorMainBackground, 30);
$variables['--color-background-darker'] = $this->util->darken($colorMainBackground, 30);
'--color-background-dark' => $this->util->darken($colorMainBackground, 30),
'--color-background-darker' => $this->util->darken($colorMainBackground, 30),
$variables['--color-main-background-blur'] = $colorMainBackground;
$variables['--filter-background-blur'] = 'none';
'--color-main-background-blur' => $colorMainBackground,
'--filter-background-blur' => 'none',
$variables['--color-placeholder-light'] = $this->util->darken($colorMainBackground, 30);
$variables['--color-placeholder-dark'] = $this->util->darken($colorMainBackground, 45);
'--color-placeholder-light' => $this->util->darken($colorMainBackground, 30),
'--color-placeholder-dark' => $this->util->darken($colorMainBackground, 45),
$variables['--color-text-maxcontrast'] = 'var(--color-main-text)';
$variables['--color-text-light'] = 'var(--color-main-text)';
$variables['--color-text-lighter'] = 'var(--color-main-text)';
'--color-text-maxcontrast' => $colorMainText,
'--color-text-maxcontrast-background-blur' => $colorMainText,
'--color-text-light' => $colorMainText,
'--color-text-lighter' => $colorMainText,
$variables['--color-scrollbar'] = $this->util->darken($colorMainBackground, 25);
'--color-scrollbar' => $this->util->darken($colorMainBackground, 25),
// used for the icon loading animation
$variables['--color-loading-light'] = '#dddddd';
$variables['--color-loading-dark'] = '#000000';
// used for the icon loading animation
'--color-loading-light' => '#dddddd',
'--color-loading-dark' => '#000000',
$variables['--color-box-shadow-rgb'] = 'var(--color-main-text)';
$variables['--color-box-shadow'] = 'var(--color-main-text)';
'--color-box-shadow-rgb' => $colorMainText,
'--color-box-shadow' => $colorMainText,
$variables['--color-border'] = $this->util->darken($colorMainBackground, 50);
$variables['--color-border-dark'] = $this->util->darken($colorMainBackground, 50);
return $variables;
'--color-border' => $this->util->darken($colorMainBackground, 50),
'--color-border-dark' => $this->util->darken($colorMainBackground, 50),
]
);
}
public function getCustomCss(): string {

View File

@@ -40,6 +40,8 @@
*/
namespace OCA\Theming;
use OCA\Theming\AppInfo\Application;
use OCA\Theming\Service\BackgroundService;
use OCP\App\AppPathNotFoundException;
use OCP\App\IAppManager;
use OCP\Files\NotFoundException;
@@ -49,47 +51,31 @@ use OCP\IConfig;
use OCP\IL10N;
use OCP\INavigationManager;
use OCP\IURLGenerator;
use OCP\IUserSession;
class ThemingDefaults extends \OC_Defaults {
/** @var IConfig */
private $config;
/** @var IL10N */
private $l;
/** @var ImageManager */
private $imageManager;
/** @var IURLGenerator */
private $urlGenerator;
/** @var ICacheFactory */
private $cacheFactory;
/** @var Util */
private $util;
/** @var IAppManager */
private $appManager;
/** @var INavigationManager */
private $navigationManager;
private IConfig $config;
private IL10N $l;
private ImageManager $imageManager;
private IUserSession $userSession;
private IURLGenerator $urlGenerator;
private ICacheFactory $cacheFactory;
private Util $util;
private IAppManager $appManager;
private INavigationManager $navigationManager;
/** @var string */
private $name;
/** @var string */
private $title;
/** @var string */
private $entity;
/** @var string */
private $productName;
/** @var string */
private $url;
/** @var string */
private $color;
private string $name;
private string $title;
private string $entity;
private string $productName;
private string $url;
private string $color;
/** @var string */
private $iTunesAppId;
/** @var string */
private $iOSClientUrl;
/** @var string */
private $AndroidClientUrl;
/** @var string */
private $FDroidClientUrl;
private string $iTunesAppId;
private string $iOSClientUrl;
private string $AndroidClientUrl;
private string $FDroidClientUrl;
/**
* ThemingDefaults constructor.
@@ -97,6 +83,7 @@ class ThemingDefaults extends \OC_Defaults {
* @param IConfig $config
* @param IL10N $l
* @param ImageManager $imageManager
* @param IUserSession $userSession
* @param IURLGenerator $urlGenerator
* @param ICacheFactory $cacheFactory
* @param Util $util
@@ -104,6 +91,7 @@ class ThemingDefaults extends \OC_Defaults {
*/
public function __construct(IConfig $config,
IL10N $l,
IUserSession $userSession,
IURLGenerator $urlGenerator,
ICacheFactory $cacheFactory,
Util $util,
@@ -115,6 +103,7 @@ class ThemingDefaults extends \OC_Defaults {
$this->config = $config;
$this->l = $l;
$this->imageManager = $imageManager;
$this->userSession = $userSession;
$this->urlGenerator = $urlGenerator;
$this->cacheFactory = $cacheFactory;
$this->util = $util;
@@ -225,11 +214,47 @@ class ThemingDefaults extends \OC_Defaults {
/**
* Color that is used for the header as well as for mail headers
*
* @return string
*/
public function getColorPrimary() {
$color = $this->config->getAppValue('theming', 'color', $this->color);
public function getColorPrimary(): string {
$user = $this->userSession->getUser();
// admin-defined primary color
$defaultColor = $this->getDefaultColorPrimary();
// user-defined primary color
$themingBackground = '';
if (!empty($user)) {
$themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background', '');
// If the user selected the default background
if ($themingBackground === '') {
return BackgroundService::DEFAULT_COLOR;
}
// If the user selected a specific colour
if (preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $themingBackground)) {
return $themingBackground;
}
// if the user-selected background is a background reference
if (isset(BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground]['primary_color'])) {
return BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground]['primary_color'];
}
}
// If the default color is not valid, return the default background one
if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $defaultColor)) {
return BackgroundService::DEFAULT_COLOR;
}
// Finally, return the system global primary color
return $defaultColor;
}
/**
* Return the default color primary
*/
public function getDefaultColorPrimary(): string {
$color = $this->config->getAppValue(Application::APP_ID, 'color');
if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $color)) {
$color = '#0082c9';
}
@@ -408,7 +433,7 @@ class ThemingDefaults extends \OC_Defaults {
/**
* Increases the cache buster key
*/
private function increaseCacheBuster(): void {
public function increaseCacheBuster(): void {
$cacheBusterKey = (int)$this->config->getAppValue('theming', 'cachebuster', '0');
$this->config->setAppValue('theming', 'cachebuster', (string)($cacheBusterKey + 1));
$this->cacheFactory->createDistributed('theming-')->clear();

View File

@@ -74,7 +74,7 @@
</template>
<script>
import { generateOcsUrl, imagePath } from '@nextcloud/router'
import { generateOcsUrl } from '@nextcloud/router'
import { loadState } from '@nextcloud/initial-state'
import axios from '@nextcloud/axios'
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch'
@@ -83,8 +83,6 @@ import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection'
import BackgroundSettings from './components/BackgroundSettings.vue'
import ItemPreview from './components/ItemPreview.vue'
import { getBackgroundUrl } from '../src/helpers/getBackgroundUrl.js'
const availableThemes = loadState('theming', 'themes', [])
const enforceTheme = loadState('theming', 'enforceTheme', '')
const shortcutsDisabled = loadState('theming', 'shortcutsDisabled', false)
@@ -111,24 +109,12 @@ export default {
enforceTheme,
shortcutsDisabled,
background,
backgroundVersion,
themingDefaultBackground,
}
},
computed: {
backgroundImage() {
return getBackgroundUrl(this.background, backgroundVersion, this.themingDefaultBackground)
},
backgroundStyle() {
if ((this.background === 'default' && this.themingDefaultBackground === 'backgroundColor')
|| this.background.match(/#[0-9A-Fa-f]{6}/g)) {
return null
}
return {
backgroundImage: this.background === 'default' ? 'var(--image-main-background)' : `url('${this.backgroundImage}')`,
}
},
themes() {
return this.availableThemes.filter(theme => theme.type === 1)
},
@@ -170,20 +156,22 @@ export default {
},
},
mounted() {
this.updateGlobalStyles()
},
watch: {
shortcutsDisabled(newState) {
this.changeShortcutsDisabled(newState)
},
},
mounted() {
this.updateGlobalStyles()
},
methods: {
updateBackground(data) {
this.background = (data.type === 'custom' || data.type === 'default') ? data.type : data.value
this.backgroundVersion = data.version
this.updateGlobalStyles()
this.$emit('update:background')
},
updateGlobalStyles() {
// Override primary-invert-if-bright and color-primary-text if background is set
@@ -199,17 +187,6 @@ export default {
// document.body.removeAttribute('data-theme-light')
// document.body.setAttribute('data-theme-dark', 'true')
}
const themeElements = [document.documentElement, document.querySelector('#header'), document.querySelector('body')]
for (const element of themeElements) {
if (this.background === 'default') {
element.style.setProperty('--image-main-background', `url('${imagePath('core', 'app-background.jpg')}')`)
} else if (this.background.match(/#[0-9A-Fa-f]{6}/g)) {
element.style.setProperty('--image-main-background', undefined)
} else {
element.style.setProperty('--image-main-background', this.backgroundStyle.backgroundImage)
}
}
},
changeTheme({ enabled, id }) {
// Reset selected and select new one

View File

@@ -25,42 +25,67 @@
<template>
<div class="background-selector">
<!-- Custom background -->
<button class="background filepicker"
:class="{ active: background === 'custom' }"
tabindex="0"
@click="pickFile">
{{ t('theming', 'Pick from Files') }}
</button>
<!-- Default background -->
<button class="background default"
tabindex="0"
:class="{ 'icon-loading': loading === 'default', active: background === 'default' }"
@click="setDefault">
{{ t('theming', 'Default image') }}
</button>
<!-- Custom color picker -->
<NcColorPicker v-model="Theming.color" @input="debouncePickColor">
<button class="background color"
:class="{ active: background === Theming.color}"
tabindex="0"
:data-color="Theming.color"
:data-color-bright="invertTextColor(Theming.color)"
:style="{ backgroundColor: Theming.color, color: invertTextColor(Theming.color) ? '#000000' : '#ffffff'}">
{{ t('theming', 'Custom color') }}
</button>
</NcColorPicker>
<!-- Default admin primary color -->
<button class="background color"
:class="{ active: background === 'custom' }"
:class="{ active: background === Theming.defaultColor }"
tabindex="0"
@click="pickColor">
:data-color="Theming.defaultColor"
:data-color-bright="invertTextColor(Theming.defaultColor)"
:style="{ color: invertTextColor(Theming.defaultColor) ? '#000000' : '#ffffff'}"
@click="debouncePickColor">
{{ t('theming', 'Plain background') }}
</button>
<!-- Background set selection -->
<button v-for="shippedBackground in shippedBackgrounds"
:key="shippedBackground.name"
v-tooltip="shippedBackground.details.attribution"
:class="{ 'icon-loading': loading === shippedBackground.name, active: background === shippedBackground.name }"
tabindex="0"
class="background"
:data-color-bright="shippedBackground.details.theming === 'dark'"
:style="{ 'background-image': 'url(' + shippedBackground.preview + ')' }"
@click="setShipped(shippedBackground.name)" />
</div>
</template>
<script>
import axios from '@nextcloud/axios'
import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip'
import { generateUrl } from '@nextcloud/router'
import { loadState } from '@nextcloud/initial-state'
import { getBackgroundUrl } from '../helpers/getBackgroundUrl.js'
import { loadState } from '@nextcloud/initial-state'
import { prefixWithBaseUrl } from '../helpers/prefixWithBaseUrl.js'
import axios from '@nextcloud/axios'
import debounce from 'debounce'
import NcColorPicker from '@nextcloud/vue/dist/Components/NcColorPicker'
import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip'
const shippedBackgroundList = loadState('theming', 'shippedBackgrounds')
@@ -69,6 +94,11 @@ export default {
directives: {
Tooltip,
},
components: {
NcColorPicker,
},
props: {
background: {
type: String,
@@ -79,12 +109,15 @@ export default {
default: '',
},
},
data() {
return {
backgroundImage: generateUrl('/apps/theming/background') + '?v=' + Date.now(),
loading: false,
Theming: loadState('theming', 'data', {}),
}
},
computed: {
shippedBackgrounds() {
return Object.keys(shippedBackgroundList).map(fileName => {
@@ -97,7 +130,39 @@ export default {
})
},
},
methods: {
/**
* Do we need to invert the text if color is too bright?
*
* @param {string} color the hex color
*/
invertTextColor(color) {
return this.calculateLuma(color) > 0.6
},
/**
* Calculate luminance of provided hex color
*
* @param {string} color the hex color
*/
calculateLuma(color) {
const [red, green, blue] = this.hexToRGB(color)
return (0.2126 * red + 0.7152 * green + 0.0722 * blue) / 255
},
/**
* Convert hex color to RGB
*
* @param {string} hex the hex color
*/
hexToRGB(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
return result
? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)]
: null
},
async update(data) {
const background = data.type === 'custom' || data.type === 'default' ? data.type : data.value
this.backgroundImage = getBackgroundUrl(background, data.version, this.themingDefaultBackground)
@@ -113,27 +178,35 @@ export default {
}
image.src = this.backgroundImage
},
async setDefault() {
this.loading = 'default'
const result = await axios.post(generateUrl('/apps/theming/background/default'))
this.update(result.data)
},
async setShipped(shipped) {
this.loading = shipped
const result = await axios.post(generateUrl('/apps/theming/background/shipped'), { value: shipped })
this.update(result.data)
},
async setFile(path) {
this.loading = 'custom'
const result = await axios.post(generateUrl('/apps/theming/background/custom'), { value: path })
this.update(result.data)
},
async pickColor() {
debouncePickColor: debounce(function() {
this.pickColor(...arguments)
}, 200),
async pickColor(event) {
this.loading = 'color'
const color = OCA && OCA.Theming ? OCA.Theming.color : '#0082c9'
const color = event?.target?.dataset?.color || this.Theming?.color || '#0082c9'
const result = await axios.post(generateUrl('/apps/theming/background/color'), { value: color })
this.update(result.data)
},
pickFile() {
window.OC.dialogs.filepicker(t('theming', 'Insert from {productName}', { productName: OC.theme.name }), (path, type) => {
if (type === OC.dialogs.FILEPICKER_TYPE_CHOOSE) {
@@ -171,7 +244,7 @@ export default {
}
&.color {
background-color: var(--color-primary);
background-color: var(--color-primary-default);
color: var(--color-primary-text);
}
@@ -181,14 +254,20 @@ export default {
border: 2px solid var(--color-primary);
}
&.active:not(.icon-loading):after {
background-image: var(--icon-checkmark-white);
background-repeat: no-repeat;
background-position: center;
background-size: 44px;
content: '';
display: block;
height: 100%;
&.active:not(.icon-loading) {
&:after {
background-image: var(--icon-checkmark-white);
background-repeat: no-repeat;
background-position: center;
background-size: 44px;
content: '';
display: block;
height: 100%;
}
&[data-color-bright]:after {
background-image: var(--icon-checkmark-dark);
}
}
}
}

View File

@@ -22,8 +22,6 @@
*
*/
// FIXME hoist this into a package? The same logic is used in `apps/dashboard/src/helpers/getBackgroundUrl.js`
import { generateUrl } from '@nextcloud/router'
import { prefixWithBaseUrl } from './prefixWithBaseUrl.js'

View File

@@ -20,8 +20,6 @@
*
*/
// FIXME hoist this into a package? The same logic is used in `apps/dashboard/src/helpers/prefixWithBaseUrl.js`
import { generateFilePath } from '@nextcloud/router'
export const prefixWithBaseUrl = (url) => generateFilePath('theming', '', 'img/background/') + url

View File

@@ -30,3 +30,15 @@ Vue.prototype.t = t
const View = Vue.extend(App)
const theming = new View()
theming.$mount('#theming')
theming.$on('update:background', () => {
// Refresh server-side generated theming CSS
[...document.head.querySelectorAll('link.theme')].forEach(theme => {
const url = new URL(theme.href)
url.searchParams.set('v', Date.now())
const newTheme = theme.cloneNode()
newTheme.href = url.toString()
newTheme.onload = () => theme.remove()
document.head.append(newTheme)
})
})

View File

@@ -33,6 +33,7 @@ use OCA\Theming\Themes\DyslexiaFont;
use OCA\Theming\Themes\HighContrastTheme;
use OCA\Theming\Service\ThemesService;
use OCA\Theming\Themes\LightTheme;
use OCA\Theming\ThemingDefaults;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSBadRequestException;
use OCP\IConfig;
@@ -54,6 +55,8 @@ class UserThemeControllerTest extends TestCase {
private $userSession;
/** @var ThemeService|MockObject */
private $themesService;
/** @var ThemingDefaults */
private $themingDefaults;
/** @var BackgroundService|MockObject */
private $backgroundService;
@@ -66,13 +69,14 @@ class UserThemeControllerTest extends TestCase {
$this->config = $this->createMock(IConfig::class);
$this->userSession = $this->createMock(IUserSession::class);
$this->themesService = $this->createMock(ThemesService::class);
$this->themingDefaults = $this->createMock(ThemingDefaults::class);
$this->backgroundService = $this->createMock(BackgroundService::class);
$this->themes = [
'default' => $this->createMock(DefaultTheme::class),
'light' => $this->createMock(LightTheme::class),
'dark' => $this->createMock(DarkTheme::class),
'highcontrast' => $this->createMock(HighContrastTheme::class),
'light-highcontrast' => $this->createMock(HighContrastTheme::class),
'dark-highcontrast' => $this->createMock(DarkHighContrastTheme::class),
'opendyslexic' => $this->createMock(DyslexiaFont::class),
];
@@ -91,6 +95,7 @@ class UserThemeControllerTest extends TestCase {
$this->config,
$this->userSession,
$this->themesService,
$this->themingDefaults,
$this->backgroundService,
);
@@ -102,7 +107,7 @@ class UserThemeControllerTest extends TestCase {
['default'],
['light'],
['dark'],
['highcontrast'],
['light-highcontrast'],
['dark-highcontrast'],
['opendyslexic'],
['', OCSBadRequestException::class],

View File

@@ -68,6 +68,10 @@ class ThemesServiceTest extends TestCase {
->method('getColorPrimary')
->willReturn('#0082c9');
$this->themingDefaults->expects($this->any())
->method('getDefaultColorPrimary')
->willReturn('#0082c9');
$this->initThemes();
$this->themesService = new ThemesService(
@@ -84,7 +88,7 @@ class ThemesServiceTest extends TestCase {
'default',
'light',
'dark',
'highcontrast',
'light-highcontrast',
'dark-highcontrast',
'opendyslexic',
];
@@ -98,7 +102,7 @@ class ThemesServiceTest extends TestCase {
['dark', [], ['dark']],
['dark', ['dark'], ['dark']],
['opendyslexic', ['dark'], ['dark', 'opendyslexic']],
['dark', ['highcontrast', 'opendyslexic'], ['opendyslexic', 'dark']],
['dark', ['light-highcontrast', 'opendyslexic'], ['opendyslexic', 'dark']],
];
}
@@ -132,7 +136,7 @@ class ThemesServiceTest extends TestCase {
['dark', [], []],
['dark', ['dark'], []],
['opendyslexic', ['dark', 'opendyslexic'], ['dark'], ],
['highcontrast', ['opendyslexic'], ['opendyslexic']],
['light-highcontrast', ['opendyslexic'], ['opendyslexic']],
];
}
@@ -156,7 +160,7 @@ class ThemesServiceTest extends TestCase {
->method('getUserValue')
->with('user', Application::APP_ID, 'enabled-themes', '[]')
->willReturn(json_encode($enabledThemes));
$this->assertEquals($expectedEnabled, $this->themesService->disableTheme($this->themes[$toDisable]));
}
@@ -167,7 +171,7 @@ class ThemesServiceTest extends TestCase {
['dark', [], false],
['dark', ['dark'], true],
['opendyslexic', ['dark', 'opendyslexic'], true],
['highcontrast', ['opendyslexic'], false],
['light-highcontrast', ['opendyslexic'], false],
];
}
@@ -190,7 +194,7 @@ class ThemesServiceTest extends TestCase {
->method('getUserValue')
->with('user', Application::APP_ID, 'enabled-themes', '[]')
->willReturn(json_encode($enabledThemes));
$this->assertEquals($expected, $this->themesService->isEnabled($this->themes[$themeId]));
}
@@ -281,6 +285,7 @@ class ThemesServiceTest extends TestCase {
'default' => new DefaultTheme(
$util,
$this->themingDefaults,
$this->userSession,
$urlGenerator,
$imageManager,
$this->config,
@@ -289,6 +294,7 @@ class ThemesServiceTest extends TestCase {
'light' => new LightTheme(
$util,
$this->themingDefaults,
$this->userSession,
$urlGenerator,
$imageManager,
$this->config,
@@ -297,14 +303,16 @@ class ThemesServiceTest extends TestCase {
'dark' => new DarkTheme(
$util,
$this->themingDefaults,
$this->userSession,
$urlGenerator,
$imageManager,
$this->config,
$l10n,
),
'highcontrast' => new HighContrastTheme(
'light-highcontrast' => new HighContrastTheme(
$util,
$this->themingDefaults,
$this->userSession,
$urlGenerator,
$imageManager,
$this->config,
@@ -313,6 +321,7 @@ class ThemesServiceTest extends TestCase {
'dark-highcontrast' => new DarkHighContrastTheme(
$util,
$this->themingDefaults,
$this->userSession,
$urlGenerator,
$imageManager,
$this->config,
@@ -321,6 +330,7 @@ class ThemesServiceTest extends TestCase {
'opendyslexic' => new DyslexiaFont(
$util,
$this->themingDefaults,
$this->userSession,
$urlGenerator,
$imageManager,
$this->config,

View File

@@ -97,7 +97,7 @@ class AdminTest extends TestCase {
->willReturn('MySlogan');
$this->themingDefaults
->expects($this->once())
->method('getColorPrimary')
->method('getDefaultColorPrimary')
->willReturn('#fff');
$this->urlGenerator
->expects($this->once())
@@ -156,7 +156,7 @@ class AdminTest extends TestCase {
->willReturn('MySlogan');
$this->themingDefaults
->expects($this->once())
->method('getColorPrimary')
->method('getDefaultColorPrimary')
->willReturn('#fff');
$this->urlGenerator
->expects($this->once())

View File

@@ -45,6 +45,7 @@ use OCP\AppFramework\Services\IInitialState;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUserSession;
use Test\TestCase;
class PersonalTest extends TestCase {
@@ -83,7 +84,7 @@ class PersonalTest extends TestCase {
$this->formatThemeForm('default'),
$this->formatThemeForm('light'),
$this->formatThemeForm('dark'),
$this->formatThemeForm('highcontrast'),
$this->formatThemeForm('light-highcontrast'),
$this->formatThemeForm('dark-highcontrast'),
$this->formatThemeForm('opendyslexic'),
]],
@@ -128,6 +129,7 @@ class PersonalTest extends TestCase {
private function initThemes() {
$util = $this->createMock(Util::class);
$themingDefaults = $this->createMock(ThemingDefaults::class);
$userSession = $this->createMock(IUserSession::class);
$urlGenerator = $this->createMock(IURLGenerator::class);
$imageManager = $this->createMock(ImageManager::class);
$config = $this->createMock(IConfig::class);
@@ -136,11 +138,16 @@ class PersonalTest extends TestCase {
$themingDefaults->expects($this->any())
->method('getColorPrimary')
->willReturn('#0082c9');
$themingDefaults->expects($this->any())
->method('getDefaultColorPrimary')
->willReturn('#0082c9');
$this->themes = [
'default' => new DefaultTheme(
$util,
$themingDefaults,
$userSession,
$urlGenerator,
$imageManager,
$config,
@@ -149,6 +156,7 @@ class PersonalTest extends TestCase {
'light' => new LightTheme(
$util,
$themingDefaults,
$userSession,
$urlGenerator,
$imageManager,
$config,
@@ -157,14 +165,16 @@ class PersonalTest extends TestCase {
'dark' => new DarkTheme(
$util,
$themingDefaults,
$userSession,
$urlGenerator,
$imageManager,
$config,
$l10n,
),
'highcontrast' => new HighContrastTheme(
'light-highcontrast' => new HighContrastTheme(
$util,
$themingDefaults,
$userSession,
$urlGenerator,
$imageManager,
$config,
@@ -173,6 +183,7 @@ class PersonalTest extends TestCase {
'dark-highcontrast' => new DarkHighContrastTheme(
$util,
$themingDefaults,
$userSession,
$urlGenerator,
$imageManager,
$config,
@@ -181,6 +192,7 @@ class PersonalTest extends TestCase {
'opendyslexic' => new DyslexiaFont(
$util,
$themingDefaults,
$userSession,
$urlGenerator,
$imageManager,
$config,

View File

@@ -32,6 +32,7 @@ use OCP\Files\IAppData;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUserSession;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
@@ -52,6 +53,7 @@ class DefaultThemeTest extends TestCase {
protected function setUp(): void {
$this->themingDefaults = $this->createMock(ThemingDefaults::class);
$this->userSession = $this->createMock(IUserSession::class);
$this->urlGenerator = $this->createMock(IURLGenerator::class);
$this->imageManager = $this->createMock(ImageManager::class);
$this->config = $this->createMock(IConfig::class);
@@ -68,6 +70,11 @@ class DefaultThemeTest extends TestCase {
->method('getColorPrimary')
->willReturn('#0082c9');
$this->themingDefaults
->expects($this->any())
->method('getDefaultColorPrimary')
->willReturn('#0082c9');
$this->l10n
->expects($this->any())
->method('t')
@@ -85,6 +92,7 @@ class DefaultThemeTest extends TestCase {
$this->defaultTheme = new DefaultTheme(
$util,
$this->themingDefaults,
$this->userSession,
$this->urlGenerator,
$this->imageManager,
$this->config,

View File

@@ -56,6 +56,7 @@ class DyslexiaFontTest extends TestCase {
protected function setUp(): void {
$this->themingDefaults = $this->createMock(ThemingDefaults::class);
$this->userSession = $this->createMock(IUserSession::class);
$this->imageManager = $this->createMock(ImageManager::class);
$this->config = $this->createMock(IConfig::class);
$this->l10n = $this->createMock(IL10N::class);
@@ -83,6 +84,11 @@ class DyslexiaFontTest extends TestCase {
->method('getColorPrimary')
->willReturn('#0082c9');
$this->themingDefaults
->expects($this->any())
->method('getDefaultColorPrimary')
->willReturn('#0082c9');
$this->l10n
->expects($this->any())
->method('t')
@@ -93,6 +99,7 @@ class DyslexiaFontTest extends TestCase {
$this->dyslexiaFont = new DyslexiaFont(
$util,
$this->themingDefaults,
$this->userSession,
$this->urlGenerator,
$this->imageManager,
$this->config,
@@ -142,7 +149,7 @@ class DyslexiaFontTest extends TestCase {
/**
* @dataProvider dataTestGetCustomCss
*
*
* Ensure the fonts are always loaded from the web root
* despite having url rewriting enabled or not
*
@@ -155,7 +162,7 @@ class DyslexiaFontTest extends TestCase {
->method('getSystemValue')
->with('htaccess.IgnoreFrontController', false)
->willReturn($prettyUrlsEnabled);
$this->assertStringContainsString("'$webRoot/apps/theming/fonts/OpenDyslexic-Regular.woff'", $this->dyslexiaFont->getCustomCss());
$this->assertStringNotContainsString('index.php', $this->dyslexiaFont->getCustomCss());
}

View File

@@ -35,6 +35,7 @@
namespace OCA\Theming\Tests;
use OCA\Theming\ImageManager;
use OCA\Theming\Service\BackgroundService;
use OCA\Theming\ThemingDefaults;
use OCA\Theming\Util;
use OCP\App\IAppManager;
@@ -46,6 +47,8 @@ use OCP\IConfig;
use OCP\IL10N;
use OCP\INavigationManager;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\IUserSession;
use Test\TestCase;
class ThemingDefaultsTest extends TestCase {
@@ -53,6 +56,8 @@ class ThemingDefaultsTest extends TestCase {
private $config;
/** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
private $l10n;
/** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
private $userSession;
/** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
private $urlGenerator;
/** @var \OC_Defaults|\PHPUnit\Framework\MockObject\MockObject */
@@ -78,6 +83,7 @@ class ThemingDefaultsTest extends TestCase {
parent::setUp();
$this->config = $this->createMock(IConfig::class);
$this->l10n = $this->createMock(IL10N::class);
$this->userSession = $this->createMock(IUserSession::class);
$this->urlGenerator = $this->createMock(IURLGenerator::class);
$this->cacheFactory = $this->createMock(ICacheFactory::class);
$this->cache = $this->createMock(ICache::class);
@@ -93,6 +99,7 @@ class ThemingDefaultsTest extends TestCase {
$this->template = new ThemingDefaults(
$this->config,
$this->l10n,
$this->userSession,
$this->urlGenerator,
$this->cacheFactory,
$this->util,
@@ -415,11 +422,11 @@ class ThemingDefaultsTest extends TestCase {
$this->assertEquals('<a href="url" target="_blank" rel="noreferrer noopener" class="entity-name">Name</a> Slogan', $this->template->getShortFooter());
}
public function testgetColorPrimaryWithDefault() {
public function testGetColorPrimaryWithDefault() {
$this->config
->expects($this->once())
->method('getAppValue')
->with('theming', 'color', $this->defaults->getColorPrimary())
->with('theming', 'color', null)
->willReturn($this->defaults->getColorPrimary());
$this->assertEquals($this->defaults->getColorPrimary(), $this->template->getColorPrimary());
@@ -429,12 +436,80 @@ class ThemingDefaultsTest extends TestCase {
$this->config
->expects($this->once())
->method('getAppValue')
->with('theming', 'color', $this->defaults->getColorPrimary())
->with('theming', 'color', null)
->willReturn('#fff');
$this->assertEquals('#fff', $this->template->getColorPrimary());
}
public function testGetColorPrimaryWithDefaultBackground() {
$user = $this->createMock(IUser::class);
$this->userSession->expects($this->any())
->method('getUser')
->willReturn($user);
$user->expects($this->any())
->method('getUID')
->willReturn('user');
$this->assertEquals(BackgroundService::DEFAULT_COLOR, $this->template->getColorPrimary());
}
public function testGetColorPrimaryWithCustomBackground() {
$backgroundIndex = 2;
$background = array_values(BackgroundService::SHIPPED_BACKGROUNDS)[$backgroundIndex];
$user = $this->createMock(IUser::class);
$this->userSession->expects($this->any())
->method('getUser')
->willReturn($user);
$user->expects($this->any())
->method('getUID')
->willReturn('user');
$this->config
->expects($this->once())
->method('getUserValue')
->with('user', 'theming', 'background', '')
->willReturn(array_keys(BackgroundService::SHIPPED_BACKGROUNDS)[$backgroundIndex]);
$this->assertEquals($background['primary_color'], $this->template->getColorPrimary());
}
public function testGetColorPrimaryWithCustomBackgroundColor() {
$user = $this->createMock(IUser::class);
$this->userSession->expects($this->any())
->method('getUser')
->willReturn($user);
$user->expects($this->any())
->method('getUID')
->willReturn('user');
$this->config
->expects($this->once())
->method('getUserValue')
->with('user', 'theming', 'background', '')
->willReturn('#fff');
$this->assertEquals('#fff', $this->template->getColorPrimary());
}
public function testGetColorPrimaryWithInvalidCustomBackgroundColor() {
$user = $this->createMock(IUser::class);
$this->userSession->expects($this->any())
->method('getUser')
->willReturn($user);
$user->expects($this->any())
->method('getUID')
->willReturn('user');
$this->config
->expects($this->once())
->method('getUserValue')
->with('user', 'theming', 'background', '')
->willReturn('nextcloud');
$this->assertEquals($this->template->getDefaultColorPrimary(), $this->template->getColorPrimary());
}
public function testSet() {
$this->config
->expects($this->exactly(2))
@@ -542,7 +617,7 @@ class ThemingDefaultsTest extends TestCase {
->method('getAppValue')
->withConsecutive(
['theming', 'cachebuster', '0'],
['theming', 'color', $this->defaults->getColorPrimary()],
['theming', 'color', null],
)->willReturnOnConsecutiveCalls(
'15',
$this->defaults->getColorPrimary(),

View File

@@ -406,7 +406,7 @@ export default {
const hiddenField = document.createElement('input')
hiddenField.setAttribute('type', 'hidden')
hiddenField.setAttribute('name', 'updater-secret-input')
hiddenField.setAttribute('value', data.token)
hiddenField.setAttribute('value', data)
form.appendChild(hiddenField)

View File

@@ -94,8 +94,8 @@
}
.icon-user-status-invisible {
/* $dir is the app name, so we add this to the icon var to avoid conflicts between apps */
background-image: var(--icon-user-status-invisible-dark);
background-image: url("../img/user-status-invisible.svg");
filter: var(--background-invert-if-dark);
}
/*# sourceMappingURL=user-status-menu.css.map */

View File

@@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../../core/css/variables.scss","user-status-menu.scss","../../../core/css/functions.scss"],"names":[],"mappings":";AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsBA;AAAA;AAAA;AA4BA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AD1BA;ACuCC;EAEA;;;ADrCD;EACC;;;AAGD;EACC;;;AAGD;EACC;;;AAID;ACsBC;EAEA","file":"user-status-menu.css"}
{"version":3,"sourceRoot":"","sources":["../../../core/css/variables.scss","user-status-menu.scss","../../../core/css/functions.scss"],"names":[],"mappings":";AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsBA;AAAA;AAAA;AA4BA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AD1BA;ACuCC;EAEA;;;ADrCD;EACC;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA","file":"user-status-menu.css"}

View File

@@ -38,7 +38,7 @@
background-image: url('../img/user-status-dnd.svg');
}
// TODO: debug why icon-black-white does not work here
.icon-user-status-invisible {
@include icon-color('user-status-invisible', 'user_status', variables.$color-black, 1);
background-image: url('../img/user-status-invisible.svg');
filter: var(--background-invert-if-dark);
}

View File

@@ -48,11 +48,17 @@ import NcEmojiPicker from '@nextcloud/vue/dist/Components/NcEmojiPicker.js'
export default {
name: 'CustomMessageInput',
components: {
NcButton,
NcEmojiPicker,
},
props: {
icon: {
type: String,
default: '😀',
},
message: {
type: String,
required: true,
@@ -63,11 +69,13 @@ export default {
default: false,
},
},
emits: [
'change',
'submit',
'icon-selected',
],
computed: {
/**
* Returns the user-set icon or a smiley in case no icon is set
@@ -78,6 +86,7 @@ export default {
return this.icon || '😀'
},
},
methods: {
focus() {
this.$refs.input.focus()
@@ -96,8 +105,8 @@ export default {
this.$emit('submit', event.target.value)
},
setIcon(event) {
this.$emit('icon-selected', event)
setIcon(icon) {
this.$emit('select-icon', icon)
},
},
}

View File

@@ -27,8 +27,9 @@
type="radio"
name="user-status-online"
@change="onChange">
<label :for="id" :class="icon" class="user-status-online-select__label">
<label :for="id" class="user-status-online-select__label">
{{ label }}
<span :class="icon" role="img" />
<em class="user-status-online-select__subline">{{ subline }}</em>
</label>
</div>
@@ -76,6 +77,7 @@ export default {
</script>
<style lang="scss" scoped>
@use 'sass:math';
$icon-size: 24px;
$label-padding: 8px;
@@ -91,6 +93,7 @@ $label-padding: 8px;
}
&__label {
position: relative;
display: block;
margin: $label-padding;
padding: $label-padding;
@@ -105,6 +108,15 @@ $label-padding: 8px;
& {
cursor: pointer;
}
span {
position: absolute;
top: calc(50% - math.div($icon-size, 2));
left: $label-padding;
display: block;
width: $icon-size;
height: $icon-size;
}
}
&__input:checked + &__label,

View File

@@ -42,10 +42,11 @@
</div>
<div class="set-status-modal__custom-input">
<CustomMessageInput ref="customMessageInput"
:icon="icon"
:message="message"
@change="setMessage"
@submit="saveStatus"
@iconSelected="setIcon" />
@select-icon="setIcon" />
</div>
<PredefinedStatusesList @select-status="selectPredefinedMessage" />
<ClearAtSelect :clear-at="clearAt"
@@ -74,12 +75,12 @@
import { showError } from '@nextcloud/dialogs'
import NcModal from '@nextcloud/vue/dist/Components/NcModal'
import NcButton from '@nextcloud/vue/dist/Components/NcButton'
import { getAllStatusOptions } from '../services/statusOptionsService'
import OnlineStatusMixin from '../mixins/OnlineStatusMixin'
import PredefinedStatusesList from './PredefinedStatusesList'
import CustomMessageInput from './CustomMessageInput'
import ClearAtSelect from './ClearAtSelect'
import OnlineStatusSelect from './OnlineStatusSelect'
import { getAllStatusOptions } from '../services/statusOptionsService.js'
import OnlineStatusMixin from '../mixins/OnlineStatusMixin.js'
import PredefinedStatusesList from './PredefinedStatusesList.vue'
import CustomMessageInput from './CustomMessageInput.vue'
import ClearAtSelect from './ClearAtSelect.vue'
import OnlineStatusSelect from './OnlineStatusSelect.vue'
export default {
name: 'SetStatusModal',

View File

@@ -1 +1 @@
3650d-5e41fd9674803
3707b-5eab9a4124dc3

View File

@@ -4622,7 +4622,7 @@ function idn_to_ascii($domain, $options = 0, $variant = INTL_IDNA_VARIANT_2003,
* @param int $variant [optional] <p>
* Either INTL_IDNA_VARIANT_2003 for IDNA 2003 or INTL_IDNA_VARIANT_UTS46 for UTS #46.
* </p>
* @param int &$idna_info [optional] <p>
* @param array &$idna_info [optional] <p>
* This parameter can be used only if INTL_IDNA_VARIANT_UTS46 was used for variant.
* In that case, it will be filled with an array with the keys 'result',
* the possibly illegal result of the transformation, 'isTransitionalDifferent',
@@ -4634,7 +4634,7 @@ function idn_to_ascii($domain, $options = 0, $variant = INTL_IDNA_VARIANT_2003,
* RFC 3490 4.2 states though "ToUnicode never fails. If any step fails, then the original input
* sequence is returned immediately in that step."
*/
function idn_to_utf8($domain, $options = 0, $variant = INTL_IDNA_VARIANT_2003, array &$idna_info) { }
function idn_to_utf8($domain, $options = 0, $variant = INTL_IDNA_VARIANT_2003, array &$idna_info = null) { }
/**
* (PHP 5 &gt;=5.5.0 PECL intl &gt;= 3.0.0a1)<br/>

View File

@@ -2177,7 +2177,7 @@ class Redis
*
* @param string $pattern pattern, using '*' as a wildcard
*
* @return array string[] The keys that match a certain pattern.
* @return string[]|false The keys that match a certain pattern.
*
* @link https://redis.io/commands/keys
* @example

View File

@@ -45,14 +45,14 @@ class BackgroundCleanupUpdaterBackupsJob extends QueuedJob {
* This job cleans up all backups except the latest 3 from the updaters backup directory
*/
public function run($arguments) {
$dataDir = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data');
$updateDir = $this->config->getSystemValue('updatedirectory', null) ?? $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data');
$instanceId = $this->config->getSystemValue('instanceid', null);
if (!is_string($instanceId) || empty($instanceId)) {
return;
}
$updaterFolderPath = $dataDir . '/updater-' . $instanceId;
$updaterFolderPath = $updateDir . '/updater-' . $instanceId;
$backupFolderPath = $updaterFolderPath . '/backups';
if (file_exists($backupFolderPath)) {
$this->log->info("$backupFolderPath exists - start to clean it up");

View File

@@ -57,6 +57,16 @@ class ReferenceApiController extends \OCP\AppFramework\OCSController {
]);
}
/**
* @NoAdminRequired
*/
public function resolveOne(string $reference): DataResponse {
$resolvedReference = $this->referenceManager->resolveReference(trim($reference));
$response = new DataResponse(['references' => [ $reference => $resolvedReference ]]);
$response->cacheFor(3600, false, true);
return $response;
}
/**
* @NoAdminRequired

View File

@@ -59,9 +59,11 @@ class ReferenceController extends Controller {
$appData = $this->appDataFactory->get('core');
$folder = $appData->getFolder('opengraph');
$file = $folder->getFile($referenceId);
return new DataDownloadResponse($file->getContent(), $referenceId, $reference->getImageContentType());
$response = new DataDownloadResponse($file->getContent(), $referenceId, $reference->getImageContentType());
} catch (NotFoundException|NotPermittedException $e) {
return new DataResponse('', Http::STATUS_NOT_FOUND);
$response = new DataResponse('', Http::STATUS_NOT_FOUND);
}
$response->cacheFor(3600, false, true);
return $response;
}
}

View File

@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
/**
* @copyright 2022 Christopher Ng <chrng8@gmail.com>
*
* @author Christopher Ng <chrng8@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Core\Migrations;
use Closure;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
/**
* User background settings handling was moved from the
* dashboard app to the theming app so we migrate the
* respective preference values here
*
*/
class Version25000Date20221007010957 extends SimpleMigrationStep {
protected IDBConnection $connection;
public function __construct(IDBConnection $connection) {
$this->connection = $connection;
}
/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
*/
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
$cleanUpQuery = $this->connection->getQueryBuilder();
$cleanUpQuery->delete('preferences')
->where($cleanUpQuery->expr()->eq('appid', $cleanUpQuery->createNamedParameter('theming')))
->andWhere($cleanUpQuery->expr()->orX(
$cleanUpQuery->expr()->eq('configkey', $cleanUpQuery->createNamedParameter('background')),
$cleanUpQuery->expr()->eq('configkey', $cleanUpQuery->createNamedParameter('backgroundVersion')),
));
$cleanUpQuery->executeStatement();
$updateQuery = $this->connection->getQueryBuilder();
$updateQuery->update('preferences')
->set('appid', $updateQuery->createNamedParameter('theming'))
->where($updateQuery->expr()->eq('appid', $updateQuery->createNamedParameter('dashboard')))
->andWhere($updateQuery->expr()->orX(
$updateQuery->expr()->eq('configkey', $updateQuery->createNamedParameter('background')),
$updateQuery->expr()->eq('configkey', $updateQuery->createNamedParameter('backgroundVersion')),
));
$updateQuery->executeStatement();
}
}

View File

@@ -89,14 +89,14 @@ html {
width: 100%;
height: 100%;
position: absolute;
background-color: var(--color-primary);
background-color: var(--color-main-background-plain, var(--color-main-background));
background-image: var(--image-main-background);
background-size: cover;
background-position: center;
}
body {
background-color: var(--color-primary);
background-color: var(--color-main-background-plain, var(--color-main-background));
background-image: var(--image-main-background);
background-size: cover;
background-position: center;
@@ -814,7 +814,7 @@ kbd {
width: 100%;
padding: 0;
margin: 0;
background-color: var(--color-main-background);
background-color: transparent;
box-shadow: none;
border: 0;
border-radius: calc(var(--default-clickable-area) / 2);
@@ -958,6 +958,11 @@ kbd {
border: 1px solid var(--color-border);
}
.contact .popovermenu ul > li > a > img,
.popover__menu > li > a > img {
filter: var(--background-invert-if-dark);
}
.bubble,
.app-navigation-entry-menu,
.popovermenu {

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More