aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/ajax/share.php375
-rw-r--r--core/css/apps.css6
-rw-r--r--core/css/header.css4
-rw-r--r--core/css/styles.css16
-rw-r--r--core/js/js.js34
-rw-r--r--core/routes.php3
-rw-r--r--core/templates/layout.user.php8
7 files changed, 26 insertions, 420 deletions
diff --git a/core/ajax/share.php b/core/ajax/share.php
deleted file mode 100644
index adcf31a0dfa..00000000000
--- a/core/ajax/share.php
+++ /dev/null
@@ -1,375 +0,0 @@
-<?php
-/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Bart Visscher <bartv@thisnet.nl>
- * @author Björn Schießle <bjoern@schiessle.org>
- * @author Craig Morrissey <craig@owncloud.com>
- * @author dampfklon <me@dampfklon.de>
- * @author Felix Böhm <felixboehm@gmx.de>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Leonardo Diez <leio10@users.noreply.github.com>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Michael Gapczynski <GapczynskiM@gmail.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Ramiro Aparicio <rapariciog@gmail.com>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- * @author Thomas Tanghus <thomas@tanghus.net>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-
-use OCP\IUser;
-
-OC_JSON::checkLoggedIn();
-OCP\JSON::callCheck();
-
-$defaults = new \OCP\Defaults();
-
-if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSource'])) {
- switch ($_POST['action']) {
- case 'informRecipients':
- $l = \OC::$server->getL10N('core');
- $shareType = (int) $_POST['shareType'];
- $itemType = (string)$_POST['itemType'];
- $itemSource = (string)$_POST['itemSource'];
- $recipient = (string)$_POST['recipient'];
-
- $userManager = \OC::$server->getUserManager();
- $recipientList = [];
- if($shareType === \OCP\Share::SHARE_TYPE_USER) {
- $recipientList[] = $userManager->get($recipient);
- } elseif ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
- $recipientList = \OC_Group::usersInGroup($recipient);
- $group = \OC::$server->getGroupManager()->get($recipient);
- $recipientList = $group->searchUsers('');
- }
- // don't send a mail to the user who shared the file
- $recipientList = array_filter($recipientList, function($user) {
- /** @var IUser $user */
- return $user->getUID() !== \OCP\User::getUser();
- });
-
- $mailNotification = new \OC\Share\MailNotifications(
- \OC::$server->getUserSession()->getUser(),
- \OC::$server->getL10N('lib'),
- \OC::$server->getMailer(),
- \OC::$server->getLogger(),
- $defaults,
- \OC::$server->getURLGenerator()
- );
- $result = $mailNotification->sendInternalShareMail($recipientList, $itemSource, $itemType);
-
- \OCP\Share::setSendMailStatus($itemType, $itemSource, $shareType, $recipient, true);
-
- if (empty($result)) {
- OCP\JSON::success();
- } else {
- OCP\JSON::error(array(
- 'data' => array(
- 'message' => $l->t("Couldn't send mail to following users: %s ",
- implode(', ', $result)
- )
- )
- ));
- }
- break;
- case 'informRecipientsDisabled':
- $itemSource = (string)$_POST['itemSource'];
- $shareType = (int)$_POST['shareType'];
- $itemType = (string)$_POST['itemType'];
- $recipient = (string)$_POST['recipient'];
- \OCP\Share::setSendMailStatus($itemType, $itemSource, $shareType, $recipient, false);
- OCP\JSON::success();
- break;
-
- case 'email':
- // read post variables
- $link = (string)$_POST['link'];
- $file = (string)$_POST['file'];
- $to_address = (string)$_POST['toaddress'];
-
- $mailNotification = new \OC\Share\MailNotifications(
- \OC::$server->getUserSession()->getUser(),
- \OC::$server->getL10N('lib'),
- \OC::$server->getMailer(),
- \OC::$server->getLogger(),
- $defaults,
- \OC::$server->getURLGenerator()
- );
-
- $expiration = null;
- if (isset($_POST['expiration']) && $_POST['expiration'] !== '') {
- try {
- $date = new DateTime((string)$_POST['expiration']);
- $expiration = $date->getTimestamp();
- } catch (Exception $e) {
- \OCP\Util::writeLog('sharing', "Couldn't read date: " . $e->getMessage(), \OCP\Util::ERROR);
- }
- }
-
- $result = $mailNotification->sendLinkShareMail($to_address, $file, $link, $expiration);
- if(empty($result)) {
- // Get the token from the link
- $linkParts = explode('/', $link);
- $token = array_pop($linkParts);
-
- // Get the share for the token
- $share = \OCP\Share::getShareByToken($token, false);
- if ($share !== false) {
- $currentUser = \OC::$server->getUserSession()->getUser()->getUID();
- $file = '/' . ltrim($file, '/');
-
- // Check whether share belongs to the user and whether the file is the same
- if ($share['file_target'] === $file && $share['uid_owner'] === $currentUser) {
-
- // Get the path for the user
- $view = new \OC\Files\View('/' . $currentUser . '/files');
- $fileId = (int) $share['item_source'];
- $path = $view->getPath((int) $share['item_source']);
-
- if ($path !== null) {
- $event = \OC::$server->getActivityManager()->generateEvent();
- $event->setApp(\OCA\Files_Sharing\Activity::FILES_SHARING_APP)
- ->setType(\OCA\Files_Sharing\Activity::TYPE_SHARED)
- ->setAuthor($currentUser)
- ->setAffectedUser($currentUser)
- ->setObject('files', $fileId, $path)
- ->setSubject(\OCA\Files_Sharing\Activity::SUBJECT_SHARED_EMAIL, [$path, $to_address]);
- \OC::$server->getActivityManager()->publish($event);
- }
- }
- }
-
- \OCP\JSON::success();
- } else {
- $l = \OC::$server->getL10N('core');
- OCP\JSON::error(array(
- 'data' => array(
- 'message' => $l->t("Couldn't send mail to following users: %s ",
- implode(', ', $result)
- )
- )
- ));
- }
-
- break;
- }
-} else if (isset($_GET['fetch'])) {
- switch ($_GET['fetch']) {
- case 'getItemsSharedStatuses':
- if (isset($_GET['itemType'])) {
- $return = OCP\Share::getItemsShared((string)$_GET['itemType'], OCP\Share::FORMAT_STATUSES);
- is_array($return) ? OC_JSON::success(array('data' => $return)) : OC_JSON::error();
- }
- break;
- case 'getItem':
- if (isset($_GET['itemType'])
- && isset($_GET['itemSource'])
- && isset($_GET['checkReshare'])
- && isset($_GET['checkShares'])) {
- if ($_GET['checkReshare'] == 'true') {
- $reshare = OCP\Share::getItemSharedWithBySource(
- (string)$_GET['itemType'],
- (string)$_GET['itemSource'],
- OCP\Share::FORMAT_NONE,
- null,
- true
- );
- } else {
- $reshare = false;
- }
- if ($_GET['checkShares'] == 'true') {
- $shares = OCP\Share::getItemShared(
- (string)$_GET['itemType'],
- (string)$_GET['itemSource'],
- OCP\Share::FORMAT_NONE,
- null,
- true
- );
- } else {
- $shares = false;
- }
- OC_JSON::success(array('data' => array('reshare' => $reshare, 'shares' => $shares)));
- }
- break;
- case 'getShareWithEmail':
- $result = array();
- if (isset($_GET['search'])) {
- $cm = OC::$server->getContactsManager();
- if (!is_null($cm) && $cm->isEnabled()) {
- $contacts = $cm->search((string)$_GET['search'], array('FN', 'EMAIL'));
- foreach ($contacts as $contact) {
- if (!isset($contact['EMAIL'])) {
- continue;
- }
-
- $emails = $contact['EMAIL'];
- if (!is_array($emails)) {
- $emails = array($emails);
- }
-
- foreach($emails as $email) {
- $result[] = array(
- 'id' => $contact['id'],
- 'email' => $email,
- 'displayname' => $contact['FN'],
- );
- }
- }
- }
- }
- OC_JSON::success(array('data' => $result));
- break;
- case 'getShareWith':
- if (isset($_GET['search'])) {
- $shareWithinGroupOnly = OC\Share\Share::shareWithGroupMembersOnly();
- $shareWith = array();
- $groups = OC_Group::getGroups((string)$_GET['search']);
- if ($shareWithinGroupOnly) {
- $usergroups = OC_Group::getUserGroups(OC_User::getUser());
- $groups = array_intersect($groups, $usergroups);
- }
-
- $sharedUsers = [];
- $sharedGroups = [];
- if (isset($_GET['itemShares'])) {
- if (isset($_GET['itemShares'][OCP\Share::SHARE_TYPE_USER]) &&
- is_array($_GET['itemShares'][OCP\Share::SHARE_TYPE_USER])) {
- $sharedUsers = $_GET['itemShares'][OCP\Share::SHARE_TYPE_USER];
- }
-
- if (isset($_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP]) &&
- is_array($_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP])) {
- $sharedGroups = $_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP];
- }
- }
-
- $count = 0;
- $users = array();
- $limit = 0;
- $offset = 0;
- // limit defaults to 15 if not specified via request parameter and can be no larger than 500
- $request_limit = min((int)$_GET['limit'] ?: 15, 500);
- while ($count < $request_limit && count($users) == $limit) {
- $limit = $request_limit - $count;
- if ($shareWithinGroupOnly) {
- $users = OC_Group::displayNamesInGroups($usergroups, (string)$_GET['search'], $limit, $offset);
- } else {
- $users = OC_User::getDisplayNames((string)$_GET['search'], $limit, $offset);
- }
-
- $offset += $limit;
- foreach ($users as $uid => $displayName) {
- if (in_array($uid, $sharedUsers)) {
- continue;
- }
-
- if ((!isset($_GET['itemShares'])
- || !is_array($_GET['itemShares'][OCP\Share::SHARE_TYPE_USER])
- || !in_array($uid, $_GET['itemShares'][OCP\Share::SHARE_TYPE_USER]))
- && $uid != OC_User::getUser()) {
- $shareWith[] = array(
- 'label' => $displayName,
- 'value' => array(
- 'shareType' => OCP\Share::SHARE_TYPE_USER,
- 'shareWith' => $uid)
- );
- $count++;
- }
- }
- }
- $count = 0;
-
- // enable l10n support
- $l = \OC::$server->getL10N('core');
-
- foreach ($groups as $group) {
- if (in_array($group, $sharedGroups)) {
- continue;
- }
-
- if ($count < $request_limit) {
- if (!isset($_GET['itemShares'])
- || !isset($_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP])
- || !is_array($_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP])
- || !in_array($group, $_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP])) {
- $shareWith[] = array(
- 'label' => $group,
- 'value' => array(
- 'shareType' => OCP\Share::SHARE_TYPE_GROUP,
- 'shareWith' => $group
- )
- );
- $count++;
- }
- } else {
- break;
- }
- }
-
- // allow user to add unknown remote addresses for server-to-server share
- $backend = \OCP\Share::getBackend((string)$_GET['itemType']);
- if ($backend->isShareTypeAllowed(\OCP\Share::SHARE_TYPE_REMOTE)) {
- if (substr_count((string)$_GET['search'], '@') >= 1) {
- $shareWith[] = array(
- 'label' => (string)$_GET['search'],
- 'value' => array(
- 'shareType' => \OCP\Share::SHARE_TYPE_REMOTE,
- 'shareWith' => (string)$_GET['search']
- )
- );
- }
- $contactManager = \OC::$server->getContactsManager();
- $addressBookContacts = $contactManager->search($_GET['search'], ['CLOUD', 'FN']);
- foreach ($addressBookContacts as $contact) {
- if (isset($contact['CLOUD'])) {
- foreach ($contact['CLOUD'] as $cloudId) {
- $shareWith[] = array(
- 'label' => $contact['FN'] . ' (' . $cloudId . ')',
- 'value' => array(
- 'shareType' => \OCP\Share::SHARE_TYPE_REMOTE,
- 'shareWith' => $cloudId
- )
- );
- }
- }
- }
- }
-
- $sharingAutocompletion = \OC::$server->getConfig()
- ->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes');
-
- if ($sharingAutocompletion !== 'yes') {
- $searchTerm = strtolower($_GET['search']);
- $shareWith = array_filter($shareWith, function($user) use ($searchTerm) {
- return strtolower($user['label']) === $searchTerm
- || strtolower($user['value']['shareWith']) === $searchTerm;
- });
- }
-
- $sorter = new \OC\Share\SearchResultSorter((string)$_GET['search'],
- 'label',
- \OC::$server->getLogger());
- usort($shareWith, array($sorter, 'sort'));
- OC_JSON::success(array('data' => $shareWith));
- }
- break;
- }
-}
diff --git a/core/css/apps.css b/core/css/apps.css
index 0ccdb8a0398..24865cd6f6d 100644
--- a/core/css/apps.css
+++ b/core/css/apps.css
@@ -92,7 +92,7 @@
width: 44px;
margin: 0;
padding: 0;
- background: none; background-image: url('../img/actions/triangle-s.svg');
+ background: none; background-image: url('../img/actions/triangle-s.svg?v=1');
background-size: 16px; background-repeat: no-repeat; background-position: center;
border: none;
border-radius: 0;
@@ -256,7 +256,7 @@
background-color: transparent;
background-repeat: no-repeat;
background-position: center;
- background-image: url('../img/actions/more.svg');
+ background-image: url('../img/actions/more.svg?v=1');
}
#app-navigation .app-navigation-entry-utils-menu-button:hover button,
@@ -497,7 +497,7 @@
padding: 0;
margin: 0;
background-color: #fff;
- background-image: url('../img/actions/settings.svg');
+ background-image: url('../img/actions/settings.svg?v=1');
background-position: 14px center;
background-repeat: no-repeat;
box-shadow: none;
diff --git a/core/css/header.css b/core/css/header.css
index 5a2b8009b15..efdd3be7ceb 100644
--- a/core/css/header.css
+++ b/core/css/header.css
@@ -68,7 +68,7 @@
}
#header .logo {
- background-image: url('../img/logo-icon.svg');
+ background-image: url('../img/logo-icon.svg?v=1');
background-repeat: no-repeat;
background-size: 175px;
background-position: center;
@@ -80,7 +80,7 @@
#header .logo-icon {
/* display logo so appname can be shown next to it */
display: inline-block;
- background-image: url('../img/logo-icon.svg');
+ background-image: url('../img/logo-icon.svg?v=1');
background-repeat: no-repeat;
background-position: center center;
width: 62px;
diff --git a/core/css/styles.css b/core/css/styles.css
index 7f4f7896c94..428688060b7 100644
--- a/core/css/styles.css
+++ b/core/css/styles.css
@@ -26,7 +26,7 @@ body {
#body-login {
text-align: center;
background-color: #0082c9;
- background-image: url('../img/background.jpg');
+ background-image: url('../img/background.jpg?v=1');
background-position: 50% 50%;
background-repeat: no-repeat;
background-size: cover;
@@ -98,7 +98,7 @@ a.two-factor-cancel {
font-size: 1.2em;
padding: 3px;
padding-left: 25px;
- background: transparent url('../img/actions/search-white.svg') no-repeat 6px center;
+ background: transparent url('../img/actions/search-white.svg?v=1') no-repeat 6px center;
color: #fff;
border: 0;
border-radius: 3px;
@@ -465,7 +465,7 @@ label.infield {
position: absolute !important;
height: 20px;
width: 24px;
- background-image: url('../img/actions/toggle.svg');
+ background-image: url('../img/actions/toggle.svg?v=1');
background-repeat: no-repeat;
background-position: center;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)";
@@ -757,15 +757,15 @@ a.bookmarklet { background-color:#ddd; border:1px solid #ccc; padding:5px;paddin
.exception{color:#000;}
.exception textarea{width:95%;height:200px;background:#ffe;border:0;}
-.ui-icon-circle-triangle-e{ background-image:url('../img/actions/play-next.svg'); }
-.ui-icon-circle-triangle-w{ background-image:url('../img/actions/play-previous.svg'); }
+.ui-icon-circle-triangle-e{ background-image:url('../img/actions/play-next.svg?v=1'); }
+.ui-icon-circle-triangle-w{ background-image:url('../img/actions/play-previous.svg?v=1'); }
.ui-datepicker-prev,.ui-datepicker-next{ border:1px solid #ddd; background:#fff; }
/* ---- DIALOGS ---- */
#oc-dialog-filepicker-content .dirtree {width:92%; overflow:hidden; }
#oc-dialog-filepicker-content .dirtree .home {
- background-image:url('../img/places/home.svg');
+ background-image:url('../img/places/home.svg?v=1');
background-repeat:no-repeat;
background-position: left center;
width: 30px;
@@ -848,7 +848,7 @@ span.ui-icon {float: left; margin: 3px 7px 30px 0;}
}
.popup.topright { top:7em; right:1em; }
.popup.bottomleft { bottom:1em; left:33em; }
-.popup .close { position:absolute; top:0.2em; right:0.2em; height:20px; width:20px; background:url('../img/actions/close.svg') no-repeat center; }
+.popup .close { position:absolute; top:0.2em; right:0.2em; height:20px; width:20px; background:url('../img/actions/close.svg?v=1') no-repeat center; }
.popup h2 { font-size:20px; }
.arrow { border-bottom:10px solid white; border-left:10px solid transparent; border-right:10px solid transparent; display:block; height:0; position:absolute; width:0; z-index:201; }
.arrow.left { left:-13px; bottom:1.2em; -webkit-transform:rotate(270deg); -ms-transform:rotate(270deg); transform:rotate(270deg); }
@@ -860,7 +860,7 @@ span.ui-icon {float: left; margin: 3px 7px 30px 0;}
div.crumb {
float: left;
display: block;
- background-image: url('../img/breadcrumb.svg');
+ background-image: url('../img/breadcrumb.svg?v=1');
background-repeat: no-repeat;
background-position: right center;
height: 44px;
diff --git a/core/js/js.js b/core/js/js.js
index ea621123fb0..4e8d3a01416 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -1501,21 +1501,17 @@ function initCore() {
$navigation.hide();
// show loading feedback
- $navigation.delegate('a', 'mousedown', function(event) {
+ $navigation.delegate('a', 'click', function(event) {
var $app = $(event.target);
if(!$app.is('a')) {
$app = $app.closest('a');
}
- if(event.which === 1 && !event.ctrlKey && !event.metaKey) {
+ if(!event.ctrlKey) {
$app.addClass('app-loading');
} else {
- // On middle click or on first button click with ctrl key or meta key hold
- if(event.which === 2 || (event.which === 1 && (event.ctrlKey || event.metaKey))) {
- // Close navigation when opening app in
- // a new tab
- OC.hideMenus();
- window.open($app, '_blank');
- }
+ // Close navigation when opening app in
+ // a new tab
+ OC.hideMenus();
}
});
}
@@ -1523,26 +1519,14 @@ function initCore() {
function setupUserMenu() {
var $menu = $('#header #settings');
- // show loading feedback
- $menu.delegate('a', 'mousedown', function(event) {
+ $menu.delegate('a', 'click', function(event) {
var $page = $(event.target);
if (!$page.is('a')) {
$page = $page.closest('a');
}
- if(event.which === 1 && !event.ctrlKey && !event.metaKey) {
- $page.find('img').remove();
- $page.find('div').remove(); // prevent odd double-clicks
- $page.prepend($('<div/>').addClass('icon-loading-small-dark'));
- } else {
- // On middle click or on first button click with ctrl key or meta key hold
- if(event.which === 2 || (event.which === 1 && (event.ctrlKey || event.metaKey))) {
- // Close navigation when opening menu entry in
- // a new tab
- OC.hideMenus();
- window.open($page, '_blank');
- }
- }
- $($page).click();
+ $page.find('img').remove();
+ $page.find('div').remove(); // prevent odd double-clicks
+ $page.prepend($('<div/>').addClass('icon-loading-small-dark'));
});
}
diff --git a/core/routes.php b/core/routes.php
index 90b8b4d2ace..3eec1e58c33 100644
--- a/core/routes.php
+++ b/core/routes.php
@@ -70,9 +70,6 @@ $this->create('search_ajax_search', '/core/search')
// AppConfig
$this->create('core_ajax_appconfig', '/core/ajax/appconfig.php')
->actionInclude('core/ajax/appconfig.php');
-// Share
-$this->create('core_ajax_share', '/core/ajax/share.php')
- ->actionInclude('core/ajax/share.php');
// Tags
$this->create('core_tags_tags', '/tags/{type}')
->get()
diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php
index 2c0d3f05297..17f895bc17d 100644
--- a/core/templates/layout.user.php
+++ b/core/templates/layout.user.php
@@ -81,14 +81,14 @@
<li>
<a href="<?php print_unescaped($entry['href']); ?>"
<?php if( $entry["active"] ): ?> class="active"<?php endif; ?>>
- <img alt="" src="<?php print_unescaped($entry['icon']); ?>">
+ <img alt="" src="<?php print_unescaped($entry['icon'] . '?v=' . $_['versionHash']); ?>">
<?php p($entry['name']) ?>
</a>
</li>
<?php endforeach; ?>
<li>
<a id="logout" <?php print_unescaped(OC_User::getLogoutAttribute()); ?>>
- <img alt="" src="<?php print_unescaped(image_path('', 'actions/logout.svg')); ?>">
+ <img alt="" src="<?php print_unescaped(image_path('', 'actions/logout.svg') . '?v=' . $_['versionHash']); ?>">
<?php p($l->t('Log out'));?>
</a>
</li>
@@ -115,7 +115,7 @@
<?php if( $entry['active'] ): ?> class="active"<?php endif; ?>>
<svg width="32" height="32" viewBox="0 0 32 32">
<defs><filter id="invert"><feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"></feColorMatrix></filter></defs>
- <image x="0" y="0" width="32" height="32" preserveAspectRatio="xMinYMin meet" filter="url(#invert)" xlink:href="<?php print_unescaped($entry['icon']); ?>" class="app-icon"></image>
+ <image x="0" y="0" width="32" height="32" preserveAspectRatio="xMinYMin meet" filter="url(#invert)" xlink:href="<?php print_unescaped($entry['icon'] . '?v=' . $_['versionHash']); ?>" class="app-icon"></image>
</svg>
<div class="icon-loading-dark" style="display:none;"></div>
<span>
@@ -133,7 +133,7 @@
<?php if( $_['appsmanagement_active'] ): ?> class="active"<?php endif; ?>>
<svg width="32" height="32" viewBox="0 0 32 32" class="app-icon">
<defs><filter id="invert"><feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"></feColorMatrix></filter></defs>
- <image x="0" y="0" width="32" height="32" preserveAspectRatio="xMinYMin meet" filter="url(#invert)" xlink:href="<?php print_unescaped(image_path('settings', 'apps.svg')); ?>"></image>
+ <image x="0" y="0" width="32" height="32" preserveAspectRatio="xMinYMin meet" filter="url(#invert)" xlink:href="<?php print_unescaped(image_path('settings', 'apps.svg') . '?v=' . $_['versionHash']); ?>"></image>
</svg>
<div class="icon-loading-dark" style="display:none;"></div>
<span>