Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorJohn Molakvoæ <skjnldsv@users.noreply.github.com>2022-05-11 10:28:58 +0300
committerGitHub <noreply@github.com>2022-05-11 10:28:58 +0300
commit5a0b28d603e142051967175f023b698ff7e262db (patch)
treec089d8787ef77b7e08a1fa110b49db5de6473cec /core
parent18f2340a403554cde825d673d6bd5aa31283b4e5 (diff)
parentcb73fe26b818cfdb8fbf07116cd8e970a9cdc708 (diff)
Merge pull request #32326 from nextcloud/fix/icons-cacher
Diffstat (limited to 'core')
-rw-r--r--core/Controller/SvgController.php151
-rw-r--r--core/css/apps.scss21
-rw-r--r--core/css/functions.scss59
-rw-r--r--core/css/icons.scss371
-rw-r--r--core/css/styles.scss2
-rw-r--r--core/img/actions/change.svg1
-rw-r--r--core/img/actions/recent.svg1
-rw-r--r--core/img/actions/unshare.svg1
-rw-r--r--core/img/apps/circles.svg1
-rw-r--r--core/routes.php2
-rw-r--r--core/src/icons.js334
-rw-r--r--core/src/jquery/css/jquery.ocdialog.scss2
-rw-r--r--core/templates/layout.user.php2
13 files changed, 392 insertions, 556 deletions
diff --git a/core/Controller/SvgController.php b/core/Controller/SvgController.php
deleted file mode 100644
index 17f16dd48e6..00000000000
--- a/core/Controller/SvgController.php
+++ /dev/null
@@ -1,151 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-/**
- * @copyright Copyright (c) 2018, John Molakvoæ (skjnldsv@protonmail.com)
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- * @author Joas Schilling <coding@schilljs.com>
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Thomas Citharel <nextcloud@tcit.fr>
- *
- * @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\Controller;
-
-use OC\Files\Filesystem;
-use OC\Template\IconsCacher;
-use OCP\App\AppPathNotFoundException;
-use OCP\App\IAppManager;
-use OCP\AppFramework\Controller;
-use OCP\AppFramework\Http;
-use OCP\AppFramework\Http\DataDisplayResponse;
-use OCP\AppFramework\Http\NotFoundResponse;
-use OCP\AppFramework\Utility\ITimeFactory;
-use OCP\IRequest;
-
-class SvgController extends Controller {
-
- /** @var string */
- protected $serverRoot;
-
- /** @var ITimeFactory */
- protected $timeFactory;
-
- /** @var IAppManager */
- protected $appManager;
-
- /** @var IconsCacher */
- private $iconsCacher;
-
- public function __construct(string $appName,
- IRequest $request,
- ITimeFactory $timeFactory,
- IAppManager $appManager,
- IconsCacher $iconsCacher) {
- parent::__construct($appName, $request);
-
- $this->serverRoot = \OC::$SERVERROOT;
- $this->timeFactory = $timeFactory;
- $this->appManager = $appManager;
- $this->iconsCacher = $iconsCacher;
- }
-
- /**
- * @PublicPage
- * @NoCSRFRequired
- * @NoSameSiteCookieRequired
- *
- * Generate svg from filename with the requested color
- *
- * @param string $folder
- * @param string $fileName
- * @param string $color
- * @return DataDisplayResponse|NotFoundResponse
- */
- public function getSvgFromCore(string $folder, string $fileName, string $color = 'ffffff') {
- $path = $this->serverRoot . "/core/img/$folder/$fileName.svg";
- return $this->getSvg($path, $color, $fileName);
- }
-
- /**
- * @PublicPage
- * @NoCSRFRequired
- * @NoSameSiteCookieRequired
- *
- * Generate svg from filename with the requested color
- *
- * @param string $app
- * @param string $fileName
- * @param string $color
- * @return DataDisplayResponse|NotFoundResponse
- */
- public function getSvgFromApp(string $app, string $fileName, string $color = 'ffffff') {
- try {
- $appPath = $this->appManager->getAppPath($app);
- } catch (AppPathNotFoundException $e) {
- return new NotFoundResponse();
- }
-
- $path = $appPath . "/img/$fileName.svg";
- return $this->getSvg($path, $color, $fileName);
- }
-
- /**
- * Generate svg from filename with the requested color
- *
- * @param string $path
- * @param string $color
- * @param string $fileName
- * @return DataDisplayResponse|NotFoundResponse
- */
- private function getSvg(string $path, string $color, string $fileName) {
- if (!Filesystem::isValidPath($path)) {
- return new NotFoundResponse();
- }
-
- if (!file_exists($path)) {
- return new NotFoundResponse();
- }
-
- $svg = file_get_contents($path);
-
- if ($svg === null) {
- return new NotFoundResponse();
- }
-
- $svg = $this->iconsCacher->colorizeSvg($svg, $color);
-
- $response = new DataDisplayResponse($svg, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']);
-
- // Set cache control
- $ttl = 31536000;
- $response->cacheFor($ttl);
- $response->addHeader('Content-Disposition', 'inline; filename="' . $fileName . '.svg"');
- $expires = new \DateTime();
- $expires->setTimestamp($this->timeFactory->getTime());
- $expires->add(new \DateInterval('PT' . $ttl . 'S'));
- $response->addHeader('Expires', $expires->format(\DateTime::RFC1123));
- $response->addHeader('Pragma', 'cache');
-
- return $response;
- }
-}
diff --git a/core/css/apps.scss b/core/css/apps.scss
index 8d753eb8d23..a9b20cfec2b 100644
--- a/core/css/apps.scss
+++ b/core/css/apps.scss
@@ -721,20 +721,16 @@ $min-content-width: $breakpoint-mobile - $navigation-width - $list-min-width;
#app-settings-header .settings-button {
- display: block;
+ display: flex;
+ align-items: center;
height: 44px;
width: 100%;
padding: 0;
margin: 0;
- background-color: var(--color-main-background);
- @include icon-color('settings-dark', 'actions', $color-black, 1, true);
- background-position: 14px center;
- background-repeat: no-repeat;
box-shadow: none;
border: 0;
border-radius: 0;
text-align: left;
- padding-left: 44px;
font-weight: normal;
font-size: 100%;
opacity: 0.8;
@@ -750,6 +746,19 @@ $min-content-width: $breakpoint-mobile - $navigation-width - $list-min-width;
&:focus {
background-color: var(--color-background-hover);
}
+
+ &::before {
+ background-image: var(--icon-settings-dark);
+ background-position: 14px center;
+ background-repeat: no-repeat;
+ content: '';
+ width: 44px;
+ height: 44px;
+ top: 0;
+ left: 0;
+ display: block;
+ filter: var(--background-invert-if-dark);
+ }
}
/* GENERAL SECTION ------------------------------------------------------------ */
diff --git a/core/css/functions.scss b/core/css/functions.scss
index 7489e574e97..35db19c3142 100644
--- a/core/css/functions.scss
+++ b/core/css/functions.scss
@@ -36,22 +36,31 @@
}
/**
- * Calculates the URL to the svg under the SVG API.
- *
- * @param string $icon the icon filename
- * @param string $dir the icon folder within /core/img if $core or app name
- * @param string $color the desired color in hexadecimal
- * @param int [$version] the version of the file
- * @param bool [$core] search icon in core
- * @return string The URL to the svg.
+ * @see core/src/icons.js
*/
-@function icon-color-path($icon, $dir, $color, $version: 1, $core: false) {
- $color: remove-hash-from-color($color);
- @if $core {
- @return '#{$webroot}/svg/core/#{$dir}/#{$icon}?color=#{$color}&v=#{$version}';
- } @else {
- @return '#{$webroot}/svg/#{$dir}/#{$icon}?color=#{$color}&v=#{$version}';
+@function match-color-string($color) {
+ @if $color == #000 {
+ @return "dark";
+ }
+ @if $color == #fff {
+ @return 'white';
+ }
+ @if $color == #FC0 {
+ @return 'yellow';
+ }
+ @if $color == #e9322d {
+ @return 'red';
+ }
+ @if $color == #eca700 {
+ @return 'orange';
}
+ @if $color == #46ba61 {
+ @return 'green';
+ }
+ @if $color == #969696 {
+ @return 'grey';
+ }
+ @return $color;
}
/**
@@ -66,30 +75,12 @@
* @returns A background image with the url to the set to the requested icon.
*/
@mixin icon-color($icon, $dir, $color, $version: 1, $core: false) {
- $color: remove-hash-from-color($color);
+ $color: match-color-string($color);
/* $dir is the app name, so we add this to the icon var to avoid conflicts between apps */
- $varName: "--icon-#{$dir}-#{$icon}-#{$color}";
- @if $core {
- $varName: "--icon-#{$icon}-#{$color}";
- }
- #{$varName}: url(icon-color-path($icon, $dir, $color, $version, $core));
+ $varName: "--icon-#{$icon}-#{$color}";
background-image: var(#{$varName});
}
-/**
- * Create black and white icons
- * This will add a default black version of and an additional white version when .icon-white is applied
- */
-@mixin icon-black-white($icon, $dir, $version, $core: false) {
- .icon-#{$icon} {
- @include icon-color($icon, $dir, $color-black, $version, $core);
- }
- .icon-#{$icon}-white,
- .icon-#{$icon}.icon-white {
- @include icon-color($icon, $dir, $color-white, $version, $core);
- }
-}
-
@mixin position($value) {
@if $value == 'sticky' {
position: -webkit-sticky; // Safari support
diff --git a/core/css/icons.scss b/core/css/icons.scss
index 78522895cf5..9acec4895be 100644
--- a/core/css/icons.scss
+++ b/core/css/icons.scss
@@ -123,393 +123,42 @@ audio, canvas, embed, iframe, img, input, object, video {
}
}
-/* ICONS -------------------------------------------------------------------
- * These icon classes are generated automatically with the following pattern
- * for icon-black-white('close', ...)
- * .icon-close (black icon)
- * .icon-close-white (white icon)
- * .icon-close.icon-white (white icon)
- *
- * Some class definitions are kept as before, since they don't follow the pattern
- * or have some additional styling like drop shadows
- */
-
-@include icon-black-white('add', 'actions', 1, true);
-@include icon-black-white('address', 'actions', 1, true);
-
-@include icon-black-white('audio', 'actions', 2, true);
.icon-audio-white {
filter: drop-shadow(1px 1px 4px var(--color-box-shadow));
}
-@include icon-black-white('audio-off', 'actions', 1, true);
.icon-audio-off-white {
filter: drop-shadow(1px 1px 4px var(--color-box-shadow));
}
-.icon-caret-white,
-.icon-caret {
- @include icon-color('caret', 'actions', $color-white, 1, true);
-}
-
-.icon-caret-dark {
- @include icon-color('caret', 'actions', $color-black, 1, true);
-}
-
-@include icon-black-white('checkmark', 'actions', 1, true);
-.icon-checkmark-color {
- @include icon-color('checkmark', 'actions', $color-success, 1, true);
-}
-
-@include icon-black-white('clippy', 'actions', 2, true);
-@include icon-black-white('close', 'actions', 1, true);
-@include icon-black-white('comment', 'actions', 1, true);
-@include icon-black-white('confirm', 'actions', 2, true);
-@include icon-black-white('download', 'actions', 1, true);
-
-.icon-confirm-fade {
- @include icon-color('confirm-fade', 'actions', $color-black, 2, true);
-}
-
-.icon-delete {
- @include icon-color('delete', 'actions', $color-black, 1, true);
- &.no-permission,
- &.no-hover {
- &:hover,
- &:focus {
- @include icon-color('delete', 'actions', $color-black, 1, true);
- }
- }
- &:hover,
- &:focus {
- @include icon-color('delete', 'actions', $color-error, 1, true);
- filter: initial;
- }
-
- &.icon-white {
- @include icon-color('delete', 'actions', $color-white, 1, true);
- }
-}
-
-.icon-delete-white {
- @include icon-color('delete', 'actions', $color-white, 1, true);
- &.no-permission {
- &:hover,
- &:focus {
- @include icon-color('delete', 'actions', $color-white, 1, true);
- }
- }
- &:hover,
- &:focus {
- @include icon-color('delete', 'actions', $color-error, 1, true);
- }
-}
-
-@include icon-black-white('details', 'actions', 1, true);
-@include icon-black-white('edit', 'actions', 1, true);
-@include icon-black-white('error', 'actions', 1, true);
-
-.icon-error-color {
- @include icon-color('error', 'actions', $color-error, 1, true);
-}
-@include icon-black-white('external', 'actions', 1, true);
-@include icon-black-white('fullscreen', 'actions', 1, true);
-
.icon-fullscreen-white {
filter: drop-shadow(1px 1px 4px var(--color-box-shadow));
}
-@include icon-black-white('history', 'actions', 2, true);
-@include icon-black-white('info', 'actions', 1, true);
-@include icon-black-white('logout', 'actions', 1, true);
-@include icon-black-white('mail', 'actions', 1, true);
-@include icon-black-white('menu', 'actions', 1, true);
-@include icon-black-white('menu-sidebar', 'actions', 1, true);
-@include icon-black-white('more', 'actions', 1, true);
-@include icon-black-white('password', 'actions', 1, true);
-@include icon-black-white('pause', 'actions', 1, true);
-@include icon-black-white('play', 'actions', 1, true);
-@include icon-black-white('play-add', 'actions', 1, true);
-@include icon-black-white('play-next', 'actions', 1, true);
-@include icon-black-white('play-previous', 'actions', 1, true);
-@include icon-black-white('projects', 'actions', 1, true);
-@include icon-black-white('public', 'actions', 1, true);
-@include icon-black-white('quota', 'actions', 1, true);
-@include icon-black-white('rename', 'actions', 1, true);
-@include icon-black-white('screen', 'actions', 1, true);
-@include icon-black-white('template-add', 'actions', 1, true);
-
.icon-screen-white {
filter: drop-shadow(1px 1px 4px var(--color-box-shadow));
}
-@include icon-black-white('screen-off', 'actions', 1, true);
.icon-screen-off-white {
filter: drop-shadow(1px 1px 4px var(--color-box-shadow));
}
-@include icon-black-white('search', 'actions', 1, true);
-
-/* default icon have a .5 opacity */
-.icon-settings {
- @include icon-color('settings', 'actions', $color-black, 1, true);
-}
-
-.icon-settings-dark {
- @include icon-color('settings-dark', 'actions', $color-black, 1, true);
-}
-
-.icon-settings-white {
- @include icon-color('settings-dark', 'actions', $color-white, 1, true);
-}
-
-/* always use icon-shared, AdBlock blocks icon-share */
-.icon-shared,
-.icon-share {
- @include icon-color('share', 'actions', $color-black, 1, true);
- &.icon-white {
- @include icon-color('share', 'actions', $color-white, 1, true);
- }
-}
-.icon-shared-white,
-.icon-share-white {
- @include icon-color('share', 'actions', $color-white, 1, true);
-}
-
-@include icon-black-white('sound', 'actions', 1, true);
-@include icon-black-white('sound-off', 'actions', 1, true);
-
-.icon-favorite {
- @include icon-color('star-dark', 'actions', $color-black, 1, true);
-}
-
-@include icon-black-white('star', 'actions', 1, true);
-
-.icon-star-dark {
- @include icon-color('star', 'actions', $color-black, 1, true);
-}
-
-.icon-starred {
- &:hover,
- &:focus {
- @include icon-color('star', 'actions', $color-black, 1, true);
- }
- @include icon-color('star-dark', 'actions', $color-yellow, 1, true);
-}
-
-.icon-star {
- &:hover,
- &:focus {
- @include icon-color('star-dark', 'actions', $color-yellow, 1, true);
- }
-}
-
-@include icon-black-white('tag', 'actions', 2, true);
-@include icon-black-white('timezone', 'actions', 1, true);
-@include icon-black-white('toggle', 'actions', 1, true);
-@include icon-black-white('toggle-background', 'actions', 1, true);
-@include icon-black-white('toggle-pictures', 'actions', 1, true);
-@include icon-black-white('toggle-filelist', 'actions', 1, true);
-@include icon-black-white('triangle-e', 'actions', 1, true);
-@include icon-black-white('triangle-n', 'actions', 1, true);
-@include icon-black-white('triangle-s', 'actions', 1, true);
-@include icon-black-white('upload', 'actions', 1, true);
-@include icon-black-white('user', 'actions', 1, true);
-@include icon-black-white('group', 'actions', 1, true);
-@include icon-black-white('filter', 'actions', 1, true);
-
-@include icon-black-white('video', 'actions', 2, true);
.icon-video-white {
filter: drop-shadow(1px 1px 4px var(--color-box-shadow));
}
-@include icon-black-white('video-off', 'actions', 1, true);
.icon-video-off-white {
filter: drop-shadow(1px 1px 4px var(--color-box-shadow));
}
-@include icon-black-white('video-switch', 'actions', 1, true);
-
-/* SHADOW WHITE ICONS: white version only ----------------------------------- */
-.icon-view-close,
-.icon-view-close-white {
- @include icon-color('view-close', 'actions', $color-white, 1, true);
-}
-.icon-view-download,
-.icon-view-download-white {
- @include icon-color('view-download', 'actions', $color-white, 1, true);
-}
-.icon-view-pause,
-.icon-view-pause-white {
- @include icon-color('view-pause', 'actions', $color-white, 1, true);
-}
-.icon-view-play,
-.icon-view-play-white {
- @include icon-color('view-play', 'actions', $color-white, 1, true);
-}
-.icon-view-next {
- @include icon-color('arrow-right', 'actions', $color-black, 1, true);
- &.icon-white {
- @include icon-color('arrow-right', 'actions', $color-white, 1, true);
- }
-}
-
-
-.icon-view-previous {
- @include icon-color('arrow-left', 'actions', $color-black, 1, true);
- &.icon-white {
- @include icon-color('arrow-left', 'actions', $color-white, 1, true);
- }
-}
-
-
-@include icon-black-white('disabled-user', 'actions', 1, true);
-@include icon-black-white('disabled-users', 'actions', 1, true);
-@include icon-black-white('user-admin', 'actions', 1, true);
-
-@include icon-black-white('alert-outline', 'actions', 1, true);
-
-/* PLACES ------------------------------------------------------------------- */
-.icon-calendar {
- @include icon-color('calendar', 'places', $color-white, 1, true);
-}
-
-.icon-calendar-dark {
- @include icon-color('calendar', 'places', $color-black, 1, true);
-}
-
-.icon-contacts {
- @include icon-color('contacts', 'places', $color-white, 1, true);
-}
-
-.icon-contacts-dark {
- @include icon-color('contacts', 'places', $color-black, 1, true);
-}
-
-.icon-files {
- @include icon-color('files', 'places', $color-white, 1, true);
-}
-
-.icon-files-dark {
- @include icon-color('files', 'places', $color-black, 1, true);
-}
-
-.icon-file,
-.icon-filetype-text {
- @include icon-color('text', 'filetypes', #969696, 1, true);
-}
-
-.icon-filetype-file {
- @include icon-color('file', 'filetypes', #969696, 1, true);
-}
-
-@include icon-black-white('folder', 'filetypes', 1, true);
-.icon-filetype-folder {
- @include icon-color('folder', 'filetypes', $color-primary, 1, true);
-}
-
-.icon-filetype-folder-drag-accept {
- @include icon-color('folder-drag-accept', 'filetypes', $color-primary, 1, true);
-}
-
-
-@include icon-black-white('home', 'places', 1, true);
-@include icon-black-white('link', 'places', 1, true);
-@include icon-black-white('music', 'places', 1, true);
-@include icon-black-white('picture', 'places', 1, true);
-
-
-/* CLIENTS ------------------------------------------------------------------- */
-
-@include icon-black-white('desktop', 'clients', 1, true);
-@include icon-black-white('phone', 'clients', 1, true);
-@include icon-black-white('tablet', 'clients', 1, true);
-
-/* APP CATEGORIES ------------------------------------------------------------------- */
-.icon-category-installed {
- @include icon-color('user', 'actions', $color-black, 1, true);
-}
-
-.icon-category-enabled {
- @include icon-color('checkmark', 'actions', $color-black, 1, true);
-}
-
-.icon-category-disabled {
- @include icon-color('close', 'actions', $color-black, 1, true);
-}
-
-.icon-category-app-bundles {
- @include icon-color('bundles', 'categories', $color-black, 1, true);
-}
-
-.icon-category-updates {
- @include icon-color('download', 'actions', $color-black, 1, true);
-}
-
-.icon-category-files {
- @include icon-color('files', 'categories', $color-black, 1, true);
-}
-
-.icon-category-social {
- @include icon-color('social', 'categories', $color-black, 1, true);
-}
-
-.icon-category-office {
- @include icon-color('office', 'categories', $color-black, 1, true);
-}
-
-.icon-category-auth {
- @include icon-color('auth', 'categories', $color-black, 1, true);
-}
-
-.icon-category-monitoring {
- @include icon-color('monitoring', 'categories', $color-black, 1, true);
-}
-
-.icon-category-multimedia {
- @include icon-color('multimedia', 'categories', $color-black, 1, true);
-}
-
-.icon-category-organization {
- @include icon-color('organization', 'categories', $color-black, 1, true);
-}
-
-.icon-category-customization {
- @include icon-color('customization', 'categories', $color-black, 1, true);
-}
-
-.icon-category-integration {
- @include icon-color('integration', 'categories', $color-black, 1, true);
-}
-
-.icon-category-tools {
- @include icon-color('settings-dark', 'actions', $color-black, 1, true);
-}
-
-.icon-category-games {
- @include icon-color('games', 'categories', $color-black, 1, true);
-}
-
-.icon-category-security {
- @include icon-color('password', 'actions', $color-black, 1, true);
-}
-
-.icon-category-search {
- @include icon-color('search', 'actions', $color-black, 1, true);
-}
-
-.icon-category-workflow {
- @include icon-color('workflow', 'categories', $color-black, 1, true);
-}
-
-.icon-category-dashboard {
- @include icon-color('dashboard', 'categories', $color-black, 1, true);
-}
-
-.icon-talk {
- @include icon-color('app-dark', 'spreed', $color-black, 1);
-}
+/* ICONS -------------------------------------------------------------------
+ * These icon classes are generated automatically with the following pattern
+ * .icon-close (black icon)
+ * .icon-close-white (white icon)
+ * .icon-close.icon-white (white icon)
+ *
+ * Some class definitions are kept as before, since they don't follow the pattern
+ * or have some additional styling like drop shadows
+ */
-.nav-icon-systemtagsfilter {
- @include icon-color('tag', 'actions', $color-black, 1, true);
-}
+@import url('../../dist/icons.css');
diff --git a/core/css/styles.scss b/core/css/styles.scss
index f0c33a2037d..bd7e747169b 100644
--- a/core/css/styles.scss
+++ b/core/css/styles.scss
@@ -975,6 +975,8 @@ span.ui-icon {
background-size: 20px 20px;
padding: 14px;
cursor: pointer;
+ // Force white
+ background-image: var(--original-icon-contacts-white);
filter: var(--primary-invert-if-bright);
&:hover,
diff --git a/core/img/actions/change.svg b/core/img/actions/change.svg
new file mode 100644
index 00000000000..12071422b7f
--- /dev/null
+++ b/core/img/actions/change.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" version="1.1" height="16"><path d="m8 2c-2.142 0-4.125 1.145-5.196 3l1.948 1.125c0.671-1.162 1.906-1.875 3.2476-1.875 1.1906 0 2.297 0.56157 3 1.5l-1.5 1.5h4.5v-4.5l-1.406 1.406c-1.129-1.348-2.802-2.1563-4.594-2.1563z"/><path d="m2 8.75v4.5l1.408-1.41c1.116 1.334 2.817 2.145 4.592 2.16 2.16 0.01827 4.116-1.132 5.196-3.002l-1.948-1.125c-0.677 1.171-1.9005 1.886-3.248 1.875-1.18-0.01-2.3047-0.572-3-1.5l1.5-1.5z"/></svg>
diff --git a/core/img/actions/recent.svg b/core/img/actions/recent.svg
new file mode 100644
index 00000000000..8021bc4bb71
--- /dev/null
+++ b/core/img/actions/recent.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 16 16" height="16" width="16" version="1.1"><circle stroke-width="2" stroke="#000" cy="8" cx="8" r="7" fill="none"/><path stroke-linejoin="round" d="m8 3.5-1 5 3.5 2-2-2z" stroke="#000" stroke-linecap="round" stroke-width="1.5"/></svg>
diff --git a/core/img/actions/unshare.svg b/core/img/actions/unshare.svg
new file mode 100644
index 00000000000..0c22ca64057
--- /dev/null
+++ b/core/img/actions/unshare.svg
@@ -0,0 +1 @@
+<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m12.5 1a2.5 2.5 0 0 0-2.5 2.5 2.5 2.5 0 0 0 0.003906 0.12891l-4.9023 2.4512a2.5 2.5 0 0 0-1.6016-0.58008 2.5 2.5 0 0 0-2.5 2.5 2.5 2.5 0 0 0 2.5 2.5 2.5 2.5 0 0 0 0.30469-0.021484l3.4395-1.7246-1.25-0.625a2.5 2.5 0 0 0 0.0058594-0.12891 2.5 2.5 0 0 0-0.0039062-0.12891l4.9023-2.4512a2.5 2.5 0 0 0 1.6016 0.58008 2.5 2.5 0 0 0 0.26562-0.013672l1.5625-0.7832a2.5 2.5 0 0 0 0.67188-1.7031 2.5 2.5 0 0 0-2.5-2.5zm0.25391 9.0156-3.7246 1.8672 0.97656 0.48828a2.5 2.5 0 0 0-0.005859 0.12891 2.5 2.5 0 0 0 2.5 2.5 2.5 2.5 0 0 0 2.5-2.5 2.5 2.5 0 0 0-2.2461-2.4844z"/><rect transform="rotate(-26.63)" x="-1.0586" y="11.891" width="11.687" height="2.0029" ry="0" style="paint-order:normal"/></svg>
diff --git a/core/img/apps/circles.svg b/core/img/apps/circles.svg
new file mode 100644
index 00000000000..6264e74626c
--- /dev/null
+++ b/core/img/apps/circles.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 21.33 21.33" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M10.67 1.33a9.34 9.34 0 100 18.68 9.34 9.34 0 000-18.68zM6.93 15.8a2.33 2.33 0 110-4.67 2.33 2.33 0 010 4.67zm1.4-8.87a2.33 2.33 0 114.67 0 2.33 2.33 0 01-4.67 0zm6.07 8.87a2.33 2.33 0 110-4.67 2.33 2.33 0 010 4.67z"/></svg> \ No newline at end of file
diff --git a/core/routes.php b/core/routes.php
index c3bbb7337ba..bfc614935e1 100644
--- a/core/routes.php
+++ b/core/routes.php
@@ -79,8 +79,6 @@ $application->registerRoutes($this, [
['name' => 'Preview#getPreviewByFileId', 'url' => '/core/preview', 'verb' => 'GET'],
['name' => 'Preview#getPreview', 'url' => '/core/preview.png', 'verb' => 'GET'],
['name' => 'RecommendedApps#index', 'url' => '/core/apps/recommended', 'verb' => 'GET'],
- ['name' => 'Svg#getSvgFromCore', 'url' => '/svg/core/{folder}/{fileName}', 'verb' => 'GET'],
- ['name' => 'Svg#getSvgFromApp', 'url' => '/svg/{app}/{fileName}', 'verb' => 'GET'],
['name' => 'Css#getCss', 'url' => '/css/{appName}/{fileName}', 'verb' => 'GET'],
['name' => 'Js#getJs', 'url' => '/js/{appName}/{fileName}', 'verb' => 'GET'],
['name' => 'contactsMenu#index', 'url' => '/contactsmenu/contacts', 'verb' => 'POST'],
diff --git a/core/src/icons.js b/core/src/icons.js
new file mode 100644
index 00000000000..ca706dcfdb8
--- /dev/null
+++ b/core/src/icons.js
@@ -0,0 +1,334 @@
+/* eslint-disable quote-props */
+/* eslint-disable node/no-unpublished-import */
+import path from 'path'
+import fs from 'fs'
+import sass from 'sass'
+
+const colors = {
+ dark: '000',
+ white: 'fff',
+ yellow: 'FC0',
+ red: 'e9322d',
+ orange: 'eca700',
+ green: '46ba61',
+ grey: '969696',
+}
+
+const variables = {}
+const icons = {
+ 'add': path.join(__dirname, '../img', 'actions', 'add.svg'),
+ 'address': path.join(__dirname, '../img', 'actions', 'address.svg'),
+ 'alert-outline': path.join(__dirname, '../img', 'actions', 'alert-outline.svg'),
+ 'audio-off': path.join(__dirname, '../img', 'actions', 'audio-off.svg'),
+ 'audio': path.join(__dirname, '../img', 'actions', 'audio.svg'),
+ 'calendar': path.join(__dirname, '../img', 'places', 'calendar.svg'),
+ 'caret': path.join(__dirname, '../img', 'actions', 'caret.svg'),
+ 'category-app-bundles': path.join(__dirname, '../img', 'categories', 'bundles.svg'),
+ 'category-auth': path.join(__dirname, '../img', 'categories', 'auth.svg'),
+ 'category-customization': path.join(__dirname, '../img', 'categories', 'customization.svg'),
+ 'category-dashboard': path.join(__dirname, '../img', 'categories', 'dashboard.svg'),
+ 'category-files': path.join(__dirname, '../img', 'categories', 'files.svg'),
+ 'category-games': path.join(__dirname, '../img', 'categories', 'games.svg'),
+ 'category-integration': path.join(__dirname, '../img', 'categories', 'integration.svg'),
+ 'category-monitoring': path.join(__dirname, '../img', 'categories', 'monitoring.svg'),
+ 'category-multimedia': path.join(__dirname, '../img', 'categories', 'multimedia.svg'),
+ 'category-office': path.join(__dirname, '../img', 'categories', 'office.svg'),
+ 'category-organization': path.join(__dirname, '../img', 'categories', 'organization.svg'),
+ 'category-social': path.join(__dirname, '../img', 'categories', 'social.svg'),
+ 'category-workflow': path.join(__dirname, '../img', 'categories', 'workflow.svg'),
+ 'change': path.join(__dirname, '../img', 'actions', 'change.svg'),
+ 'checkmark': path.join(__dirname, '../img', 'actions', 'checkmark.svg'),
+ 'circles': path.join(__dirname, '../img', 'apps', 'circles.svg'),
+ 'clippy': path.join(__dirname, '../img', 'actions', 'clippy.svg'),
+ 'close': path.join(__dirname, '../img', 'actions', 'close.svg'),
+ 'comment': path.join(__dirname, '../img', 'actions', 'comment.svg'),
+ 'confirm-fade': path.join(__dirname, '../img', 'actions', 'confirm-fade.svg'),
+ 'confirm': path.join(__dirname, '../img', 'actions', 'confirm.svg'),
+ 'contacts': path.join(__dirname, '../img', 'places', 'contacts.svg'),
+ 'delete': path.join(__dirname, '../img', 'actions', 'delete.svg'),
+ 'desktop': path.join(__dirname, '../img', 'clients', 'desktop.svg'),
+ 'details': path.join(__dirname, '../img', 'actions', 'details.svg'),
+ 'disabled-user': path.join(__dirname, '../img', 'actions', 'disabled-user.svg'),
+ 'disabled-users': path.join(__dirname, '../img', 'actions', 'disabled-users.svg'),
+ 'download': path.join(__dirname, '../img', 'actions', 'download.svg'),
+ 'edit': path.join(__dirname, '../img', 'actions', 'edit.svg'),
+ 'encryption': path.join(__dirname, '../../', 'apps/files_external/img', 'app.svg'),
+ 'error': path.join(__dirname, '../img', 'actions', 'error.svg'),
+ 'external': path.join(__dirname, '../img', 'actions', 'external.svg'),
+ 'favorite': path.join(__dirname, '../img', 'actions', 'star-dark.svg'),
+ 'files': path.join(__dirname, '../img', 'places', 'files.svg'),
+ 'filter': path.join(__dirname, '../img', 'actions', 'filter.svg'),
+ 'folder': path.join(__dirname, '../img', 'filetypes', 'folder.svg'),
+ 'fullscreen': path.join(__dirname, '../img', 'actions', 'fullscreen.svg'),
+ 'group': path.join(__dirname, '../img', 'actions', 'group.svg'),
+ 'history': path.join(__dirname, '../img', 'actions', 'history.svg'),
+ 'home': path.join(__dirname, '../img', 'places', 'home.svg'),
+ 'info': path.join(__dirname, '../img', 'actions', 'info.svg'),
+ 'link': path.join(__dirname, '../img', 'places', 'link.svg'),
+ 'logout': path.join(__dirname, '../img', 'actions', 'logout.svg'),
+ 'mail': path.join(__dirname, '../img', 'actions', 'mail.svg'),
+ 'menu-sidebar': path.join(__dirname, '../img', 'actions', 'menu-sidebar.svg'),
+ 'menu': path.join(__dirname, '../img', 'actions', 'menu.svg'),
+ 'more': path.join(__dirname, '../img', 'actions', 'more.svg'),
+ 'music': path.join(__dirname, '../img', 'places', 'music.svg'),
+ 'password': path.join(__dirname, '../img', 'actions', 'password.svg'),
+ 'pause': path.join(__dirname, '../img', 'actions', 'pause.svg'),
+ 'phone': path.join(__dirname, '../img', 'clients', 'phone.svg'),
+ 'picture': path.join(__dirname, '../img', 'places', 'picture.svg'),
+ 'play-add': path.join(__dirname, '../img', 'actions', 'play-add.svg'),
+ 'play-next': path.join(__dirname, '../img', 'actions', 'play-next.svg'),
+ 'play-previous': path.join(__dirname, '../img', 'actions', 'play-previous.svg'),
+ 'play': path.join(__dirname, '../img', 'actions', 'play.svg'),
+ 'projects': path.join(__dirname, '../img', 'actions', 'projects.svg'),
+ 'public': path.join(__dirname, '../img', 'actions', 'public.svg'),
+ 'quota': path.join(__dirname, '../img', 'actions', 'quota.svg'),
+ 'recent': path.join(__dirname, '../img', 'actions', 'recent.svg'),
+ 'rename': path.join(__dirname, '../img', 'actions', 'rename.svg'),
+ 'screen-off': path.join(__dirname, '../img', 'actions', 'screen-off.svg'),
+ 'screen': path.join(__dirname, '../img', 'actions', 'screen.svg'),
+ 'search': path.join(__dirname, '../img', 'actions', 'search.svg'),
+ 'settings': path.join(__dirname, '../img', 'actions', 'settings-dark.svg'),
+ 'share': path.join(__dirname, '../img', 'actions', 'share.svg'),
+ 'shared': path.join(__dirname, '../img', 'actions', 'share.svg'),
+ 'sound-off': path.join(__dirname, '../img', 'actions', 'sound-off.svg'),
+ 'sound': path.join(__dirname, '../img', 'actions', 'sound.svg'),
+ 'star': path.join(__dirname, '../img', 'actions', 'star.svg'),
+ 'starred': path.join(__dirname, '../img', 'actions', 'star-dark.svg'),
+ 'tablet': path.join(__dirname, '../img', 'clients', 'tablet.svg'),
+ 'tag': path.join(__dirname, '../img', 'actions', 'tag.svg'),
+ 'talk': path.join(__dirname, '../img', 'apps', 'spreed.svg'),
+ 'template-add': path.join(__dirname, '../img', 'actions', 'template-add.svg'),
+ 'timezone': path.join(__dirname, '../img', 'actions', 'timezone.svg'),
+ 'toggle-background': path.join(__dirname, '../img', 'actions', 'toggle-background.svg'),
+ 'toggle-filelist': path.join(__dirname, '../img', 'actions', 'toggle-filelist.svg'),
+ 'toggle-pictures': path.join(__dirname, '../img', 'actions', 'toggle-pictures.svg'),
+ 'toggle': path.join(__dirname, '../img', 'actions', 'toggle.svg'),
+ 'triangle-e': path.join(__dirname, '../img', 'actions', 'triangle-e.svg'),
+ 'triangle-n': path.join(__dirname, '../img', 'actions', 'triangle-n.svg'),
+ 'triangle-s': path.join(__dirname, '../img', 'actions', 'triangle-s.svg'),
+ 'unshare': path.join(__dirname, '../img', 'actions', 'unshare.svg'),
+ 'upload': path.join(__dirname, '../img', 'actions', 'upload.svg'),
+ 'user-admin': path.join(__dirname, '../img', 'actions', 'user-admin.svg'),
+ 'user': path.join(__dirname, '../img', 'actions', 'user.svg'),
+ 'video-off': path.join(__dirname, '../img', 'actions', 'video-off.svg'),
+ 'video-switch': path.join(__dirname, '../img', 'actions', 'video-switch.svg'),
+ 'video': path.join(__dirname, '../img', 'actions', 'video.svg'),
+ 'view-close': path.join(__dirname, '../img', 'actions', 'view-close.svg'),
+ 'view-download': path.join(__dirname, '../img', 'actions', 'view-download.svg'),
+ 'view-next': path.join(__dirname, '../img', 'actions', 'arrow-right.svg'),
+ 'view-pause': path.join(__dirname, '../img', 'actions', 'view-pause.svg'),
+ 'view-play': path.join(__dirname, '../img', 'actions', 'view-play.svg'),
+ 'view-previous': path.join(__dirname, '../img', 'actions', 'arrow-left.svg'),
+}
+
+const iconsColor = {
+ 'settings': {
+ path: path.join(__dirname, '../img', 'actions', 'settings.svg'),
+ color: 'black',
+ },
+ 'error-color': {
+ path: path.join(__dirname, '../img', 'actions', 'error.svg'),
+ color: 'red',
+ },
+ 'checkmark-color': {
+ path: path.join(__dirname, '../img', 'actions', 'checkmark.svg'),
+ color: 'green',
+ },
+ 'starred': {
+ path: path.join(__dirname, '../img', 'actions', 'star-dark.svg'),
+ color: 'yellow',
+ },
+ 'delete-color': {
+ path: path.join(__dirname, '../img', 'actions', 'delete.svg'),
+ color: 'red',
+ },
+ 'file': {
+ path: path.join(__dirname, '../img', 'filetypes', 'text.svg'),
+ color: 'grey',
+ },
+ 'filetype-file': {
+ path: path.join(__dirname, '../img', 'filetypes', 'file.svg'),
+ color: 'grey',
+ },
+ 'filetype-folder': {
+ path: path.join(__dirname, '../img', 'filetypes', 'folder.svg'),
+ // TODO: replace primary ?
+ color: 'primary',
+ },
+ 'filetype-folder-drag-accept': {
+ path: path.join(__dirname, '../img', 'filetypes', 'folder-drag-accept.svg'),
+ // TODO: replace primary ?
+ color: 'primary',
+ },
+}
+
+// use this to define aliases to existing icons
+// key is the css selector, value is the variable
+const iconsAliases = {
+ 'icon-caret': 'icon-caret-white',
+ // starring action
+ 'icon-star:hover': 'icon-starred',
+ 'icon-star:focus': 'icon-starred',
+ // Un-starring action
+ 'icon-starred:hover': 'icon-star',
+ 'icon-starred:focus': 'icon-star',
+ // Delete normal
+ 'icon-delete.no-permission:hover': 'icon-delete-dark',
+ 'icon-delete.no-permission:focus': 'icon-delete-dark',
+ 'icon-delete.no-hover:hover': 'icon-delete-dark',
+ 'icon-delete.no-hover:focus': 'icon-delete-dark',
+ 'icon-delete:hover': 'icon-delete-color-red',
+ 'icon-delete:focus': 'icon-delete-color-red',
+ // Delete white
+ 'icon-delete-white.no-permission:hover': 'icon-delete-white',
+ 'icon-delete-white.no-permission:focus': 'icon-delete-white',
+ 'icon-delete-white.no-hover:hover': 'icon-delete-white',
+ 'icon-delete-white.no-hover:focus': 'icon-delete-white',
+ 'icon-delete-white:hover': 'icon-delete-color-red',
+ 'icon-delete-white:focus': 'icon-delete-color-red',
+ // Default to white
+ 'icon-view-close': 'icon-view-close-white',
+ 'icon-view-download': 'icon-view-download-white',
+ 'icon-view-pause': 'icon-view-pause-white',
+ 'icon-view-play': 'icon-view-play-white',
+ // Default app place to white
+ 'icon-calendar': 'icon-calendar-white',
+ 'icon-contacts': 'icon-contacts-white',
+ 'icon-files': 'icon-files-white',
+ // Re-using existing icons
+ 'icon-category-installed': 'icon-user-dark',
+ 'icon-category-enabled': 'icon-checkmark-dark',
+ 'icon-category-disabled': 'icon-close-dark',
+ 'icon-category-updates': 'icon-download-dark',
+ 'icon-category-security': 'icon-password-dark',
+ 'icon-category-search': 'icon-search-dark',
+ 'icon-category-tools': 'icon-settings-dark',
+ 'icon-filetype-text': 'icon-file-grey',
+}
+
+const colorSvg = function(svg = '', color = '000') {
+ if (!color.match(/^[0-9a-f]{3,6}$/i)) {
+ // Prevent not-sane colors from being written into the SVG
+ console.warn(color, 'does not match the required format')
+ color = '000'
+ }
+
+ // add fill (fill is not present on black elements)
+ const fillRe = /<((circle|rect|path)((?!fill)[a-z0-9 =".\-#():;,])+)\/>/gmi
+ svg = svg.replace(fillRe, '<$1 fill="#' + color + '"/>')
+
+ // replace any fill or stroke colors
+ svg = svg.replace(/stroke="#([a-z0-9]{3,6})"/gmi, 'stroke="#' + color + '"')
+ svg = svg.replace(/fill="#([a-z0-9]{3,6})"/gmi, 'fill="#' + color + '"')
+
+ return svg
+}
+
+const generateVariablesAliases = function(invert = false) {
+ let css = ''
+ Object.keys(variables).forEach(variable => {
+ if (variable.indexOf('original-') !== -1) {
+ let finalVariable = variable.replace('original-', '')
+ if (invert) {
+ finalVariable = finalVariable.replace('white', 'tempwhite')
+ .replace('dark', 'white')
+ .replace('tempwhite', 'dark')
+ }
+ css += `${finalVariable}: var(${variable});`
+ }
+ })
+ return css
+}
+
+const formatIcon = function(icon, invert = false) {
+ const color1 = invert ? 'white' : 'dark'
+ const color2 = invert ? 'dark' : 'white'
+ return `
+ .icon-${icon},
+ .icon-${icon}-dark {
+ background-image: var(--icon-${icon}-${color1});
+ }
+ .icon-${icon}-white,
+ .icon-${icon}.icon-white {
+ background-image: var(--icon-${icon}-${color2});
+ }`
+}
+const formatIconColor = function(icon) {
+ const { color } = iconsColor[icon]
+ return `
+ .icon-${icon} {
+ background-image: var(--icon-${icon}-${color});
+ }`
+}
+const formatAlias = function(alias, invert = false) {
+ let icon = iconsAliases[alias]
+ if (invert) {
+ icon = icon.replace('white', 'tempwhite')
+ .replace('dark', 'white')
+ .replace('tempwhite', 'dark')
+ }
+ return `
+ .${alias} {
+ background-image: var(--${icon})
+ }`
+}
+
+let css = ''
+Object.keys(icons).forEach(icon => {
+ const path = icons[icon]
+
+ const svg = fs.readFileSync(path, 'utf8')
+ const darkSvg = colorSvg(svg, '000000')
+ const whiteSvg = colorSvg(svg, 'ffffff')
+
+ variables[`--original-icon-${icon}-dark`] = Buffer.from(darkSvg, 'utf-8').toString('base64')
+ variables[`--original-icon-${icon}-white`] = Buffer.from(whiteSvg, 'utf-8').toString('base64')
+})
+
+Object.keys(iconsColor).forEach(icon => {
+ const { path, color } = iconsColor[icon]
+
+ const svg = fs.readFileSync(path, 'utf8')
+ const coloredSvg = colorSvg(svg, colors[color])
+ variables[`--icon-${icon}-${color}`] = Buffer.from(coloredSvg, 'utf-8').toString('base64')
+})
+
+// ICONS VARIABLES LIST
+css += ':root {'
+Object.keys(variables).forEach(variable => {
+ const data = variables[variable]
+ css += `${variable}: url(data:image/svg+xml;base64,${data});`
+})
+css += '}'
+
+// DEFAULT THEME
+css += 'body {'
+css += generateVariablesAliases()
+Object.keys(icons).forEach(icon => {
+ css += formatIcon(icon)
+})
+Object.keys(iconsColor).forEach(icon => {
+ css += formatIconColor(icon)
+})
+Object.keys(iconsAliases).forEach(alias => {
+ css += formatAlias(alias)
+})
+css += '}'
+
+// DARK THEME MEDIA QUERY
+css += '@media (prefers-color-scheme: dark) { body {'
+css += generateVariablesAliases(true)
+css += '}}'
+
+// DARK THEME
+css += 'body[data-themes*=light] {'
+css += generateVariablesAliases()
+css += '}'
+
+// DARK THEME
+css += 'body[data-themes*=dark] {'
+css += generateVariablesAliases(true)
+css += '}'
+
+// WRITE CSS
+fs.writeFileSync(path.join(__dirname, '../../dist', 'icons.css'), sass.compileString(css).css)
diff --git a/core/src/jquery/css/jquery.ocdialog.scss b/core/src/jquery/css/jquery.ocdialog.scss
index 280e9ac5f35..c5b6a0dcc64 100644
--- a/core/src/jquery/css/jquery.ocdialog.scss
+++ b/core/src/jquery/css/jquery.ocdialog.scss
@@ -53,7 +53,7 @@
top: 0;
right: 0;
padding: 25px;
- background: var(--icon-close-000) no-repeat center;
+ background: var(--icon-close-dark) no-repeat center;
opacity: .5;
&:hover,
diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php
index 40fd13a1000..0b06dff087e 100644
--- a/core/templates/layout.user.php
+++ b/core/templates/layout.user.php
@@ -42,7 +42,7 @@ $getUserAvatar = static function (int $size) use ($_): string {
</head>
<body id="<?php p($_['bodyid']);?>" <?php foreach ($_['enabledThemes'] as $themeId) {
p("data-theme-$themeId ");
- }?>>
+ }?> data-themes=<?php p(join(',', $_['enabledThemes'])) ?>>
<?php include 'layout.noscript.warning.php'; ?>
<?php foreach ($_['initialStates'] as $app => $initialState) { ?>