aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/comments/js/commentstabview.js2
-rw-r--r--apps/comments/tests/js/commentstabviewSpec.js4
-rw-r--r--apps/dav/appinfo/info.xml5
-rw-r--r--apps/dav/composer/composer/autoload_classmap.php1
-rw-r--r--apps/dav/composer/composer/autoload_static.php1
-rw-r--r--apps/dav/lib/CardDAV/SyncJob.php42
-rw-r--r--apps/files/css/files.scss4
-rw-r--r--apps/files/js/file-upload.js2
-rw-r--r--apps/files/js/filelist.js4
-rw-r--r--apps/files/js/files.js27
-rw-r--r--apps/files/lib/Helper.php2
-rw-r--r--apps/files/templates/appnavigation.php2
-rw-r--r--apps/files/tests/js/filelistSpec.js24
-rw-r--r--apps/files_external/js/mountsfilelist.js3
-rw-r--r--apps/files_external/js/statusmanager.js10
-rw-r--r--apps/files_sharing/js/public.js7
-rw-r--r--apps/user_ldap/lib/Access.php7
-rw-r--r--apps/user_ldap/lib/Connection.php2
-rw-r--r--apps/user_ldap/lib/LDAP.php17
-rw-r--r--build/package.json1
-rw-r--r--core/Controller/LoginController.php1
-rw-r--r--core/js/js.js37
-rw-r--r--core/js/tests/specs/coreSpec.js427
-rw-r--r--core/templates/login.php2
-rw-r--r--core/vendor/core.js6
-rw-r--r--core/vendor/snapjs/dist/latest/snap.js6
-rw-r--r--issue_template.md4
-rw-r--r--lib/private/App/AppStore/Fetcher/Fetcher.php3
-rw-r--r--lib/private/Console/TimestampFormatter.php6
-rw-r--r--lib/private/Template/CSSResourceLocator.php4
-rw-r--r--lib/private/Template/SCSSCacher.php31
-rw-r--r--settings/Activity/SecurityProvider.php2
-rw-r--r--settings/js/apps.js12
-rw-r--r--tests/Core/Controller/LoginControllerTest.php33
-rw-r--r--tests/Settings/Activity/SecurityProviderTest.php4
-rw-r--r--tests/karma.config.js2
-rw-r--r--tests/lib/LegacyHelperTest.php11
-rw-r--r--tests/lib/Template/SCSSCacherTest.php64
-rw-r--r--tests/lib/UrlGeneratorTest.php10
39 files changed, 719 insertions, 113 deletions
diff --git a/apps/comments/js/commentstabview.js b/apps/comments/js/commentstabview.js
index 9b75cb4671e..bd89c8bbb49 100644
--- a/apps/comments/js/commentstabview.js
+++ b/apps/comments/js/commentstabview.js
@@ -512,7 +512,7 @@
_onTypeComment: function(ev) {
var $field = $(ev.target);
- var len = $field.val().length;
+ var len = $field.text().length;
var $submitButton = $field.data('submitButtonEl');
if (!$submitButton) {
$submitButton = $field.closest('form').find('.submit');
diff --git a/apps/comments/tests/js/commentstabviewSpec.js b/apps/comments/tests/js/commentstabviewSpec.js
index 813b2a72eae..0131bc7bce3 100644
--- a/apps/comments/tests/js/commentstabviewSpec.js
+++ b/apps/comments/tests/js/commentstabviewSpec.js
@@ -411,7 +411,7 @@ describe('OCA.Comments.CommentsTabView tests', function() {
expect($message.hasClass('error')).toEqual(false);
});
it('displays tooltip when limit is almost reached', function() {
- $message.val(createMessageWithLength(view._commentMaxLength - 2));
+ $message.text(createMessageWithLength(view._commentMaxLength - 2));
$message.trigger('change');
expect(tooltipStub.calledWith('show')).toEqual(true);
@@ -419,7 +419,7 @@ describe('OCA.Comments.CommentsTabView tests', function() {
expect($message.hasClass('error')).toEqual(false);
});
it('displays tooltip and disabled button when limit is exceeded', function() {
- $message.val(createMessageWithLength(view._commentMaxLength + 2));
+ $message.text(createMessageWithLength(view._commentMaxLength + 2));
$message.trigger('change');
expect(tooltipStub.calledWith('show')).toEqual(true);
diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml
index 0f97289ba37..25a0d542e85 100644
--- a/apps/dav/appinfo/info.xml
+++ b/apps/dav/appinfo/info.xml
@@ -5,7 +5,7 @@
<description>WebDAV endpoint</description>
<licence>AGPL</licence>
<author>owncloud.org</author>
- <version>1.4.5</version>
+ <version>1.4.6</version>
<default_enable/>
<types>
<filesystem/>
@@ -17,9 +17,6 @@
<dependencies>
<nextcloud min-version="13" max-version="13" />
</dependencies>
- <background-jobs>
- <job>OCA\DAV\CardDAV\SyncJob</job>
- </background-jobs>
<repair-steps>
<post-migration>
<step>OCA\DAV\Migration\FixBirthdayCalendarComponent</step>
diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php
index ddb8d7ca8d2..4d2db8e2f5e 100644
--- a/apps/dav/composer/composer/autoload_classmap.php
+++ b/apps/dav/composer/composer/autoload_classmap.php
@@ -59,7 +59,6 @@ return array(
'OCA\\DAV\\CardDAV\\ImageExportPlugin' => $baseDir . '/../lib/CardDAV/ImageExportPlugin.php',
'OCA\\DAV\\CardDAV\\PhotoCache' => $baseDir . '/../lib/CardDAV/PhotoCache.php',
'OCA\\DAV\\CardDAV\\Plugin' => $baseDir . '/../lib/CardDAV/Plugin.php',
- 'OCA\\DAV\\CardDAV\\SyncJob' => $baseDir . '/../lib/CardDAV/SyncJob.php',
'OCA\\DAV\\CardDAV\\SyncService' => $baseDir . '/../lib/CardDAV/SyncService.php',
'OCA\\DAV\\CardDAV\\UserAddressBooks' => $baseDir . '/../lib/CardDAV/UserAddressBooks.php',
'OCA\\DAV\\CardDAV\\Xml\\Groups' => $baseDir . '/../lib/CardDAV/Xml/Groups.php',
diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php
index 46707a93912..42f2e6da286 100644
--- a/apps/dav/composer/composer/autoload_static.php
+++ b/apps/dav/composer/composer/autoload_static.php
@@ -74,7 +74,6 @@ class ComposerStaticInitDAV
'OCA\\DAV\\CardDAV\\ImageExportPlugin' => __DIR__ . '/..' . '/../lib/CardDAV/ImageExportPlugin.php',
'OCA\\DAV\\CardDAV\\PhotoCache' => __DIR__ . '/..' . '/../lib/CardDAV/PhotoCache.php',
'OCA\\DAV\\CardDAV\\Plugin' => __DIR__ . '/..' . '/../lib/CardDAV/Plugin.php',
- 'OCA\\DAV\\CardDAV\\SyncJob' => __DIR__ . '/..' . '/../lib/CardDAV/SyncJob.php',
'OCA\\DAV\\CardDAV\\SyncService' => __DIR__ . '/..' . '/../lib/CardDAV/SyncService.php',
'OCA\\DAV\\CardDAV\\UserAddressBooks' => __DIR__ . '/..' . '/../lib/CardDAV/UserAddressBooks.php',
'OCA\\DAV\\CardDAV\\Xml\\Groups' => __DIR__ . '/..' . '/../lib/CardDAV/Xml/Groups.php',
diff --git a/apps/dav/lib/CardDAV/SyncJob.php b/apps/dav/lib/CardDAV/SyncJob.php
deleted file mode 100644
index f0f8d51c2ca..00000000000
--- a/apps/dav/lib/CardDAV/SyncJob.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Joas Schilling <coding@schilljs.com>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-
-namespace OCA\DAV\CardDAV;
-
-use OC\BackgroundJob\TimedJob;
-use OCA\DAV\AppInfo\Application;
-
-class SyncJob extends TimedJob {
-
- public function __construct() {
- // Run once a day
- $this->setInterval(24 * 60 * 60);
- }
-
- protected function run($argument) {
- $app = new Application();
- /** @var SyncService $ss */
- $ss = $app->getSyncService();
- $ss->syncInstance();
- }
-}
diff --git a/apps/files/css/files.scss b/apps/files/css/files.scss
index dce5008ad19..b29ce9ea950 100644
--- a/apps/files/css/files.scss
+++ b/apps/files/css/files.scss
@@ -242,12 +242,12 @@ table th.column-last, table td.column-last {
/* Multiselect bar */
#filestable.multiselect {
- top: 95px;
+ top: 51px;
}
table.multiselect thead {
position: fixed;
top: 89px;
- z-index: 10;
+ z-index: 55;
-moz-box-sizing: border-box;
box-sizing: border-box;
left: 250px; /* sidebar */
diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js
index d1730fa7bc7..e9534111e10 100644
--- a/apps/files/js/file-upload.js
+++ b/apps/files/js/file-upload.js
@@ -130,7 +130,7 @@ OC.FileUpload.prototype = {
},
/**
- * Get full path for the target file,
+ * Get full path for the target file,
* including relative path and file name.
*
* @return {String} full path
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index 7735e9357b1..d0c0fc1a7fc 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -683,11 +683,14 @@
// the details to be shown.
event.preventDefault();
var filename = $tr.attr('data-file');
+ this.fileActions.currentFile = $tr.find('td');
var mime = this.fileActions.getCurrentMimeType();
var type = this.fileActions.getCurrentType();
var permissions = this.fileActions.getCurrentPermissions();
var action = this.fileActions.get(mime, type, permissions)['Details'];
if (action) {
+ // also set on global object for legacy apps
+ window.FileActions.currentFile = this.fileActions.currentFile;
action(filename, {
$file: $tr,
fileList: this,
@@ -1768,7 +1771,6 @@
return true;
}
- // TODO: parse remaining quota from PROPFIND response
this.updateStorageStatistics(true);
// first entry is the root
diff --git a/apps/files/js/files.js b/apps/files/js/files.js
index 479a2817f44..153307fec52 100644
--- a/apps/files/js/files.js
+++ b/apps/files/js/files.js
@@ -29,6 +29,7 @@
state.dir = null;
state.call = null;
Files.updateMaxUploadFilesize(response);
+ Files.updateQuota(response);
});
},
/**
@@ -77,6 +78,32 @@
},
+ updateQuota:function(response) {
+ if (response === undefined) {
+ return;
+ }
+ if (response.data !== undefined
+ && response.data.quota !== undefined
+ && response.data.used !== undefined
+ && response.data.usedSpacePercent !== undefined) {
+ var humanUsed = OC.Util.humanFileSize(response.data.used, true);
+ var humanQuota = OC.Util.humanFileSize(response.data.quota, true);
+ if (response.data.quota > 0) {
+ $('#quota').attr('data-original-title', Math.floor(response.data.used/response.data.quota*1000)/10 + '%');
+ $('#quota progress').val(response.data.usedSpacePercent);
+ $('#quotatext').text(t('files', '{used} of {quota} used', {used: humanUsed, quota: humanQuota}));
+ } else {
+ $('#quotatext').text(t('files', '{used} used', {used: humanUsed}));
+ }
+ if (response.data.usedSpacePercent > 80) {
+ $('#quota progress').addClass('warn');
+ } else {
+ $('#quota progress').removeClass('warn');
+ }
+ }
+
+ },
+
/**
* Fix path name by removing double slash at the beginning, if any
*/
diff --git a/apps/files/lib/Helper.php b/apps/files/lib/Helper.php
index ab952c97dfb..9d9717c9401 100644
--- a/apps/files/lib/Helper.php
+++ b/apps/files/lib/Helper.php
@@ -56,6 +56,8 @@ class Helper {
'uploadMaxFilesize' => $maxUploadFileSize,
'maxHumanFilesize' => $maxHumanFileSize,
'freeSpace' => $storageInfo['free'],
+ 'quota' => $storageInfo['quota'],
+ 'used' => $storageInfo['used'],
'usedSpacePercent' => (int)$storageInfo['relative'],
'owner' => $storageInfo['owner'],
'ownerDisplayName' => $storageInfo['ownerDisplayName'],
diff --git a/apps/files/templates/appnavigation.php b/apps/files/templates/appnavigation.php
index 955cd03a019..5d270914ff1 100644
--- a/apps/files/templates/appnavigation.php
+++ b/apps/files/templates/appnavigation.php
@@ -11,7 +11,7 @@
</a>
</li>
<?php } ?>
- <li id="quota" class="pinned <?php
+ <li id="quota" class="pinned <?php p($pinned===0?'first-pinned ':'') ?><?php
if ($_['quota'] !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
?>has-tooltip" title="<?php p($_['usage_relative'] . '%');
} ?>">
diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js
index 83926b24fee..fc5a6c18f95 100644
--- a/apps/files/tests/js/filelistSpec.js
+++ b/apps/files/tests/js/filelistSpec.js
@@ -2489,6 +2489,30 @@ describe('OCA.Files.FileList tests', function() {
expect(context.fileActions).toBeDefined();
expect(context.dir).toEqual('/subdir');
});
+ it('Clicking on an empty space of the file row will trigger the "Details" action', function() {
+ var detailsActionStub = sinon.stub();
+ fileList.setFiles(testFiles);
+ // Override the "Details" action set internally by the FileList for
+ // easier testing.
+ fileList.fileActions.registerAction({
+ mime: 'all',
+ name: 'Details',
+ permissions: OC.PERMISSION_NONE,
+ actionHandler: detailsActionStub
+ });
+ // Ensure that the action works even if fileActions.currentFile is
+ // not set.
+ fileList.fileActions.currentFile = null;
+ var $tr = fileList.findFileEl('One.txt');
+ $tr.find('td.filename a.name').click();
+ expect(detailsActionStub.calledOnce).toEqual(true);
+ expect(detailsActionStub.getCall(0).args[0]).toEqual('One.txt');
+ var context = detailsActionStub.getCall(0).args[1];
+ expect(context.$file.is($tr)).toEqual(true);
+ expect(context.fileList).toBe(fileList);
+ expect(context.fileActions).toBe(fileList.fileActions);
+ expect(context.dir).toEqual('/subdir');
+ });
it('redisplays actions when new actions have been registered', function() {
var actionStub = sinon.stub();
var readyHandler = sinon.stub();
diff --git a/apps/files_external/js/mountsfilelist.js b/apps/files_external/js/mountsfilelist.js
index 3beee6f73b2..90b90e38745 100644
--- a/apps/files_external/js/mountsfilelist.js
+++ b/apps/files_external/js/mountsfilelist.js
@@ -30,6 +30,8 @@
/** @lends OCA.External.FileList.prototype */ {
appName: 'External storages',
+ _allowSelection: false,
+
/**
* @private
*/
@@ -56,7 +58,6 @@
$scopeColumn.find('span').text(scopeText);
$backendColumn.text(fileData.backend);
$tr.find('td.filename').after($scopeColumn).after($backendColumn);
- $tr.find('td.filename input:checkbox').remove();
return $tr;
},
diff --git a/apps/files_external/js/statusmanager.js b/apps/files_external/js/statusmanager.js
index 6c1a965c33d..3850351d213 100644
--- a/apps/files_external/js/statusmanager.js
+++ b/apps/files_external/js/statusmanager.js
@@ -489,7 +489,7 @@ OCA.External.StatusManager.Utils = {
}
});
- var icon = trFolder.find('td:first-child div.thumbnail');
+ var icon = trFolder.find('td.filename div.thumbnail');
icon.each(function () {
var thisElement = $(this);
if (thisElement.data('oldImage') === undefined) {
@@ -510,7 +510,7 @@ OCA.External.StatusManager.Utils = {
trFolder = $('#fileList tr[data-file=\"' + OCA.External.StatusManager.Utils.jqSelEscape(folder) + '\"]');
}
trFolder.removeClass('externalErroredRow').removeClass('externalDisabledRow');
- var tdChilds = trFolder.find("td:first-child div.thumbnail");
+ var tdChilds = trFolder.find("td.filename div.thumbnail");
tdChilds.each(function () {
var thisElement = $(this);
thisElement.css('background-image', thisElement.data('oldImage'));
@@ -529,10 +529,10 @@ OCA.External.StatusManager.Utils = {
$.each(filename, function (index) {
route = OCA.External.StatusManager.Utils.getIconRoute($(this));
$(this).attr("data-icon", route);
- $(this).find('td:first-child div.thumbnail').css('background-image', "url(" + route + ")").css('display', 'none').css('display', 'inline');
+ $(this).find('td.filename div.thumbnail').css('background-image', "url(" + route + ")").css('display', 'none').css('display', 'inline');
});
} else {
- file = $("#fileList tr[data-file=\"" + this.jqSelEscape(filename) + "\"] > td:first-child div.thumbnail");
+ file = $("#fileList tr[data-file=\"" + this.jqSelEscape(filename) + "\"] > td.filename div.thumbnail");
var parentTr = file.parents('tr:first');
route = OCA.External.StatusManager.Utils.getIconRoute(parentTr);
parentTr.attr("data-icon", route);
@@ -573,7 +573,7 @@ OCA.External.StatusManager.Utils = {
if (filename instanceof $) {
link = filename;
} else {
- link = $("#fileList tr[data-file=\"" + this.jqSelEscape(filename) + "\"] > td:first-child a.name");
+ link = $("#fileList tr[data-file=\"" + this.jqSelEscape(filename) + "\"] > td.filename a.name");
}
if (active) {
link.off('click.connectivity');
diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js
index 2142dec1218..ae19500080b 100644
--- a/apps/files_sharing/js/public.js
+++ b/apps/files_sharing/js/public.js
@@ -434,10 +434,13 @@ $(document).ready(function () {
$(document).mouseup(function(e) {
+ var toggle = $('#share-menutoggle');
var container = $('#share-menu');
- // if the target of the click isn't the container nor a descendant of the container
- if (!container.is(e.target) && container.has(e.target).length === 0) {
+ // if the target of the click isn't the menu toggle, nor a descendant of the
+ // menu toggle, nor the container nor a descendant of the container
+ if (!toggle.is(e.target) && toggle.has(e.target).length === 0 &&
+ !container.is(e.target) && container.has(e.target).length === 0) {
container.removeClass('open');
}
});
diff --git a/apps/user_ldap/lib/Access.php b/apps/user_ldap/lib/Access.php
index 27fda38a737..14d5b826f63 100644
--- a/apps/user_ldap/lib/Access.php
+++ b/apps/user_ldap/lib/Access.php
@@ -1095,8 +1095,11 @@ class Access extends LDAPUtility implements IUserTools {
$this->pagedSearchedSuccessful = true;
}
} else {
- if(!is_null($limit)) {
- \OCP\Util::writeLog('user_ldap', 'Paged search was not available', \OCP\Util::INFO);
+ if(!is_null($limit) && intval($this->connection->ldapPagingSize) !== 0) {
+ \OC::$server->getLogger()->debug(
+ 'Paged search was not available',
+ [ 'app' => 'user_ldap' ]
+ );
}
}
/* ++ Fixing RHDS searches with pages with zero results ++
diff --git a/apps/user_ldap/lib/Connection.php b/apps/user_ldap/lib/Connection.php
index bde489e2710..c73a35e6bf1 100644
--- a/apps/user_ldap/lib/Connection.php
+++ b/apps/user_ldap/lib/Connection.php
@@ -50,7 +50,7 @@ use OC\ServerNotAvailableException;
* @property boolean turnOnPasswordChange
* @property boolean hasPagedResultSupport
* @property string[] ldapBaseUsers
- * @property int|string ldapPagingSize holds an integer
+ * @property int|null ldapPagingSize holds an integer
* @property bool|mixed|void ldapGroupMemberAssocAttr
* @property string ldapUuidUserAttribute
* @property string ldapUuidGroupAttribute
diff --git a/apps/user_ldap/lib/LDAP.php b/apps/user_ldap/lib/LDAP.php
index eafd8eacd06..bdc2f204225 100644
--- a/apps/user_ldap/lib/LDAP.php
+++ b/apps/user_ldap/lib/LDAP.php
@@ -63,8 +63,8 @@ class LDAP implements ILDAPWrapper {
}
/**
- * @param LDAP $link
- * @param LDAP $result
+ * @param resource $link
+ * @param resource $result
* @param string $cookie
* @return bool|LDAP
*/
@@ -331,6 +331,8 @@ class LDAP implements ILDAPWrapper {
//referrals, we switch them off, but then there is AD :)
} else if ($errorCode === -1) {
throw new ServerNotAvailableException('Lost connection to LDAP server.');
+ } else if ($errorCode === 52) {
+ throw new ServerNotAvailableException('LDAP server is shutting down.');
} else if ($errorCode === 48) {
throw new \Exception('LDAP authentication method rejected', $errorCode);
} else if ($errorCode === 1) {
@@ -339,11 +341,12 @@ class LDAP implements ILDAPWrapper {
ldap_get_option($this->curArgs[0], LDAP_OPT_ERROR_STRING, $extended_error);
throw new ConstraintViolationException(!empty($extended_error)?$extended_error:$errorMsg, $errorCode);
} else {
- \OCP\Util::writeLog('user_ldap',
- 'LDAP error '.$errorMsg.' (' .
- $errorCode.') after calling '.
- $this->curFunc,
- \OCP\Util::DEBUG);
+ \OC::$server->getLogger()->debug('LDAP error {message} ({code}) after calling {func}', [
+ 'app' => 'user_ldap',
+ 'message' => $errorMsg,
+ 'code' => $errorCode,
+ 'func' => $this->curFunc,
+ ]);
}
}
diff --git a/build/package.json b/build/package.json
index b078727805d..e6688966693 100644
--- a/build/package.json
+++ b/build/package.json
@@ -22,6 +22,7 @@
"karma-jasmine-sinon": "^1.0.4",
"karma-junit-reporter": "*",
"karma-phantomjs-launcher": "*",
+ "karma-viewport": "^0.4.2",
"phantomjs-prebuilt": "*",
"node-sass": "~4.1.1",
"sinon": "*"
diff --git a/core/Controller/LoginController.php b/core/Controller/LoginController.php
index e87e097e423..e53095a7de7 100644
--- a/core/Controller/LoginController.php
+++ b/core/Controller/LoginController.php
@@ -179,6 +179,7 @@ class LoginController extends Controller {
$parameters['alt_login'] = OC_App::getAlternativeLogIns();
$parameters['rememberLoginState'] = !empty($remember_login) ? $remember_login : 0;
+ $parameters['hideRemeberLoginState'] = !empty($redirect_url) && $this->session->exists('client.flow.state.token');
if (!is_null($user) && $user !== '') {
$parameters['loginName'] = $user;
diff --git a/core/js/js.js b/core/js/js.js
index 9af80676d5e..a9180663405 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -1612,12 +1612,47 @@ function initCore() {
snapper.close();
});
+ var navigationBarSlideGestureEnabled = false;
+ var navigationBarSlideGestureAllowed = true;
+ var navigationBarSlideGestureEnablePending = false;
+
+ OC.allowNavigationBarSlideGesture = function() {
+ navigationBarSlideGestureAllowed = true;
+
+ if (navigationBarSlideGestureEnablePending) {
+ snapper.enable();
+
+ navigationBarSlideGestureEnabled = true;
+ navigationBarSlideGestureEnablePending = false;
+ }
+ };
+
+ OC.disallowNavigationBarSlideGesture = function() {
+ navigationBarSlideGestureAllowed = false;
+
+ if (navigationBarSlideGestureEnabled) {
+ var endCurrentDrag = true;
+ snapper.disable(endCurrentDrag);
+
+ navigationBarSlideGestureEnabled = false;
+ navigationBarSlideGestureEnablePending = true;
+ }
+ };
+
var toggleSnapperOnSize = function() {
if($(window).width() > 768) {
snapper.close();
snapper.disable();
- } else {
+
+ navigationBarSlideGestureEnabled = false;
+ navigationBarSlideGestureEnablePending = false;
+ } else if (navigationBarSlideGestureAllowed) {
snapper.enable();
+
+ navigationBarSlideGestureEnabled = true;
+ navigationBarSlideGestureEnablePending = false;
+ } else {
+ navigationBarSlideGestureEnablePending = true;
}
};
diff --git a/core/js/tests/specs/coreSpec.js b/core/js/tests/specs/coreSpec.js
index 9848fb46ffc..b6c617303cf 100644
--- a/core/js/tests/specs/coreSpec.js
+++ b/core/js/tests/specs/coreSpec.js
@@ -1112,4 +1112,431 @@ describe('Core base tests', function() {
expect(OC._ajaxConnectionLostHandler.calls.count()).toBe(1);
});
});
+ describe('Snapper', function() {
+ var snapConstructorStub;
+ var snapperStub;
+ var clock;
+
+ beforeEach(function() {
+ snapConstructorStub = sinon.stub(window, 'Snap');
+
+ snapperStub = {};
+ snapperStub.enable = sinon.stub();
+ snapperStub.disable = sinon.stub();
+ snapperStub.close = sinon.stub();
+
+ snapConstructorStub.returns(snapperStub);
+
+ clock = sinon.useFakeTimers();
+
+ // _.now could have been set to Date.now before Sinon replaced it
+ // with a fake version, so _.now must be stubbed to ensure that the
+ // fake Date.now will be called instead of the original one.
+ _.now = sinon.stub(_, 'now').callsFake(function() {
+ return new Date().getTime();
+ });
+
+ $('#testArea').append('<div id="app-navigation">The navigation bar</div><div id="app-content">Content</div>');
+ });
+ afterEach(function() {
+ snapConstructorStub.restore();
+
+ clock.restore();
+
+ _.now.restore();
+
+ // Remove the event handler for the resize event added to the window
+ // due to calling window.initCore() when there is an #app-navigation
+ // element.
+ $(window).off('resize');
+
+ viewport.reset();
+ });
+
+ it('is enabled on a narrow screen', function() {
+ viewport.set(480);
+
+ window.initCore();
+
+ expect(snapConstructorStub.calledOnce).toBe(true);
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.called).toBe(false);
+ });
+ it('is disabled when disallowing the gesture on a narrow screen', function() {
+ viewport.set(480);
+
+ window.initCore();
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.called).toBe(false);
+ expect(snapperStub.close.called).toBe(false);
+
+ OC.disallowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.disable.alwaysCalledWithExactly(true)).toBe(true);
+ expect(snapperStub.close.called).toBe(false);
+ });
+ it('is not disabled again when disallowing the gesture twice on a narrow screen', function() {
+ viewport.set(480);
+
+ window.initCore();
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.called).toBe(false);
+ expect(snapperStub.close.called).toBe(false);
+
+ OC.disallowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.disable.alwaysCalledWithExactly(true)).toBe(true);
+ expect(snapperStub.close.called).toBe(false);
+
+ OC.disallowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.close.called).toBe(false);
+ });
+ it('is enabled when allowing the gesture after disallowing it on a narrow screen', function() {
+ viewport.set(480);
+
+ window.initCore();
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.called).toBe(false);
+ expect(snapperStub.close.called).toBe(false);
+
+ OC.disallowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.disable.alwaysCalledWithExactly(true)).toBe(true);
+ expect(snapperStub.close.called).toBe(false);
+
+ OC.allowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.calledTwice).toBe(true);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.close.called).toBe(false);
+ });
+ it('is not enabled again when allowing the gesture twice after disallowing it on a narrow screen', function() {
+ viewport.set(480);
+
+ window.initCore();
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.called).toBe(false);
+ expect(snapperStub.close.called).toBe(false);
+
+ OC.disallowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.disable.alwaysCalledWithExactly(true)).toBe(true);
+ expect(snapperStub.close.called).toBe(false);
+
+ OC.allowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.calledTwice).toBe(true);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.close.called).toBe(false);
+
+ OC.allowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.calledTwice).toBe(true);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.close.called).toBe(false);
+ });
+ it('is disabled on a wide screen', function() {
+ viewport.set(1280);
+
+ window.initCore();
+
+ expect(snapConstructorStub.calledOnce).toBe(true);
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ });
+ it('is not disabled again when disallowing the gesture on a wide screen', function() {
+ viewport.set(1280);
+
+ window.initCore();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.close.calledOnce).toBe(true);
+
+ OC.disallowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.close.calledOnce).toBe(true);
+ });
+ it('is not enabled when allowing the gesture after disallowing it on a wide screen', function() {
+ viewport.set(1280);
+
+ window.initCore();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.close.calledOnce).toBe(true);
+
+ OC.disallowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.close.calledOnce).toBe(true);
+
+ OC.allowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.close.calledOnce).toBe(true);
+ });
+ it('is enabled when resizing to a narrow screen', function() {
+ viewport.set(1280);
+
+ window.initCore();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+
+ viewport.set(480);
+
+ // Setting the viewport width does not automatically trigger a
+ // resize.
+ $(window).resize();
+
+ // The resize handler is debounced to be executed a few milliseconds
+ // after the resize event.
+ clock.tick(1000);
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ });
+ it('is not enabled when resizing to a narrow screen after disallowing the gesture', function() {
+ viewport.set(1280);
+
+ window.initCore();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+
+ OC.disallowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+
+ viewport.set(480);
+
+ // Setting the viewport width does not automatically trigger a
+ // resize.
+ $(window).resize();
+
+ // The resize handler is debounced to be executed a few milliseconds
+ // after the resize event.
+ clock.tick(1000);
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ });
+ it('is enabled when resizing to a narrow screen after disallowing the gesture and allowing it', function() {
+ viewport.set(1280);
+
+ window.initCore();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+
+ OC.disallowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+
+ OC.allowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+
+ viewport.set(480);
+
+ // Setting the viewport width does not automatically trigger a
+ // resize.
+ $(window).resize();
+
+ // The resize handler is debounced to be executed a few milliseconds
+ // after the resize event.
+ clock.tick(1000);
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ });
+ it('is enabled when allowing the gesture after disallowing it and resizing to a narrow screen', function() {
+ viewport.set(1280);
+
+ window.initCore();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+
+ OC.disallowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+
+ viewport.set(480);
+
+ // Setting the viewport width does not automatically trigger a
+ // resize.
+ $(window).resize();
+
+ // The resize handler is debounced to be executed a few milliseconds
+ // after the resize event.
+ clock.tick(1000);
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+
+ OC.allowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ });
+ it('is disabled when disallowing the gesture after disallowing it, resizing to a narrow screen and allowing it', function() {
+ viewport.set(1280);
+
+ window.initCore();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+
+ OC.disallowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+
+ viewport.set(480);
+
+ // Setting the viewport width does not automatically trigger a
+ // resize.
+ $(window).resize();
+
+ // The resize handler is debounced to be executed a few milliseconds
+ // after the resize event.
+ clock.tick(1000);
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+
+ OC.allowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+
+ OC.disallowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.calledTwice).toBe(true);
+ expect(snapperStub.disable.getCall(1).calledWithExactly(true)).toBe(true);
+ });
+ it('is disabled when resizing to a wide screen', function() {
+ viewport.set(480);
+
+ window.initCore();
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.called).toBe(false);
+ expect(snapperStub.close.called).toBe(false);
+
+ viewport.set(1280);
+
+ // Setting the viewport width does not automatically trigger a
+ // resize.
+ $(window).resize();
+
+ // The resize handler is debounced to be executed a few milliseconds
+ // after the resize event.
+ clock.tick(1000);
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.close.calledOnce).toBe(true);
+ });
+ it('is not disabled again when disallowing the gesture after resizing to a wide screen', function() {
+ viewport.set(480);
+
+ window.initCore();
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.called).toBe(false);
+ expect(snapperStub.close.called).toBe(false);
+
+ viewport.set(1280);
+
+ // Setting the viewport width does not automatically trigger a
+ // resize.
+ $(window).resize();
+
+ // The resize handler is debounced to be executed a few milliseconds
+ // after the resize event.
+ clock.tick(1000);
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.close.calledOnce).toBe(true);
+
+ OC.disallowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.calledOnce).toBe(true);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.close.calledOnce).toBe(true);
+ });
+ it('is not enabled when allowing the gesture after disallowing it, resizing to a narrow screen and resizing to a wide screen', function() {
+ viewport.set(1280);
+
+ window.initCore();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.close.calledOnce).toBe(true);
+
+ OC.disallowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.close.calledOnce).toBe(true);
+
+ viewport.set(480);
+
+ // Setting the viewport width does not automatically trigger a
+ // resize.
+ $(window).resize();
+
+ // The resize handler is debounced to be executed a few milliseconds
+ // after the resize event.
+ clock.tick(1000);
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledOnce).toBe(true);
+ expect(snapperStub.close.calledOnce).toBe(true);
+
+ viewport.set(1280);
+
+ $(window).resize();
+
+ clock.tick(1000);
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledTwice).toBe(true);
+ expect(snapperStub.close.calledTwice).toBe(true);
+
+ OC.allowNavigationBarSlideGesture();
+
+ expect(snapperStub.enable.called).toBe(false);
+ expect(snapperStub.disable.calledTwice).toBe(true);
+ expect(snapperStub.close.calledTwice).toBe(true);
+ });
+ });
});
diff --git a/core/templates/login.php b/core/templates/login.php
index 82827bbef03..d28c92e36ef 100644
--- a/core/templates/login.php
+++ b/core/templates/login.php
@@ -70,6 +70,7 @@ script('core', 'merged-login');
<?php } ?>
<div class="login-additional">
+ <?php if (!$_['hideRemeberLoginState']) { ?>
<div class="remember-login-container">
<?php if ($_['rememberLoginState'] === 0) { ?>
<input type="checkbox" name="remember_login" value="1" id="remember_login" class="checkbox checkbox--white">
@@ -78,6 +79,7 @@ script('core', 'merged-login');
<?php } ?>
<label for="remember_login"><?php p($l->t('Stay logged in')); ?></label>
</div>
+ <?php } ?>
<?php if (!empty($_['canResetPassword'])) { ?>
<div class="lost-password-container">
<a id="lost-password" href="<?php p($_['resetPasswordLink']); ?>">
diff --git a/core/vendor/core.js b/core/vendor/core.js
index bda270892ad..e573ef10642 100644
--- a/core/vendor/core.js
+++ b/core/vendor/core.js
@@ -6704,9 +6704,13 @@ dav.Client.prototype = {
/**
* Disables Snap.js events
+ * @param {Boolean} endCurrentDrag Whether to end the current drag (if any) or not.
*/
- disable: function() {
+ disable: function(endCurrentDrag) {
utils.dispatchEvent('disable');
+ if (endCurrentDrag) {
+ this.action.drag.endDrag();
+ }
this.action.drag.stopListening();
},
diff --git a/core/vendor/snapjs/dist/latest/snap.js b/core/vendor/snapjs/dist/latest/snap.js
index a0274138de0..7ae088d0aea 100644
--- a/core/vendor/snapjs/dist/latest/snap.js
+++ b/core/vendor/snapjs/dist/latest/snap.js
@@ -744,9 +744,13 @@
/**
* Disables Snap.js events
+ * @param {Boolean} endCurrentDrag Whether to end the current drag (if any) or not.
*/
- disable: function() {
+ disable: function(endCurrentDrag) {
utils.dispatchEvent('disable');
+ if (endCurrentDrag) {
+ this.action.drag.endDrag();
+ }
this.action.drag.stopListening();
},
diff --git a/issue_template.md b/issue_template.md
index ecf2f62ea68..e614ea0aed0 100644
--- a/issue_template.md
+++ b/issue_template.md
@@ -7,6 +7,10 @@ For reporting potential security issues please see https://nextcloud.com/securit
To make it possible for us to help you please fill out below information carefully.
You can also use the Issue Template application to prefill most of the required information: https://apps.nextcloud.com/apps/issuetemplate
+
+If you are a customer, please submit your issue directly in the Nextcloud Portal https://portal.nextcloud.com so it gets resolved more quickly by our dedicated engineers.
+
+Note that Nextcloud is an open source project backed by Nextcloud GmbH. Most of our volunteers are home users and thus primarily care about issues that affect home users. Our paid engineers prioritize issues of our customers. If you are neither a home user nor a customer, consider paying somebody to fix your issue, do it yourself or become a customer.
-->
### Steps to reproduce
1.
diff --git a/lib/private/App/AppStore/Fetcher/Fetcher.php b/lib/private/App/AppStore/Fetcher/Fetcher.php
index 5ce64671ffa..8bf9ca15349 100644
--- a/lib/private/App/AppStore/Fetcher/Fetcher.php
+++ b/lib/private/App/AppStore/Fetcher/Fetcher.php
@@ -36,6 +36,7 @@ use OCP\Files\NotFoundException;
use OCP\Http\Client\IClientService;
use OCP\IConfig;
use OCP\ILogger;
+use OCP\Util;
abstract class Fetcher {
const INVALIDATE_AFTER_SECONDS = 300;
@@ -170,7 +171,7 @@ abstract class Fetcher {
$file->putContent(json_encode($responseJson));
return json_decode($file->getContent(), true)['data'];
} catch (ConnectException $e) {
- $this->logger->logException($e, ['app' => 'appstoreFetcher']);
+ $this->logger->logException($e, ['app' => 'appstoreFetcher', 'level' => Util::INFO, 'message' => 'Could not connect to appstore']);
return [];
} catch (\Exception $e) {
return [];
diff --git a/lib/private/Console/TimestampFormatter.php b/lib/private/Console/TimestampFormatter.php
index 66dd38e6ac3..9ced9e18b31 100644
--- a/lib/private/Console/TimestampFormatter.php
+++ b/lib/private/Console/TimestampFormatter.php
@@ -31,6 +31,9 @@ class TimestampFormatter implements OutputFormatterInterface {
/** @var IConfig */
protected $config;
+ /** @var OutputFormatterInterface */
+ protected $formatter;
+
/**
* @param IConfig $config
* @param OutputFormatterInterface $formatter
@@ -75,7 +78,7 @@ class TimestampFormatter implements OutputFormatterInterface {
* @return bool
*/
public function hasStyle($name) {
- $this->formatter->hasStyle($name);
+ return $this->formatter->hasStyle($name);
}
/**
@@ -83,6 +86,7 @@ class TimestampFormatter implements OutputFormatterInterface {
*
* @param string $name
* @return OutputFormatterStyleInterface
+ * @throws \InvalidArgumentException When style isn't defined
*/
public function getStyle($name) {
return $this->formatter->getStyle($name);
diff --git a/lib/private/Template/CSSResourceLocator.php b/lib/private/Template/CSSResourceLocator.php
index 3c30a9d3356..5ca05d1b953 100644
--- a/lib/private/Template/CSSResourceLocator.php
+++ b/lib/private/Template/CSSResourceLocator.php
@@ -108,7 +108,7 @@ class CSSResourceLocator extends ResourceLocator {
if($this->scssCacher !== null) {
if($this->scssCacher->process($root, $file, $app)) {
- $this->append($root, $this->scssCacher->getCachedSCSS($app, $file), false, true, true);
+ $this->append($root, $this->scssCacher->getCachedSCSS($app, $file), \OC::$WEBROOT, true, true);
return true;
} else {
$this->logger->warning('Failed to compile and/or save '.$root.'/'.$file, ['app' => 'core']);
@@ -145,7 +145,7 @@ class CSSResourceLocator extends ResourceLocator {
}
}
- $this->resources[] = array($webRoot? : '/', $webRoot, $file);
+ $this->resources[] = array($webRoot? : \OC::$WEBROOT, $webRoot, $file);
}
}
}
diff --git a/lib/private/Template/SCSSCacher.php b/lib/private/Template/SCSSCacher.php
index 8f6cb85a120..a4604425544 100644
--- a/lib/private/Template/SCSSCacher.php
+++ b/lib/private/Template/SCSSCacher.php
@@ -102,8 +102,7 @@ class SCSSCacher {
$fileNameCSS = $this->prependBaseurlPrefix(str_replace('.scss', '.css', $fileNameSCSS));
$path = implode('/', $path);
-
- $webDir = substr($path, strlen($this->serverRoot)+1);
+ $webDir = $this->getWebDir($path, $app, $this->serverRoot, \OC::$WEBROOT);
try {
$folder = $this->appData->getFolder($app);
@@ -188,7 +187,7 @@ class SCSSCacher {
$scss = new Compiler();
$scss->setImportPaths([
$path,
- \OC::$SERVERROOT . '/core/css/',
+ $this->serverRoot . '/core/css/',
]);
// Continue after throw
$scss->setIgnoreErrors(true);
@@ -283,12 +282,7 @@ class SCSSCacher {
*/
private function rebaseUrls($css, $webDir) {
$re = '/url\([\'"]([\.\w?=\/-]*)[\'"]\)/x';
- // OC\Route\Router:75
- if(($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true')) {
- $subst = 'url(\'../../'.$webDir.'/$1\')';
- } else {
- $subst = 'url(\'../../../'.$webDir.'/$1\')';
- }
+ $subst = 'url(\''.$webDir.'/$1\')';
return preg_replace($re, $subst, $css);
}
@@ -315,4 +309,23 @@ class SCSSCacher {
$frontendController = ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true');
return substr(md5($this->urlGenerator->getBaseUrl() . $frontendController), 0, 8) . '-' . $cssFile;
}
+
+ /**
+ * Get WebDir root
+ * @param string $path the css file path
+ * @param string $appName the app name
+ * @param string $serverRoot the server root path
+ * @param string $webRoot the nextcloud installation root path
+ * @return string the webDir
+ */
+ private function getWebDir($path, $appName, $serverRoot, $webRoot) {
+ // Detect if path is within server root AND if path is within an app path
+ if ( strpos($path, $serverRoot) === false && $appWebPath = \OC_App::getAppWebPath($appName)) {
+ // Get the file path within the app directory
+ $appDirectoryPath = explode($appName, $path)[1];
+ // Remove the webroot
+ return str_replace($webRoot, '', $appWebPath.$appDirectoryPath);
+ }
+ return $webRoot.substr($path, strlen($serverRoot));
+ }
}
diff --git a/settings/Activity/SecurityProvider.php b/settings/Activity/SecurityProvider.php
index f0789842e82..680881b6e31 100644
--- a/settings/Activity/SecurityProvider.php
+++ b/settings/Activity/SecurityProvider.php
@@ -53,7 +53,7 @@ class SecurityProvider implements IProvider {
throw new InvalidArgumentException();
}
- $l = $this->l10n->get('core', $language);
+ $l = $this->l10n->get('settings', $language);
switch ($event->getSubject()) {
case 'twofactor_success':
diff --git a/settings/js/apps.js b/settings/js/apps.js
index 6406e37cbcb..0a6e86ed701 100644
--- a/settings/js/apps.js
+++ b/settings/js/apps.js
@@ -535,20 +535,20 @@ OC.Settings.Apps = OC.Settings.Apps || {
showEmptyUpdates: function() {
$('#apps-list').addClass('hidden');
- $('#apps-list-empty').removeClass('hidden').find('h2').text(t('settings', 'No app updates available'));
+ $('#apps-list-empty').removeClass('hidden').find('h2').text(t('settings', 'App up to date'));
$('#app-list-empty-icon').removeClass('icon-search').addClass('icon-download');
},
updateApp:function(appId, element) {
var oldButtonText = element.val();
- element.val(t('settings','Updating....'));
+ element.val(t('settings','Upgrading …'));
OC.Settings.Apps.hideErrorMessage(appId);
$.post(OC.filePath('settings','ajax','updateapp.php'),{appid:appId},function(result) {
if(!result || result.status !== 'success') {
if (result.data && result.data.message) {
OC.Settings.Apps.showErrorMessage(appId, result.data.message);
} else {
- OC.Settings.Apps.showErrorMessage(appId, t('settings','Error while updating app'));
+ OC.Settings.Apps.showErrorMessage(appId, t('settings','Could not upgrade app'));
}
element.val(oldButtonText);
}
@@ -584,7 +584,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
element.val(t('settings','Removing …'));
$.post(OC.filePath('settings','ajax','uninstallapp.php'),{appid:appId},function(result) {
if(!result || result.status !== 'success') {
- OC.Settings.Apps.showErrorMessage(appId, t('settings','Error while removing app'));
+ OC.Settings.Apps.showErrorMessage(appId, t('settings','Could not remove app'));
element.val(t('settings','Remove'));
} else {
OC.Settings.Apps.rebuildNavigation();
@@ -722,9 +722,9 @@ OC.Settings.Apps = OC.Settings.Apps || {
OC.dialogs.info(
t(
'settings',
- 'The app has been enabled but needs to be updated. You will be redirected to the update page in 5 seconds.'
+ 'The app has been enabled but needs to be upgraded. You will be redirected to the upgrade page in 5 seconds.'
),
- t('settings','App update'),
+ t('settings','App upgrade'),
function () {
window.location.reload();
},
diff --git a/tests/Core/Controller/LoginControllerTest.php b/tests/Core/Controller/LoginControllerTest.php
index e02b8403a2a..ddf7a865d66 100644
--- a/tests/Core/Controller/LoginControllerTest.php
+++ b/tests/Core/Controller/LoginControllerTest.php
@@ -182,12 +182,43 @@ class LoginControllerTest extends TestCase {
'alt_login' => [],
'rememberLoginState' => 0,
'resetPasswordLink' => null,
+ 'hideRemeberLoginState' => false,
],
'guest'
);
$this->assertEquals($expectedResponse, $this->loginController->showLoginForm('', '', ''));
}
+ public function testShowLoginFormForFlowAuth() {
+ $this->userSession
+ ->expects($this->once())
+ ->method('isLoggedIn')
+ ->willReturn(false);
+ $this->session
+ ->expects($this->once())
+ ->method('exists')
+ ->with('client.flow.state.token')
+ ->willReturn(true);
+
+ $expectedResponse = new TemplateResponse(
+ 'core',
+ 'login',
+ [
+ 'messages' => [],
+ 'redirect_url' => 'login/flow',
+ 'loginName' => '',
+ 'user_autofocus' => true,
+ 'canResetPassword' => true,
+ 'alt_login' => [],
+ 'rememberLoginState' => 0,
+ 'resetPasswordLink' => null,
+ 'hideRemeberLoginState' => true,
+ ],
+ 'guest'
+ );
+ $this->assertEquals($expectedResponse, $this->loginController->showLoginForm('', 'login/flow', ''));
+ }
+
/**
* @return array
*/
@@ -240,6 +271,7 @@ class LoginControllerTest extends TestCase {
'alt_login' => [],
'rememberLoginState' => 0,
'resetPasswordLink' => false,
+ 'hideRemeberLoginState' => false,
],
'guest'
);
@@ -278,6 +310,7 @@ class LoginControllerTest extends TestCase {
'alt_login' => [],
'rememberLoginState' => 0,
'resetPasswordLink' => false,
+ 'hideRemeberLoginState' => false,
],
'guest'
);
diff --git a/tests/Settings/Activity/SecurityProviderTest.php b/tests/Settings/Activity/SecurityProviderTest.php
index 21fc28f3c3b..552548984d7 100644
--- a/tests/Settings/Activity/SecurityProviderTest.php
+++ b/tests/Settings/Activity/SecurityProviderTest.php
@@ -87,7 +87,7 @@ class SecurityProviderTest extends TestCase {
->willReturn('security');
$this->l10n->expects($this->once())
->method('get')
- ->with('core', $lang)
+ ->with('settings', $lang)
->willReturn($l);
$this->urlGenerator->expects($this->once())
->method('imagePath')
@@ -119,7 +119,7 @@ class SecurityProviderTest extends TestCase {
->willReturn('security');
$this->l10n->expects($this->once())
->method('get')
- ->with('core', $lang)
+ ->with('settings', $lang)
->willReturn($l);
$event->expects($this->once())
->method('getSubject')
diff --git a/tests/karma.config.js b/tests/karma.config.js
index fb613857e91..0254d6a3335 100644
--- a/tests/karma.config.js
+++ b/tests/karma.config.js
@@ -237,7 +237,7 @@ module.exports = function(config) {
basePath: '..',
// frameworks to use
- frameworks: ['jasmine', 'jasmine-sinon'],
+ frameworks: ['jasmine', 'jasmine-sinon', 'viewport'],
// list of files / patterns to load in the browser
files: files,
diff --git a/tests/lib/LegacyHelperTest.php b/tests/lib/LegacyHelperTest.php
index f1e22ea600e..736c5bf7fad 100644
--- a/tests/lib/LegacyHelperTest.php
+++ b/tests/lib/LegacyHelperTest.php
@@ -12,6 +12,17 @@ use OC\Files\View;
use OC_Helper;
class LegacyHelperTest extends \Test\TestCase {
+ /** @var string */
+ private $originalWebRoot;
+
+ public function setUp() {
+ $this->originalWebRoot = \OC::$WEBROOT;
+ }
+
+ public function tearDown() {
+ // Reset webRoot
+ \OC::$WEBROOT = $this->originalWebRoot;
+ }
/**
* @dataProvider humanFileSizeProvider
diff --git a/tests/lib/Template/SCSSCacherTest.php b/tests/lib/Template/SCSSCacherTest.php
index 3825bc44c59..fca9500810e 100644
--- a/tests/lib/Template/SCSSCacherTest.php
+++ b/tests/lib/Template/SCSSCacherTest.php
@@ -352,19 +352,10 @@ class SCSSCacherTest extends \Test\TestCase {
}
public function testRebaseUrls() {
- $webDir = 'apps/files/css';
+ $webDir = '/apps/files/css';
$css = '#id { background-image: url(\'../img/image.jpg\'); }';
$actual = self::invokePrivate($this->scssCacher, 'rebaseUrls', [$css, $webDir]);
- $expected = '#id { background-image: url(\'../../../apps/files/css/../img/image.jpg\'); }';
- $this->assertEquals($expected, $actual);
- }
-
- public function testRebaseUrlsIgnoreFrontendController() {
- $this->config->expects($this->once())->method('getSystemValue')->with('htaccess.IgnoreFrontController', false)->willReturn(true);
- $webDir = 'apps/files/css';
- $css = '#id { background-image: url(\'../img/image.jpg\'); }';
- $actual = self::invokePrivate($this->scssCacher, 'rebaseUrls', [$css, $webDir]);
- $expected = '#id { background-image: url(\'../../apps/files/css/../img/image.jpg\'); }';
+ $expected = '#id { background-image: url(\'/apps/files/css/../img/image.jpg\'); }';
$this->assertEquals($expected, $actual);
}
@@ -393,4 +384,55 @@ class SCSSCacherTest extends \Test\TestCase {
$this->assertEquals(substr($result, 1), $actual);
}
+ private function randomString() {
+ return sha1(uniqid(mt_rand(), true));
+ }
+
+ private function rrmdir($directory) {
+ $files = array_diff(scandir($directory), array('.','..'));
+ foreach ($files as $file) {
+ if (is_dir($directory . '/' . $file)) {
+ $this->rrmdir($directory . '/' . $file);
+ } else {
+ unlink($directory . '/' . $file);
+ }
+ }
+ return rmdir($directory);
+ }
+
+ public function dataGetWebDir() {
+ return [
+ // Root installation
+ ['/http/core/css', 'core', '', '/http', '/core/css'],
+ ['/http/apps/scss/css', 'scss', '', '/http', '/apps/scss/css'],
+ ['/srv/apps2/scss/css', 'scss', '', '/http', '/apps2/scss/css'],
+ // Sub directory install
+ ['/http/nextcloud/core/css', 'core', '/nextcloud', '/http/nextcloud', '/nextcloud/core/css'],
+ ['/http/nextcloud/apps/scss/css', 'scss', '/nextcloud', '/http/nextcloud', '/nextcloud/apps/scss/css'],
+ ['/srv/apps2/scss/css', 'scss', '/nextcloud', '/http/nextcloud', '/apps2/scss/css']
+ ];
+ }
+
+ /**
+ * @param $path
+ * @param $appName
+ * @param $webRoot
+ * @param $serverRoot
+ * @dataProvider dataGetWebDir
+ */
+ public function testgetWebDir($path, $appName, $webRoot, $serverRoot, $correctWebDir) {
+ $tmpDir = sys_get_temp_dir().'/'.$this->randomString();
+ // Adding fake apps folder and create fake app install
+ \OC::$APPSROOTS[] = [
+ 'path' => $tmpDir.'/srv/apps2',
+ 'url' => '/apps2',
+ 'writable' => false
+ ];
+ mkdir($tmpDir.$path, 0777, true);
+ $actual = self::invokePrivate($this->scssCacher, 'getWebDir', [$tmpDir.$path, $appName, $tmpDir.$serverRoot, $webRoot]);
+ $this->assertEquals($correctWebDir, $actual);
+ array_pop(\OC::$APPSROOTS);
+ $this->rrmdir($tmpDir.$path);
+ }
+
}
diff --git a/tests/lib/UrlGeneratorTest.php b/tests/lib/UrlGeneratorTest.php
index 69067f51e08..340c9c7082d 100644
--- a/tests/lib/UrlGeneratorTest.php
+++ b/tests/lib/UrlGeneratorTest.php
@@ -27,6 +27,8 @@ class UrlGeneratorTest extends \Test\TestCase {
private $request;
/** @var IURLGenerator */
private $urlGenerator;
+ /** @var string */
+ private $originalWebRoot;
public function setUp() {
parent::setUp();
@@ -38,6 +40,12 @@ class UrlGeneratorTest extends \Test\TestCase {
$this->cacheFactory,
$this->request
);
+ $this->originalWebRoot = \OC::$WEBROOT;
+ }
+
+ public function tearDown() {
+ // Reset webRoot
+ \OC::$WEBROOT = $this->originalWebRoot;
}
private function mockBaseUrl() {
@@ -47,7 +55,6 @@ class UrlGeneratorTest extends \Test\TestCase {
$this->request->expects($this->once())
->method('getServerHost')
->willReturn('localhost');
-
}
/**
@@ -156,4 +163,3 @@ class UrlGeneratorTest extends \Test\TestCase {
}
}
-