summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CODE_OF_CONDUCT.md9
-rw-r--r--apps/files/js/mainfileinfodetailview.js9
-rw-r--r--apps/files/l10n/es.js3
-rw-r--r--apps/files/l10n/es.json3
-rw-r--r--apps/files/l10n/nb.js2
-rw-r--r--apps/files/l10n/nb.json2
-rw-r--r--apps/files/l10n/ru.js3
-rw-r--r--apps/files/l10n/ru.json3
-rw-r--r--apps/files/l10n/tr.js3
-rw-r--r--apps/files/l10n/tr.json3
-rw-r--r--apps/files/lib/AppInfo/Application.php5
-rw-r--r--apps/files/lib/Service/TagService.php16
-rw-r--r--apps/files/tests/Service/TagServiceTest.php8
-rw-r--r--apps/files/tests/js/mainfileinfodetailviewSpec.js29
-rw-r--r--apps/files_external/l10n/de.js1
-rw-r--r--apps/files_external/l10n/de.json1
-rw-r--r--apps/files_external/l10n/de_DE.js1
-rw-r--r--apps/files_external/l10n/de_DE.json1
-rw-r--r--apps/files_external/l10n/es.js1
-rw-r--r--apps/files_external/l10n/es.json1
-rw-r--r--apps/files_external/l10n/it.js1
-rw-r--r--apps/files_external/l10n/it.json1
-rw-r--r--apps/files_external/l10n/tr.js1
-rw-r--r--apps/files_external/l10n/tr.json1
-rw-r--r--apps/theming/css/theming.scss28
-rw-r--r--apps/theming/lib/Controller/ThemingController.php2
-rw-r--r--apps/theming/lib/Util.php35
-rw-r--r--apps/theming/tests/UtilTest.php20
-rw-r--r--apps/user_ldap/composer/composer/autoload_classmap.php2
-rw-r--r--apps/user_ldap/composer/composer/autoload_static.php2
-rw-r--r--apps/user_ldap/lib/AccessFactory.php61
-rw-r--r--apps/user_ldap/lib/ConnectionFactory.php38
-rw-r--r--apps/user_ldap/lib/Jobs/Sync.php37
-rw-r--r--apps/user_ldap/tests/Jobs/SyncTest.php225
-rw-r--r--build/files-checker.php1
-rw-r--r--config/config.sample.php17
-rw-r--r--core/css/apps.scss1
-rw-r--r--core/css/fixes.scss3
-rw-r--r--core/css/styles.scss2
-rw-r--r--core/css/tooltip.scss2
-rw-r--r--core/js/jquery.avatar.js5
-rw-r--r--core/js/placeholder.js1
-rw-r--r--core/js/public/comments.js2
-rw-r--r--core/js/tests/specs/jquery.avatarSpec.js2
-rw-r--r--core/templates/layout.user.php14
-rw-r--r--lib/private/Preview/Generator.php56
-rw-r--r--lib/private/Settings/Manager.php3
-rw-r--r--lib/private/Share20/DefaultShareProvider.php6
-rw-r--r--lib/private/legacy/image.php19
-rw-r--r--lib/public/IImage.php6
-rw-r--r--ocs/v1.php4
-rw-r--r--settings/l10n/es.js6
-rw-r--r--settings/l10n/es.json6
-rw-r--r--settings/l10n/nb.js3
-rw-r--r--settings/l10n/nb.json3
-rw-r--r--settings/l10n/ru.js6
-rw-r--r--settings/l10n/ru.json6
-rw-r--r--settings/l10n/tr.js6
-rw-r--r--settings/l10n/tr.json6
-rw-r--r--tests/lib/Preview/GeneratorTest.php8
60 files changed, 674 insertions, 78 deletions
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000000..d9060072e46
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,9 @@
+In the Nextcloud community, participants from all over the world come together to create Free Software for a free internet. This is made possible by the support, hard work and enthusiasm of thousands of people, including those who create and use Nextcloud software.
+
+Our code of conduct offers some guidance to ensure Nextcloud participants can cooperate effectively in a positive and inspiring atmosphere, and to explain how together we can strengthen and support each other.
+
+The Code of Conduct is shared by all contributors and users who engage with the Nextcloud team and its community services. It presents a summary of the shared values and “common sense” thinking in our community.
+
+You can find our full code of conduct on our website: https://nextcloud.com/code-of-conduct/
+
+Please, keep our CoC in mind when you contribute! That way, everyone can be a part of our community in a productive, positive, creative and fun way.
diff --git a/apps/files/js/mainfileinfodetailview.js b/apps/files/js/mainfileinfodetailview.js
index ea3063f6176..626ab86ded3 100644
--- a/apps/files/js/mainfileinfodetailview.js
+++ b/apps/files/js/mainfileinfodetailview.js
@@ -20,9 +20,11 @@
'</a>' +
'</div>' +
' <div class="file-details ellipsis">' +
+ ' {{#if hasFavoriteAction}}' +
' <a href="#" class="action action-favorite favorite permanent">' +
' <span class="icon {{starClass}}" title="{{starAltText}}"></span>' +
' </a>' +
+ ' {{/if}}' +
' {{#if hasSize}}<span class="size" title="{{altSize}}">{{size}}</span>, {{/if}}<span class="date live-relative-timestamp" data-timestamp="{{timestamp}}" title="{{altDate}}">{{date}}</span>' +
' </div>' +
'</div>' +
@@ -175,6 +177,12 @@
if (this.model) {
var isFavorite = (this.model.get('tags') || []).indexOf(OC.TAG_FAVORITE) >= 0;
+ var availableActions = this._fileActions.get(
+ this.model.get('mimetype'),
+ this.model.get('type'),
+ this.model.get('permissions')
+ );
+ var hasFavoriteAction = 'Favorite' in availableActions;
this.$el.html(this.template({
type: this.model.isImage()? 'image': '',
nameLabel: t('files', 'Name'),
@@ -189,6 +197,7 @@
altDate: OC.Util.formatDate(this.model.get('mtime')),
timestamp: this.model.get('mtime'),
date: OC.Util.relativeModifiedDate(this.model.get('mtime')),
+ hasFavoriteAction: hasFavoriteAction,
starAltText: isFavorite ? t('files', 'Favorited') : t('files', 'Favorite'),
starClass: isFavorite ? 'icon-starred' : 'icon-star',
permalink: this._makePermalink(this.model.get('id')),
diff --git a/apps/files/l10n/es.js b/apps/files/l10n/es.js
index a4666bd7bdf..0852da86efc 100644
--- a/apps/files/l10n/es.js
+++ b/apps/files/l10n/es.js
@@ -62,8 +62,11 @@ OC.L10N.register(
"You don’t have permission to upload or create files here" : "No tiene permisos para subir o crear archivos aquí",
"_Uploading %n file_::_Uploading %n files_" : ["Subiendo %n archivo","Subiendo %n archivos"],
"New" : "Nuevo",
+ "{used} of {quota} used" : "{used} usados de {quota}",
+ "{used} used" : "{used} usados",
"\"{name}\" is an invalid file name." : "\"{name}\" es un nombre de archivo inválido.",
"File name cannot be empty." : "El nombre de archivo no puede estar vacío.",
+ "\"/\" is not allowed inside a file name." : "\"/\" no se permite dentro de un nombre de archivo.",
"\"{name}\" is not an allowed filetype" : "\"{name}\" no es un tipo de archivo permitido",
"Storage of {owner} is full, files can not be updated or synced anymore!" : "El almacén de {owner} está repleto, ¡los archivos no se actualizarán ni sincronizarán más!",
"Your storage is full, files can not be updated or synced anymore!" : "Su almacenamiento está lleno, ¡los archivos no se actualizarán ni sincronizarán más!",
diff --git a/apps/files/l10n/es.json b/apps/files/l10n/es.json
index 44db92002f2..db8baad60b3 100644
--- a/apps/files/l10n/es.json
+++ b/apps/files/l10n/es.json
@@ -60,8 +60,11 @@
"You don’t have permission to upload or create files here" : "No tiene permisos para subir o crear archivos aquí",
"_Uploading %n file_::_Uploading %n files_" : ["Subiendo %n archivo","Subiendo %n archivos"],
"New" : "Nuevo",
+ "{used} of {quota} used" : "{used} usados de {quota}",
+ "{used} used" : "{used} usados",
"\"{name}\" is an invalid file name." : "\"{name}\" es un nombre de archivo inválido.",
"File name cannot be empty." : "El nombre de archivo no puede estar vacío.",
+ "\"/\" is not allowed inside a file name." : "\"/\" no se permite dentro de un nombre de archivo.",
"\"{name}\" is not an allowed filetype" : "\"{name}\" no es un tipo de archivo permitido",
"Storage of {owner} is full, files can not be updated or synced anymore!" : "El almacén de {owner} está repleto, ¡los archivos no se actualizarán ni sincronizarán más!",
"Your storage is full, files can not be updated or synced anymore!" : "Su almacenamiento está lleno, ¡los archivos no se actualizarán ni sincronizarán más!",
diff --git a/apps/files/l10n/nb.js b/apps/files/l10n/nb.js
index b9e3ee0a1b8..45b9027f782 100644
--- a/apps/files/l10n/nb.js
+++ b/apps/files/l10n/nb.js
@@ -62,6 +62,8 @@ OC.L10N.register(
"You don’t have permission to upload or create files here" : "Du har ikke tillatelse til å laste opp eller opprette filer her",
"_Uploading %n file_::_Uploading %n files_" : ["Laster opp %n fil","Laster opp %n filer"],
"New" : "Ny",
+ "{used} of {quota} used" : "{used} av {quota} brukt",
+ "{used} used" : "{used} brukt",
"\"{name}\" is an invalid file name." : "\"{name}\" er et uglydig filnavn.",
"File name cannot be empty." : "Filnavn kan ikke være tomt.",
"\"{name}\" is not an allowed filetype" : "\"{name}\" er ikke en tillatt filtype",
diff --git a/apps/files/l10n/nb.json b/apps/files/l10n/nb.json
index 4c4e0622362..0328ceb283a 100644
--- a/apps/files/l10n/nb.json
+++ b/apps/files/l10n/nb.json
@@ -60,6 +60,8 @@
"You don’t have permission to upload or create files here" : "Du har ikke tillatelse til å laste opp eller opprette filer her",
"_Uploading %n file_::_Uploading %n files_" : ["Laster opp %n fil","Laster opp %n filer"],
"New" : "Ny",
+ "{used} of {quota} used" : "{used} av {quota} brukt",
+ "{used} used" : "{used} brukt",
"\"{name}\" is an invalid file name." : "\"{name}\" er et uglydig filnavn.",
"File name cannot be empty." : "Filnavn kan ikke være tomt.",
"\"{name}\" is not an allowed filetype" : "\"{name}\" er ikke en tillatt filtype",
diff --git a/apps/files/l10n/ru.js b/apps/files/l10n/ru.js
index b6222725a72..9878e2f78dd 100644
--- a/apps/files/l10n/ru.js
+++ b/apps/files/l10n/ru.js
@@ -62,8 +62,11 @@ OC.L10N.register(
"You don’t have permission to upload or create files here" : "У вас нет разрешений на создание или загрузку файлов в эту папку.",
"_Uploading %n file_::_Uploading %n files_" : ["Выгружа%nется файл","Выгружаются %n файла","Выгружаются %n файлов","Загружаются %n файлов"],
"New" : "Новый",
+ "{used} of {quota} used" : "использовано {used} из {quota}",
+ "{used} used" : "использовано {used}",
"\"{name}\" is an invalid file name." : "«{name}» — недопустимое имя файла.",
"File name cannot be empty." : "Имя файла не может быть пустым.",
+ "\"/\" is not allowed inside a file name." : "Символ «/» недопустим в имени файла.",
"\"{name}\" is not an allowed filetype" : "«{name}» - недопустимый тип файла.",
"Storage of {owner} is full, files can not be updated or synced anymore!" : "Хранилище {owner} переполнено, файлы больше не могут быть обновлены или синхронизированы!",
"Your storage is full, files can not be updated or synced anymore!" : "Ваше хранилище переполнено, файлы больше не могут быть обновлены или синхронизированы!",
diff --git a/apps/files/l10n/ru.json b/apps/files/l10n/ru.json
index e989cdd3183..be07497fda4 100644
--- a/apps/files/l10n/ru.json
+++ b/apps/files/l10n/ru.json
@@ -60,8 +60,11 @@
"You don’t have permission to upload or create files here" : "У вас нет разрешений на создание или загрузку файлов в эту папку.",
"_Uploading %n file_::_Uploading %n files_" : ["Выгружа%nется файл","Выгружаются %n файла","Выгружаются %n файлов","Загружаются %n файлов"],
"New" : "Новый",
+ "{used} of {quota} used" : "использовано {used} из {quota}",
+ "{used} used" : "использовано {used}",
"\"{name}\" is an invalid file name." : "«{name}» — недопустимое имя файла.",
"File name cannot be empty." : "Имя файла не может быть пустым.",
+ "\"/\" is not allowed inside a file name." : "Символ «/» недопустим в имени файла.",
"\"{name}\" is not an allowed filetype" : "«{name}» - недопустимый тип файла.",
"Storage of {owner} is full, files can not be updated or synced anymore!" : "Хранилище {owner} переполнено, файлы больше не могут быть обновлены или синхронизированы!",
"Your storage is full, files can not be updated or synced anymore!" : "Ваше хранилище переполнено, файлы больше не могут быть обновлены или синхронизированы!",
diff --git a/apps/files/l10n/tr.js b/apps/files/l10n/tr.js
index 6d68c8889bc..e1252d20b18 100644
--- a/apps/files/l10n/tr.js
+++ b/apps/files/l10n/tr.js
@@ -62,8 +62,11 @@ OC.L10N.register(
"You don’t have permission to upload or create files here" : "Buraya dosya yükleme veya ekleme izniniz yok",
"_Uploading %n file_::_Uploading %n files_" : ["%n dosya yükleniyor","%n dosya yükleniyor"],
"New" : "Yeni",
+ "{used} of {quota} used" : "{used} / {quota} kullanılmış",
+ "{used} used" : "{used} kullanılmış",
"\"{name}\" is an invalid file name." : "\"{name}\" geçersiz bir dosya adı.",
"File name cannot be empty." : "Dosya adı boş olamaz.",
+ "\"/\" is not allowed inside a file name." : "Dosya adında \"/\" kullanılamaz.",
"\"{name}\" is not an allowed filetype" : "\"{name}\" dosya türüne izin verilmiyor",
"Storage of {owner} is full, files can not be updated or synced anymore!" : "{owner} için boş depolama alanı kalmadı. Artık dosyalar güncellenmeyecek ya da eşitlenmeyecek!",
"Your storage is full, files can not be updated or synced anymore!" : "Boş depolama alanınız kalmadı. Artık dosyalar güncellenmeyecek ya da eşitlenmeyecek!",
diff --git a/apps/files/l10n/tr.json b/apps/files/l10n/tr.json
index cde68e5b35b..ca8bfbf8028 100644
--- a/apps/files/l10n/tr.json
+++ b/apps/files/l10n/tr.json
@@ -60,8 +60,11 @@
"You don’t have permission to upload or create files here" : "Buraya dosya yükleme veya ekleme izniniz yok",
"_Uploading %n file_::_Uploading %n files_" : ["%n dosya yükleniyor","%n dosya yükleniyor"],
"New" : "Yeni",
+ "{used} of {quota} used" : "{used} / {quota} kullanılmış",
+ "{used} used" : "{used} kullanılmış",
"\"{name}\" is an invalid file name." : "\"{name}\" geçersiz bir dosya adı.",
"File name cannot be empty." : "Dosya adı boş olamaz.",
+ "\"/\" is not allowed inside a file name." : "Dosya adında \"/\" kullanılamaz.",
"\"{name}\" is not an allowed filetype" : "\"{name}\" dosya türüne izin verilmiyor",
"Storage of {owner} is full, files can not be updated or synced anymore!" : "{owner} için boş depolama alanı kalmadı. Artık dosyalar güncellenmeyecek ya da eşitlenmeyecek!",
"Your storage is full, files can not be updated or synced anymore!" : "Boş depolama alanınız kalmadı. Artık dosyalar güncellenmeyecek ya da eşitlenmeyecek!",
diff --git a/apps/files/lib/AppInfo/Application.php b/apps/files/lib/AppInfo/Application.php
index 7042af10ca2..e55d1c549a5 100644
--- a/apps/files/lib/AppInfo/Application.php
+++ b/apps/files/lib/AppInfo/Application.php
@@ -81,13 +81,14 @@ class Application extends App {
$container->registerService('Tagger', function(IContainer $c) {
return $c->query('ServerContainer')->getTagManager()->load('files');
});
- $container->registerService('TagService', function(IContainer $c) {
+ $container->registerService('TagService', function(IContainer $c) use ($server) {
$homeFolder = $c->query('ServerContainer')->getUserFolder();
return new TagService(
$c->query('ServerContainer')->getUserSession(),
$c->query('ServerContainer')->getActivityManager(),
$c->query('Tagger'),
- $homeFolder
+ $homeFolder,
+ $server->getEventDispatcher()
);
});
diff --git a/apps/files/lib/Service/TagService.php b/apps/files/lib/Service/TagService.php
index d812b16c30e..7437f0c31ad 100644
--- a/apps/files/lib/Service/TagService.php
+++ b/apps/files/lib/Service/TagService.php
@@ -31,6 +31,8 @@ use OCP\Files\Folder;
use OCP\ITags;
use OCP\IUser;
use OCP\IUserSession;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\EventDispatcher\GenericEvent;
/**
* Service class to manage tags on files.
@@ -45,23 +47,28 @@ class TagService {
private $tagger;
/** @var Folder */
private $homeFolder;
+ /** @var EventDispatcherInterface */
+ private $dispatcher;
/**
* @param IUserSession $userSession
* @param IManager $activityManager
* @param ITags $tagger
* @param Folder $homeFolder
+ * @param EventDispatcherInterface $dispatcher
*/
public function __construct(
IUserSession $userSession,
IManager $activityManager,
ITags $tagger,
- Folder $homeFolder
+ Folder $homeFolder,
+ EventDispatcherInterface $dispatcher
) {
$this->userSession = $userSession;
$this->activityManager = $activityManager;
$this->tagger = $tagger;
$this->homeFolder = $homeFolder;
+ $this->dispatcher = $dispatcher;
}
/**
@@ -114,6 +121,13 @@ class TagService {
return;
}
+ $eventName = $addToFavorite ? 'addFavorite' : 'removeFavorite';
+ $this->dispatcher->dispatch(self::class . '::' . $eventName, new GenericEvent(null, [
+ 'userId' => $user->getUID(),
+ 'fileId' => $fileId,
+ 'path' => $path,
+ ]));
+
$event = $this->activityManager->generateEvent();
try {
$event->setApp('files')
diff --git a/apps/files/tests/Service/TagServiceTest.php b/apps/files/tests/Service/TagServiceTest.php
index 1c4ac2328ec..4e2aeb84246 100644
--- a/apps/files/tests/Service/TagServiceTest.php
+++ b/apps/files/tests/Service/TagServiceTest.php
@@ -28,6 +28,7 @@ use OC\Tags;
use OCA\Files\Service\TagService;
use OCP\Activity\IManager;
use OCP\IUserSession;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Class TagServiceTest
@@ -54,6 +55,9 @@ class TagServiceTest extends \Test\TestCase {
*/
private $root;
+ /** @var EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject */
+ private $dispatcher;
+
/**
* @var \OCA\Files\Service\TagService|\PHPUnit_Framework_MockObject_MockObject
*/
@@ -66,7 +70,7 @@ class TagServiceTest extends \Test\TestCase {
protected function setUp() {
parent::setUp();
- $this->user = $this->getUniqueID('user');
+ $this->user = static::getUniqueID('user');
$this->activityManager = $this->createMock(IManager::class);
\OC::$server->getUserManager()->createUser($this->user, 'test');
\OC_User::setUserId($this->user);
@@ -83,6 +87,7 @@ class TagServiceTest extends \Test\TestCase {
->will($this->returnValue($user));
$this->root = \OC::$server->getUserFolder();
+ $this->dispatcher = $this->createMock(EventDispatcherInterface::class);
$this->tagger = \OC::$server->getTagManager()->load('files');
$this->tagService = $this->getTagService(['addActivity']);
@@ -99,6 +104,7 @@ class TagServiceTest extends \Test\TestCase {
$this->activityManager,
$this->tagger,
$this->root,
+ $this->dispatcher,
])
->setMethods($methods)
->getMock();
diff --git a/apps/files/tests/js/mainfileinfodetailviewSpec.js b/apps/files/tests/js/mainfileinfodetailviewSpec.js
index faf0faa8d8f..0201429a472 100644
--- a/apps/files/tests/js/mainfileinfodetailviewSpec.js
+++ b/apps/files/tests/js/mainfileinfodetailviewSpec.js
@@ -68,6 +68,12 @@ describe('OCA.Files.MainFileInfoDetailView tests', function() {
.toEqual(OC.getProtocol() + '://' + OC.getHost() + OC.generateUrl('/f/5'));
});
it('displays favorite icon', function() {
+ fileActions.registerAction({
+ name: 'Favorite',
+ mime: 'all',
+ permissions: OC.PERMISSION_NONE
+ });
+
testFileInfo.set('tags', [OC.TAG_FAVORITE]);
view.setFileInfo(testFileInfo);
expect(view.$el.find('.action-favorite > span').hasClass('icon-starred')).toEqual(true);
@@ -78,6 +84,15 @@ describe('OCA.Files.MainFileInfoDetailView tests', function() {
expect(view.$el.find('.action-favorite > span').hasClass('icon-starred')).toEqual(false);
expect(view.$el.find('.action-favorite > span').hasClass('icon-star')).toEqual(true);
});
+ it('does not display favorite icon if favorite action is not available', function() {
+ testFileInfo.set('tags', [OC.TAG_FAVORITE]);
+ view.setFileInfo(testFileInfo);
+ expect(view.$el.find('.action-favorite').length).toEqual(0);
+
+ testFileInfo.set('tags', []);
+ view.setFileInfo(testFileInfo);
+ expect(view.$el.find('.action-favorite').length).toEqual(0);
+ });
it('displays mime icon', function() {
// File
var lazyLoadPreviewStub = sinon.stub(fileList, 'lazyLoadPreview');
@@ -183,6 +198,13 @@ describe('OCA.Files.MainFileInfoDetailView tests', function() {
expect(view.$el.find('.fileName h3').attr('title')).toEqual('hello.txt');
});
it('rerenders when changes are made on the model', function() {
+ // Show the "Favorite" icon
+ fileActions.registerAction({
+ name: 'Favorite',
+ mime: 'all',
+ permissions: OC.PERMISSION_NONE
+ });
+
view.setFileInfo(testFileInfo);
testFileInfo.set('tags', [OC.TAG_FAVORITE]);
@@ -196,6 +218,13 @@ describe('OCA.Files.MainFileInfoDetailView tests', function() {
expect(view.$el.find('.action-favorite > span').hasClass('icon-star')).toEqual(true);
});
it('unbinds change listener from model', function() {
+ // Show the "Favorite" icon
+ fileActions.registerAction({
+ name: 'Favorite',
+ mime: 'all',
+ permissions: OC.PERMISSION_NONE
+ });
+
view.setFileInfo(testFileInfo);
view.setFileInfo(new OCA.Files.FileInfoModel({
id: 999,
diff --git a/apps/files_external/l10n/de.js b/apps/files_external/l10n/de.js
index 40126676f0a..eb8fbdf0d8c 100644
--- a/apps/files_external/l10n/de.js
+++ b/apps/files_external/l10n/de.js
@@ -75,6 +75,7 @@ OC.L10N.register(
"Region" : "Region",
"Enable SSL" : "SSL aktivieren",
"Enable Path Style" : "Pfad-Stil aktivieren",
+ "Legacy (v2) authentication" : "Legacy-Authentifizierung (V2)",
"WebDAV" : "WebDAV",
"URL" : "URL",
"Remote subfolder" : "Entfernter Unterordner",
diff --git a/apps/files_external/l10n/de.json b/apps/files_external/l10n/de.json
index c93ba785568..53fd2e422fa 100644
--- a/apps/files_external/l10n/de.json
+++ b/apps/files_external/l10n/de.json
@@ -73,6 +73,7 @@
"Region" : "Region",
"Enable SSL" : "SSL aktivieren",
"Enable Path Style" : "Pfad-Stil aktivieren",
+ "Legacy (v2) authentication" : "Legacy-Authentifizierung (V2)",
"WebDAV" : "WebDAV",
"URL" : "URL",
"Remote subfolder" : "Entfernter Unterordner",
diff --git a/apps/files_external/l10n/de_DE.js b/apps/files_external/l10n/de_DE.js
index 6851466d426..5f01b5179de 100644
--- a/apps/files_external/l10n/de_DE.js
+++ b/apps/files_external/l10n/de_DE.js
@@ -75,6 +75,7 @@ OC.L10N.register(
"Region" : "Region",
"Enable SSL" : "SSL aktivieren",
"Enable Path Style" : "Pfadstil aktivieren",
+ "Legacy (v2) authentication" : "Legacy-Authentifizierung (V2)",
"WebDAV" : "WebDAV",
"URL" : "Adresse",
"Remote subfolder" : "Entfernter Unterordner",
diff --git a/apps/files_external/l10n/de_DE.json b/apps/files_external/l10n/de_DE.json
index 22e359c0378..e5f25c97a1f 100644
--- a/apps/files_external/l10n/de_DE.json
+++ b/apps/files_external/l10n/de_DE.json
@@ -73,6 +73,7 @@
"Region" : "Region",
"Enable SSL" : "SSL aktivieren",
"Enable Path Style" : "Pfadstil aktivieren",
+ "Legacy (v2) authentication" : "Legacy-Authentifizierung (V2)",
"WebDAV" : "WebDAV",
"URL" : "Adresse",
"Remote subfolder" : "Entfernter Unterordner",
diff --git a/apps/files_external/l10n/es.js b/apps/files_external/l10n/es.js
index b980588862e..7bfed57158d 100644
--- a/apps/files_external/l10n/es.js
+++ b/apps/files_external/l10n/es.js
@@ -75,6 +75,7 @@ OC.L10N.register(
"Region" : "Región",
"Enable SSL" : "Habilitar SSL",
"Enable Path Style" : "Habilitar Estilo de Ruta",
+ "Legacy (v2) authentication" : "Autenticación heredada (v2)",
"WebDAV" : "WebDAV",
"URL" : "URL",
"Remote subfolder" : "Subcarpeta remota",
diff --git a/apps/files_external/l10n/es.json b/apps/files_external/l10n/es.json
index 0c6ab09d88d..79c81accd58 100644
--- a/apps/files_external/l10n/es.json
+++ b/apps/files_external/l10n/es.json
@@ -73,6 +73,7 @@
"Region" : "Región",
"Enable SSL" : "Habilitar SSL",
"Enable Path Style" : "Habilitar Estilo de Ruta",
+ "Legacy (v2) authentication" : "Autenticación heredada (v2)",
"WebDAV" : "WebDAV",
"URL" : "URL",
"Remote subfolder" : "Subcarpeta remota",
diff --git a/apps/files_external/l10n/it.js b/apps/files_external/l10n/it.js
index e19f6a657db..4bd2908b431 100644
--- a/apps/files_external/l10n/it.js
+++ b/apps/files_external/l10n/it.js
@@ -75,6 +75,7 @@ OC.L10N.register(
"Region" : "Regione",
"Enable SSL" : "Abilita SSL",
"Enable Path Style" : "Abilita stile percorsi",
+ "Legacy (v2) authentication" : "Autenticazione tradizionale (v2)",
"WebDAV" : "WebDAV",
"URL" : "URL",
"Remote subfolder" : "Sottocartella remota",
diff --git a/apps/files_external/l10n/it.json b/apps/files_external/l10n/it.json
index ecb8d2082e6..36d4b296199 100644
--- a/apps/files_external/l10n/it.json
+++ b/apps/files_external/l10n/it.json
@@ -73,6 +73,7 @@
"Region" : "Regione",
"Enable SSL" : "Abilita SSL",
"Enable Path Style" : "Abilita stile percorsi",
+ "Legacy (v2) authentication" : "Autenticazione tradizionale (v2)",
"WebDAV" : "WebDAV",
"URL" : "URL",
"Remote subfolder" : "Sottocartella remota",
diff --git a/apps/files_external/l10n/tr.js b/apps/files_external/l10n/tr.js
index 152963db282..1def1246fde 100644
--- a/apps/files_external/l10n/tr.js
+++ b/apps/files_external/l10n/tr.js
@@ -75,6 +75,7 @@ OC.L10N.register(
"Region" : "Bölge",
"Enable SSL" : "SSL Kullanılsın",
"Enable Path Style" : "Yol Stili Kullanılsın",
+ "Legacy (v2) authentication" : "Eski (v2) kimlik doğrulama",
"WebDAV" : "WebDAV",
"URL" : "Adres",
"Remote subfolder" : "Uzak alt klasör",
diff --git a/apps/files_external/l10n/tr.json b/apps/files_external/l10n/tr.json
index efa75513f54..ea9fbc84c5f 100644
--- a/apps/files_external/l10n/tr.json
+++ b/apps/files_external/l10n/tr.json
@@ -73,6 +73,7 @@
"Region" : "Bölge",
"Enable SSL" : "SSL Kullanılsın",
"Enable Path Style" : "Yol Stili Kullanılsın",
+ "Legacy (v2) authentication" : "Eski (v2) kimlik doğrulama",
"WebDAV" : "WebDAV",
"URL" : "Adres",
"Remote subfolder" : "Uzak alt klasör",
diff --git a/apps/theming/css/theming.scss b/apps/theming/css/theming.scss
index 4474c232d94..63d466542e1 100644
--- a/apps/theming/css/theming.scss
+++ b/apps/theming/css/theming.scss
@@ -1,3 +1,12 @@
+/** Calculate luma as it is also used in OCA\Theming\Util::calculateLuma */
+@function luma($c) {
+ $-local-red: red(rgba($c, 1.0));
+ $-local-green: green(rgba($c, 1.0));
+ $-local-blue: blue(rgba($c, 1.0));
+
+ @return (0.2126 * $-local-red + 0.7152 * $-local-green + 0.0722 * $-local-blue) / 255;
+}
+
.nc-theming-main-background {
background-color: $color-primary;
}
@@ -10,7 +19,13 @@
color: $color-primary-text;
}
-@if (lightness($color-primary) > 55) {
+@if (luma($color-primary) > 0.6) {
+ #appmenu:not(.inverted) svg {
+ filter: invert(1);
+ }
+ #appmenu.inverted svg {
+ filter: none;
+ }
.searchbox input[type="search"] {
background: transparent url('../../../core/img/actions/search.svg') no-repeat 6px center;
}
@@ -53,6 +68,13 @@
background-color: nc-darken($color-primary-element, 30%) !important;
}
}
+} @else {
+ #appmenu:not(.inverted) svg {
+ filter: none;
+ }
+ #appmenu.inverted svg {
+ filter: invert(1);
+ }
}
/* Colorized svg images */
@@ -90,8 +112,8 @@ input.primary,
color: $color-primary-text;
}
-@if (lightness($color-primary) > 50) {
- #body-login #submit-icon.icon-confirm-white {
+@if (luma($color-primary) > 0.6) {
+ #body-login #submit-wrapper .icon-confirm-white {
background-image: url('../../../core/img/actions/confirm.svg');
}
}
diff --git a/apps/theming/lib/Controller/ThemingController.php b/apps/theming/lib/Controller/ThemingController.php
index 5fd6edd8ab7..6592eb7f7ab 100644
--- a/apps/theming/lib/Controller/ThemingController.php
+++ b/apps/theming/lib/Controller/ThemingController.php
@@ -324,7 +324,7 @@ class ThemingController extends Controller {
public function undo($setting) {
$value = $this->themingDefaults->undo($setting);
// reprocess server scss for preview
- $cssCached = $this->scssCacher->process(\OC::$SERVERROOT, '/core/css/server.scss', 'core');
+ $cssCached = $this->scssCacher->process(\OC::$SERVERROOT, 'core/css/server.scss', 'core');
if($setting === 'logoMime') {
try {
diff --git a/apps/theming/lib/Util.php b/apps/theming/lib/Util.php
index 194b5eeb0d0..6a1aa672108 100644
--- a/apps/theming/lib/Util.php
+++ b/apps/theming/lib/Util.php
@@ -63,8 +63,8 @@ class Util {
* @return bool
*/
public function invertTextColor($color) {
- $l = $this->calculateLuminance($color);
- if($l>0.55) {
+ $l = $this->calculateLuma($color);
+ if($l>0.6) {
return true;
} else {
return false;
@@ -90,6 +90,26 @@ class Util {
* @return float
*/
public function calculateLuminance($color) {
+ list($red, $green, $blue) = $this->hexToRGB($color);
+ $compiler = new Compiler();
+ $hsl = $compiler->toHSL($red, $green, $blue);
+ return $hsl[3]/100;
+ }
+
+ /**
+ * @param string $color rgb color value
+ * @return float
+ */
+ public function calculateLuma($color) {
+ list($red, $green, $blue) = $this->hexToRGB($color);
+ return (0.2126 * $red + 0.7152 * $green + 0.0722 * $blue) / 255;
+ }
+
+ /**
+ * @param string $color rgb color value
+ * @return int[]
+ */
+ public function hexToRGB($color) {
$hex = preg_replace("/[^0-9A-Fa-f]/", '', $color);
if (strlen($hex) === 3) {
$hex = $hex{0} . $hex{0} . $hex{1} . $hex{1} . $hex{2} . $hex{2};
@@ -97,12 +117,11 @@ class Util {
if (strlen($hex) !== 6) {
return 0;
}
- $red = hexdec(substr($hex, 0, 2));
- $green = hexdec(substr($hex, 2, 2));
- $blue = hexdec(substr($hex, 4, 2));
- $compiler = new Compiler();
- $hsl = $compiler->toHSL($red, $green, $blue);
- return $hsl[3]/100;
+ return [
+ hexdec(substr($hex, 0, 2)),
+ hexdec(substr($hex, 2, 2)),
+ hexdec(substr($hex, 4, 2))
+ ];
}
/**
diff --git a/apps/theming/tests/UtilTest.php b/apps/theming/tests/UtilTest.php
index 94e4c9cbcc8..1ad77be72d5 100644
--- a/apps/theming/tests/UtilTest.php
+++ b/apps/theming/tests/UtilTest.php
@@ -53,14 +53,20 @@ class UtilTest extends TestCase {
$this->util = new Util($this->config, $this->appManager, $this->appData);
}
- public function testInvertTextColorLight() {
- $invert = $this->util->invertTextColor('#ffffff');
- $this->assertEquals(true, $invert);
+ public function dataInvertTextColor() {
+ return [
+ ['#ffffff', true],
+ ['#000000', false],
+ ['#0082C9', false],
+ ['#ffff00', true],
+ ];
}
-
- public function testInvertTextColorDark() {
- $invert = $this->util->invertTextColor('#000000');
- $this->assertEquals(false, $invert);
+ /**
+ * @dataProvider dataInvertTextColor
+ */
+ public function testInvertTextColor($color, $expected) {
+ $invert = $this->util->invertTextColor($color);
+ $this->assertEquals($expected, $invert);
}
public function testCalculateLuminanceLight() {
diff --git a/apps/user_ldap/composer/composer/autoload_classmap.php b/apps/user_ldap/composer/composer/autoload_classmap.php
index 7bade37d9f7..98a1bbfa1b7 100644
--- a/apps/user_ldap/composer/composer/autoload_classmap.php
+++ b/apps/user_ldap/composer/composer/autoload_classmap.php
@@ -7,6 +7,7 @@ $baseDir = $vendorDir;
return array(
'OCA\\User_LDAP\\Access' => $baseDir . '/../lib/Access.php',
+ 'OCA\\User_LDAP\\AccessFactory' => $baseDir . '/../lib/AccessFactory.php',
'OCA\\User_LDAP\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php',
'OCA\\User_LDAP\\BackendUtility' => $baseDir . '/../lib/BackendUtility.php',
'OCA\\User_LDAP\\Command\\CheckUser' => $baseDir . '/../lib/Command/CheckUser.php',
@@ -19,6 +20,7 @@ return array(
'OCA\\User_LDAP\\Command\\TestConfig' => $baseDir . '/../lib/Command/TestConfig.php',
'OCA\\User_LDAP\\Configuration' => $baseDir . '/../lib/Configuration.php',
'OCA\\User_LDAP\\Connection' => $baseDir . '/../lib/Connection.php',
+ 'OCA\\User_LDAP\\ConnectionFactory' => $baseDir . '/../lib/ConnectionFactory.php',
'OCA\\User_LDAP\\Controller\\ConfigAPIController' => $baseDir . '/../lib/Controller/ConfigAPIController.php',
'OCA\\User_LDAP\\Controller\\RenewPasswordController' => $baseDir . '/../lib/Controller/RenewPasswordController.php',
'OCA\\User_LDAP\\Exceptions\\ConstraintViolationException' => $baseDir . '/../lib/Exceptions/ConstraintViolationException.php',
diff --git a/apps/user_ldap/composer/composer/autoload_static.php b/apps/user_ldap/composer/composer/autoload_static.php
index fbe63285273..6c97237d62d 100644
--- a/apps/user_ldap/composer/composer/autoload_static.php
+++ b/apps/user_ldap/composer/composer/autoload_static.php
@@ -19,6 +19,7 @@ class ComposerStaticInitUser_LDAP
public static $classMap = array (
'OCA\\User_LDAP\\Access' => __DIR__ . '/..' . '/../lib/Access.php',
+ 'OCA\\User_LDAP\\AccessFactory' => __DIR__ . '/..' . '/../lib/AccessFactory.php',
'OCA\\User_LDAP\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php',
'OCA\\User_LDAP\\BackendUtility' => __DIR__ . '/..' . '/../lib/BackendUtility.php',
'OCA\\User_LDAP\\Command\\CheckUser' => __DIR__ . '/..' . '/../lib/Command/CheckUser.php',
@@ -31,6 +32,7 @@ class ComposerStaticInitUser_LDAP
'OCA\\User_LDAP\\Command\\TestConfig' => __DIR__ . '/..' . '/../lib/Command/TestConfig.php',
'OCA\\User_LDAP\\Configuration' => __DIR__ . '/..' . '/../lib/Configuration.php',
'OCA\\User_LDAP\\Connection' => __DIR__ . '/..' . '/../lib/Connection.php',
+ 'OCA\\User_LDAP\\ConnectionFactory' => __DIR__ . '/..' . '/../lib/ConnectionFactory.php',
'OCA\\User_LDAP\\Controller\\ConfigAPIController' => __DIR__ . '/..' . '/../lib/Controller/ConfigAPIController.php',
'OCA\\User_LDAP\\Controller\\RenewPasswordController' => __DIR__ . '/..' . '/../lib/Controller/RenewPasswordController.php',
'OCA\\User_LDAP\\Exceptions\\ConstraintViolationException' => __DIR__ . '/..' . '/../lib/Exceptions/ConstraintViolationException.php',
diff --git a/apps/user_ldap/lib/AccessFactory.php b/apps/user_ldap/lib/AccessFactory.php
new file mode 100644
index 00000000000..45ff779bb01
--- /dev/null
+++ b/apps/user_ldap/lib/AccessFactory.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\User_LDAP;
+
+
+use OCA\User_LDAP\User\Manager;
+use OCP\IConfig;
+
+class AccessFactory {
+ /** @var ILDAPWrapper */
+ protected $ldap;
+ /** @var Manager */
+ protected $userManager;
+ /** @var Helper */
+ protected $helper;
+ /** @var IConfig */
+ protected $config;
+
+ public function __construct(
+ ILDAPWrapper $ldap,
+ Manager $userManager,
+ Helper $helper,
+ IConfig $config)
+ {
+ $this->ldap = $ldap;
+ $this->userManager = $userManager;
+ $this->helper = $helper;
+ $this->config = $config;
+ }
+
+ public function get(Connection $connection) {
+ return new Access(
+ $connection,
+ $this->ldap,
+ $this->userManager,
+ $this->helper,
+ $this->config
+ );
+ }
+}
diff --git a/apps/user_ldap/lib/ConnectionFactory.php b/apps/user_ldap/lib/ConnectionFactory.php
new file mode 100644
index 00000000000..0857afdcc8d
--- /dev/null
+++ b/apps/user_ldap/lib/ConnectionFactory.php
@@ -0,0 +1,38 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\User_LDAP;
+
+
+class ConnectionFactory {
+ /** @var ILDAPWrapper */
+ private $ldap;
+
+ public function __construct(ILDAPWrapper $ldap) {
+ $this->ldap = $ldap;
+ }
+
+ public function get($prefix) {
+ return new Connection($this->ldap, $prefix, 'user_ldap');
+ }
+}
diff --git a/apps/user_ldap/lib/Jobs/Sync.php b/apps/user_ldap/lib/Jobs/Sync.php
index d8e0e854718..b78a1947e27 100644
--- a/apps/user_ldap/lib/Jobs/Sync.php
+++ b/apps/user_ldap/lib/Jobs/Sync.php
@@ -26,8 +26,10 @@ namespace OCA\User_LDAP\Jobs;
use OC\BackgroundJob\TimedJob;
use OC\ServerNotAvailableException;
use OCA\User_LDAP\Access;
+use OCA\User_LDAP\AccessFactory;
use OCA\User_LDAP\Configuration;
use OCA\User_LDAP\Connection;
+use OCA\User_LDAP\ConnectionFactory;
use OCA\User_LDAP\FilesystemHelper;
use OCA\User_LDAP\Helper;
use OCA\User_LDAP\LDAP;
@@ -62,6 +64,10 @@ class Sync extends TimedJob {
protected $ncUserManager;
/** @var IManager */
protected $notificationManager;
+ /** @var ConnectionFactory */
+ protected $connectionFactory;
+ /** @var AccessFactory */
+ protected $accessFactory;
public function __construct() {
$this->setInterval(
@@ -112,7 +118,7 @@ class Sync extends TimedJob {
/**
* @param array $argument
*/
- protected function run($argument) {
+ public function run($argument) {
$this->setArgument($argument);
$isBackgroundJobModeAjax = $this->config
@@ -140,11 +146,11 @@ class Sync extends TimedJob {
if ($expectMoreResults) {
$this->increaseOffset($cycleData);
} else {
- $this->determineNextCycle();
+ $this->determineNextCycle($cycleData);
}
$this->updateInterval();
} catch (ServerNotAvailableException $e) {
- $this->determineNextCycle();
+ $this->determineNextCycle($cycleData);
}
}
@@ -153,8 +159,8 @@ class Sync extends TimedJob {
* @return bool whether more results are expected from the same configuration
*/
public function runCycle($cycleData) {
- $connection = new Connection($this->ldap, $cycleData['prefix']);
- $access = new Access($connection, $this->ldap, $this->userManager, $this->ldapHelper, $this->config);
+ $connection = $this->connectionFactory->get($cycleData['prefix']);
+ $access = $this->accessFactory->get($connection);
$access->setUserMapper($this->mapper);
$filter = $access->combineFilterWithAnd(array(
@@ -173,7 +179,7 @@ class Sync extends TimedJob {
if($connection->ldapPagingSize === 0) {
return true;
}
- return count($results) !== $connection->ldapPagingSize;
+ return count($results) >= $connection->ldapPagingSize;
}
/**
@@ -246,7 +252,7 @@ class Sync extends TimedJob {
* @param $cycleData
* @return bool
*/
- protected function qualifiesToRun($cycleData) {
+ public function qualifiesToRun($cycleData) {
$lastChange = $this->config->getAppValue('user_ldap', $cycleData['prefix'] . '_lastChange', 0);
if((time() - $lastChange) > 60 * 30) {
return true;
@@ -358,5 +364,22 @@ class Sync extends TimedJob {
} else {
$this->mapper = new UserMapping($this->dbc);
}
+
+ if(isset($argument['connectionFactory'])) {
+ $this->connectionFactory = $argument['connectionFactory'];
+ } else {
+ $this->connectionFactory = new ConnectionFactory($this->ldap);
+ }
+
+ if(isset($argument['accessFactory'])) {
+ $this->accessFactory = $argument['accessFactory'];
+ } else {
+ $this->accessFactory = new AccessFactory(
+ $this->ldap,
+ $this->userManager,
+ $this->ldapHelper,
+ $this->config
+ );
+ }
}
}
diff --git a/apps/user_ldap/tests/Jobs/SyncTest.php b/apps/user_ldap/tests/Jobs/SyncTest.php
index f8a44de87e8..f8852a46664 100644
--- a/apps/user_ldap/tests/Jobs/SyncTest.php
+++ b/apps/user_ldap/tests/Jobs/SyncTest.php
@@ -23,6 +23,10 @@
namespace OCA\User_LDAP\Tests\Jobs;
+use OCA\User_LDAP\Access;
+use OCA\User_LDAP\AccessFactory;
+use OCA\User_LDAP\Connection;
+use OCA\User_LDAP\ConnectionFactory;
use OCA\User_LDAP\Helper;
use OCA\User_LDAP\Jobs\Sync;
use OCA\User_LDAP\LDAP;
@@ -33,6 +37,7 @@ use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IUserManager;
use OCP\Notification\IManager;
+use function Sodium\memcmp;
use Test\TestCase;
class SyncTest extends TestCase {
@@ -59,6 +64,10 @@ class SyncTest extends TestCase {
protected $ncUserManager;
/** @var IManager|\PHPUnit_Framework_MockObject_MockObject */
protected $notificationManager;
+ /** @var ConnectionFactory|\PHPUnit_Framework_MockObject_MockObject */
+ protected $connectionFactory;
+ /** @var AccessFactory|\PHPUnit_Framework_MockObject_MockObject */
+ protected $accessFactory;
public function setUp() {
parent::setUp();
@@ -72,6 +81,8 @@ class SyncTest extends TestCase {
$this->dbc = $this->createMock(IDBConnection::class);
$this->ncUserManager = $this->createMock(IUserManager::class);
$this->notificationManager = $this->createMock(IManager::class);
+ $this->connectionFactory = $this->createMock(ConnectionFactory::class);
+ $this->accessFactory = $this->createMock(AccessFactory::class);
$this->arguments = [
'helper' => $this->helper,
@@ -83,6 +94,8 @@ class SyncTest extends TestCase {
'dbc' => $this->dbc,
'ncUserManager' => $this->ncUserManager,
'notificationManager' => $this->notificationManager,
+ 'connectionFactory' => $this->connectionFactory,
+ 'accessFactory' => $this->accessFactory,
];
$this->sync = new Sync();
@@ -141,4 +154,216 @@ class SyncTest extends TestCase {
$this->sync->updateInterval();
}
+ public function moreResultsProvider() {
+ return [
+ [ 3, 3, true ],
+ [ 3, 5, true ],
+ [ 3, 2, false]
+ ];
+ }
+
+ /**
+ * @dataProvider moreResultsProvider
+ */
+ public function testMoreResults($pagingSize, $results, $expected) {
+ $connection = $this->createMock(Connection::class);
+ $this->connectionFactory->expects($this->any())
+ ->method('get')
+ ->willReturn($connection);
+ $connection->expects($this->any())
+ ->method('__get')
+ ->willReturnCallback(function ($key) use ($pagingSize) {
+ if($key === 'ldapPagingSize') {
+ return $pagingSize;
+ }
+ return null;
+ });
+
+ /** @var Access|\PHPUnit_Framework_MockObject_MockObject $access */
+ $access = $this->createMock(Access::class);
+ $this->accessFactory->expects($this->any())
+ ->method('get')
+ ->with($connection)
+ ->willReturn($access);
+
+ $access->expects($this->once())
+ ->method('fetchListOfUsers')
+ ->willReturn(array_pad([], $results, 'someUser'));
+ $access->connection = $connection;
+ $access->userManager = $this->userManager;
+
+ $this->sync->setArgument($this->arguments);
+ $hasMoreResults = $this->sync->runCycle(['prefix' => 's01', 'offset' => 100]);
+ $this->assertSame($expected, $hasMoreResults);
+ }
+
+ public function cycleDataProvider() {
+ $lastCycle = ['prefix' => 's01', 'offset' => 1000];
+ $lastCycle2 = ['prefix' => '', 'offset' => 1000];
+ return [
+ [ null, ['s01'], ['prefix' => 's01', 'offset' => 0] ],
+ [ null, [''], ['prefix' => '', 'offset' => 0] ],
+ [ $lastCycle, ['s01', 's02'], ['prefix' => 's02', 'offset' => 0] ],
+ [ $lastCycle, [''], ['prefix' => '', 'offset' => 0] ],
+ [ $lastCycle2, ['', 's01'], ['prefix' => 's01', 'offset' => 0] ],
+ [ $lastCycle, [], null ],
+ ];
+ }
+
+ /**
+ * @dataProvider cycleDataProvider
+ */
+ public function testDetermineNextCycle($cycleData, $prefixes, $expectedCycle) {
+ $this->helper->expects($this->any())
+ ->method('getServerConfigurationPrefixes')
+ ->with(true)
+ ->willReturn($prefixes);
+
+ if(is_array($expectedCycle)) {
+ $this->config->expects($this->exactly(2))
+ ->method('setAppValue')
+ ->withConsecutive(
+ ['user_ldap', 'background_sync_prefix', $expectedCycle['prefix']],
+ ['user_ldap', 'background_sync_offset', $expectedCycle['offset']]
+ );
+ } else {
+ $this->config->expects($this->never())
+ ->method('setAppValue');
+ }
+
+ $this->sync->setArgument($this->arguments);
+ $nextCycle = $this->sync->determineNextCycle($cycleData);
+
+ if($expectedCycle === null) {
+ $this->assertNull($nextCycle);
+ } else {
+ $this->assertSame($expectedCycle['prefix'], $nextCycle['prefix']);
+ $this->assertSame($expectedCycle['offset'], $nextCycle['offset']);
+ }
+ }
+
+ public function testQualifiesToRun() {
+ $cycleData = ['prefix' => 's01'];
+
+ $this->config->expects($this->exactly(2))
+ ->method('getAppValue')
+ ->willReturnOnConsecutiveCalls(time() - 60*40, time() - 60*20);
+
+ $this->sync->setArgument($this->arguments);
+ $this->assertTrue($this->sync->qualifiesToRun($cycleData));
+ $this->assertFalse($this->sync->qualifiesToRun($cycleData));
+ }
+
+ public function runDataProvider() {
+ return [
+ #0 - one LDAP server, reset
+ [[
+ 'prefixes' => [''],
+ 'scheduledCycle' => ['prefix' => '', 'offset' => '4500'],
+ 'pagingSize' => 500,
+ 'usersThisCycle' => 0,
+ 'expectedNextCycle' => ['prefix' => '', 'offset' => '0'],
+ 'mappedUsers' => 123,
+ ]],
+ #1 - 2 LDAP servers, next prefix
+ [[
+ 'prefixes' => ['', 's01'],
+ 'scheduledCycle' => ['prefix' => '', 'offset' => '4500'],
+ 'pagingSize' => 500,
+ 'usersThisCycle' => 0,
+ 'expectedNextCycle' => ['prefix' => 's01', 'offset' => '0'],
+ 'mappedUsers' => 123,
+ ]],
+ #2 - 2 LDAP servers, rotate prefix
+ [[
+ 'prefixes' => ['', 's01'],
+ 'scheduledCycle' => ['prefix' => 's01', 'offset' => '4500'],
+ 'pagingSize' => 500,
+ 'usersThisCycle' => 0,
+ 'expectedNextCycle' => ['prefix' => '', 'offset' => '0'],
+ 'mappedUsers' => 123,
+ ]],
+ ];
+ }
+
+ /**
+ * @dataProvider runDataProvider
+ */
+ public function testRun($runData) {
+ $this->config->expects($this->any())
+ ->method('getAppValue')
+ ->willReturnCallback(function($app, $key, $default) use ($runData) {
+ if($app === 'core' && $key === 'backgroundjobs_mode') {
+ return 'cron';
+ }
+ if($app = 'user_ldap') {
+ // for getCycle()
+ if($key === 'background_sync_prefix') {
+ return $runData['scheduledCycle']['prefix'];
+ }
+ if($key === 'background_sync_offset') {
+ return $runData['scheduledCycle']['offset'];
+ }
+ // for qualifiesToRun()
+ if($key === $runData['scheduledCycle']['prefix'] . '_lastChange') {
+ return time() - 60*40;
+ }
+ // for getMinPagingSize
+ if($key === $runData['scheduledCycle']['prefix'] . 'ldap_paging_size') {
+ return $runData['pagingSize'];
+ }
+ }
+
+ return $default;
+ });
+ $this->config->expects($this->exactly(3))
+ ->method('setAppValue')
+ ->withConsecutive(
+ ['user_ldap', 'background_sync_prefix', $runData['expectedNextCycle']['prefix']],
+ ['user_ldap', 'background_sync_offset', $runData['expectedNextCycle']['offset']],
+ ['user_ldap', 'background_sync_interval', $this->anything()]
+ );
+ $this->config->expects($this->any())
+ ->method('getAppKeys')
+ ->with('user_ldap')
+ ->willReturn([$runData['scheduledCycle']['prefix'] . 'ldap_paging_size']);
+
+ $this->helper->expects($this->any())
+ ->method('getServerConfigurationPrefixes')
+ ->with(true)
+ ->willReturn($runData['prefixes']);
+
+ $connection = $this->createMock(Connection::class);
+ $this->connectionFactory->expects($this->any())
+ ->method('get')
+ ->willReturn($connection);
+ $connection->expects($this->any())
+ ->method('__get')
+ ->willReturnCallback(function ($key) use ($runData) {
+ if($key === 'ldapPagingSize') {
+ return $runData['pagingSize'];
+ }
+ return null;
+ });
+
+ /** @var Access|\PHPUnit_Framework_MockObject_MockObject $access */
+ $access = $this->createMock(Access::class);
+ $this->accessFactory->expects($this->any())
+ ->method('get')
+ ->with($connection)
+ ->willReturn($access);
+
+ $access->expects($this->once())
+ ->method('fetchListOfUsers')
+ ->willReturn(array_pad([], $runData['usersThisCycle'], 'someUser'));
+ $access->connection = $connection;
+ $access->userManager = $this->userManager;
+
+ $this->mapper->expects($this->any())
+ ->method('count')
+ ->willReturn($runData['mappedUsers']);
+
+ $this->sync->run($this->arguments);
+ }
+
}
diff --git a/build/files-checker.php b/build/files-checker.php
index 66c44bd971b..20d8b4b5f33 100644
--- a/build/files-checker.php
+++ b/build/files-checker.php
@@ -49,6 +49,7 @@ $expectedFiles = [
'build',
'buildjsdocs.sh',
'CHANGELOG.md',
+ 'CODE_OF_CONDUCT.md',
'composer.json',
'config',
'console.php',
diff --git a/config/config.sample.php b/config/config.sample.php
index 05efcaa2738..5f29933ec65 100644
--- a/config/config.sample.php
+++ b/config/config.sample.php
@@ -849,25 +849,16 @@ $CONFIG = array(
* The maximum width, in pixels, of a preview. A value of ``null`` means there
* is no limit.
*
- * Defaults to ``2048``
+ * Defaults to ``4096``
*/
-'preview_max_x' => 2048,
+'preview_max_x' => 4096,
/**
* The maximum height, in pixels, of a preview. A value of ``null`` means there
* is no limit.
*
- * Defaults to ``2048``
+ * Defaults to ``4096``
*/
-'preview_max_y' => 2048,
-/**
- * If a lot of small pictures are stored on the Nextcloud instance and the
- * preview system generates blurry previews, you might want to consider setting
- * a maximum scale factor. By default, pictures are upscaled to 10 times the
- * original size. A value of ``1`` or ``null`` disables scaling.
- *
- * Defaults to ``2``
- */
-'preview_max_scale_factor' => 10,
+'preview_max_y' => 4096,
/**
* max file size for generating image previews with imagegd (default behavior)
diff --git a/core/css/apps.scss b/core/css/apps.scss
index e6ead27e12b..41eea3bb524 100644
--- a/core/css/apps.scss
+++ b/core/css/apps.scss
@@ -235,6 +235,7 @@ kbd {
&:first-child img {
margin-right: 11px;
width: 16px;
+ height: 16px;
margin-left: -30px;
}
diff --git a/core/css/fixes.scss b/core/css/fixes.scss
index 0303b4d751a..09ab9c1d244 100644
--- a/core/css/fixes.scss
+++ b/core/css/fixes.scss
@@ -20,6 +20,7 @@ select {
.ie .header-left #navigation,
.ie .ui-datepicker,
.ie .ui-timepicker.ui-widget,
-.ie #appmenu li span {
+.ie #appmenu li span,
+.ie .tooltip-inner {
box-shadow: 0 1px 10px $color-box-shadow;
}
diff --git a/core/css/styles.scss b/core/css/styles.scss
index b782c4e558a..4b02041976b 100644
--- a/core/css/styles.scss
+++ b/core/css/styles.scss
@@ -234,7 +234,7 @@ body {
padding: 0;
margin: 0;
background-color: rgba($color-main-background, 0.95);
- z-index: 55;
+ z-index: 60;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
diff --git a/core/css/tooltip.scss b/core/css/tooltip.scss
index e9982b580ca..a974e05e1a6 100644
--- a/core/css/tooltip.scss
+++ b/core/css/tooltip.scss
@@ -31,6 +31,7 @@
font-size: 12px;
opacity: 0;
z-index: 100000;
+ filter: drop-shadow(0 1px 10px $color-box-shadow);
&.in {
opacity: 1;
}
@@ -115,7 +116,6 @@
padding: 5px 8px;
background-color: $color-main-background;
color: $color-main-text;
- box-shadow: 0 1px 10px $color-box-shadow;
text-align: center;
border-radius: $border-radius;
}
diff --git a/core/js/jquery.avatar.js b/core/js/jquery.avatar.js
index 958f0f9edd7..6da86341c1e 100644
--- a/core/js/jquery.avatar.js
+++ b/core/js/jquery.avatar.js
@@ -110,9 +110,8 @@
// If the new image loads successfully set it.
img.onload = function() {
- $div.text('');
- $div.append(img);
$div.clearimageplaceholder();
+ $div.append(img);
if(typeof callback === 'function') {
callback();
@@ -127,7 +126,6 @@
$div.imageplaceholder(user, displayname);
} else {
setAvatarForUnknownUser($div);
- $div.removeClass('icon-loading');
}
if(typeof callback === 'function') {
@@ -136,7 +134,6 @@
};
$div.addClass('icon-loading');
- $div.show();
img.width = size;
img.height = size;
img.src = url;
diff --git a/core/js/placeholder.js b/core/js/placeholder.js
index 5cf7b9095ad..29f91b6698d 100644
--- a/core/js/placeholder.js
+++ b/core/js/placeholder.js
@@ -156,6 +156,7 @@
this.css('text-align', '');
this.css('line-height', '');
this.css('font-size', '');
+ this.html('');
this.removeClass('icon-loading');
};
}(jQuery));
diff --git a/core/js/public/comments.js b/core/js/public/comments.js
index 6de7ff7d38a..955e88c8609 100644
--- a/core/js/public/comments.js
+++ b/core/js/public/comments.js
@@ -43,7 +43,7 @@
}
var linkText = url.replace(self.protocolRegex, '');
- return '<a class="external" href="' + url + '">' + linkText + '</a>';
+ return '<a class="external" target="_blank" href="' + url + '">' + linkText + '</a>';
});
},
diff --git a/core/js/tests/specs/jquery.avatarSpec.js b/core/js/tests/specs/jquery.avatarSpec.js
index bdd1fdcc163..4e13b7f26ff 100644
--- a/core/js/tests/specs/jquery.avatarSpec.js
+++ b/core/js/tests/specs/jquery.avatarSpec.js
@@ -202,8 +202,6 @@ describe('jquery.avatar tests', function() {
expect(window.Image().height).toEqual(32);
expect(window.Image().width).toEqual(32);
expect(window.Image().src).toEqual('http://localhost/index.php/avatar/foo/32');
-
- expect($div.css('display')).toEqual('block');
});
it('callback called', function() {
diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php
index 127e794e120..32762e2c240 100644
--- a/core/templates/layout.user.php
+++ b/core/templates/layout.user.php
@@ -41,19 +41,17 @@
</div>
</a>
- <ul id="appmenu">
+ <ul id="appmenu" <?php if ($_['themingInvertMenu']) { ?>class="inverted"<?php } ?>>
<?php foreach ($_['navigation'] as $entry): ?>
<li data-id="<?php p($entry['id']); ?>" class="hidden">
<a href="<?php print_unescaped($entry['href']); ?>"
<?php if ($entry['active']): ?> class="active"<?php endif; ?>>
- <?php if ($_['themingInvertMenu']) { ?>
<svg width="20" height="20" viewBox="0 0 20 20">
- <defs><filter id="invertMenuMain-<?php p($entry['id']); ?>"><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" /></filter></defs>
- <image x="0" y="0" width="20" height="20" preserveAspectRatio="xMinYMin meet" filter="url(#invertMenuMain-<?php p($entry['id']); ?>)" xlink:href="<?php print_unescaped($entry['icon'] . '?v=' . $_['versionHash']); ?>" class="app-icon" /></svg>
- <?php } else { ?>
- <img src="<?php print_unescaped($entry['icon'] . '?v=' . $_['versionHash']); ?>"
- class="app-icon" alt="<?php p($entry['name']); ?>" />
- <?php } ?>
+ <?php if ($_['themingInvertMenu']) { ?>
+ <defs><filter id="invertMenuMain-<?php p($entry['id']); ?>"><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" /></filter></defs>
+ <?php } ?>
+ <image x="0" y="0" width="20" height="20" preserveAspectRatio="xMinYMin meet"<?php if ($_['themingInvertMenu']) { ?> filter="url(#invertMenuMain-<?php p($entry['id']); ?>)"<?php } ?> xlink:href="<?php print_unescaped($entry['icon'] . '?v=' . $_['versionHash']); ?>" class="app-icon" />
+ </svg>
<div class="icon-loading-small-dark"
style="display:none;"></div>
</a>
diff --git a/lib/private/Preview/Generator.php b/lib/private/Preview/Generator.php
index 402732ecda9..448a7a57580 100644
--- a/lib/private/Preview/Generator.php
+++ b/lib/private/Preview/Generator.php
@@ -128,9 +128,13 @@ class Generator {
// Try to get a cached preview. Else generate (and store) one
try {
- $file = $this->getCachedPreview($previewFolder, $width, $height, $crop);
- } catch (NotFoundException $e) {
- $file = $this->generatePreview($previewFolder, $maxPreview, $width, $height, $crop, $maxWidth, $maxHeight);
+ try {
+ $file = $this->getCachedPreview($previewFolder, $width, $height, $crop, $maxPreview->getMimeType());
+ } catch (NotFoundException $e) {
+ $file = $this->generatePreview($previewFolder, $maxPreview, $width, $height, $crop, $maxWidth, $maxHeight);
+ }
+ } catch (\InvalidArgumentException $e) {
+ throw new NotFoundException();
}
return $file;
@@ -164,8 +168,8 @@ class Generator {
continue;
}
- $maxWidth = (int)$this->config->getSystemValue('preview_max_x', 2048);
- $maxHeight = (int)$this->config->getSystemValue('preview_max_y', 2048);
+ $maxWidth = (int)$this->config->getSystemValue('preview_max_x', 4096);
+ $maxHeight = (int)$this->config->getSystemValue('preview_max_y', 4096);
$preview = $this->helper->getThumbnail($provider, $file, $maxWidth, $maxHeight);
@@ -173,7 +177,15 @@ class Generator {
continue;
}
- $path = (string)$preview->width() . '-' . (string)$preview->height() . '-max.png';
+ // Try to get the extention.
+ try {
+ $ext = $this->getExtention($preview->dataMimeType());
+ } catch (\InvalidArgumentException $e) {
+ // Just continue to the next iteration if this preview doesn't have a valid mimetype
+ continue;
+ }
+
+ $path = (string)$preview->width() . '-' . (string)$preview->height() . '-max.' . $ext;
try {
$file = $previewFolder->newFile($path);
$file->putContent($preview->data());
@@ -201,14 +213,17 @@ class Generator {
* @param int $width
* @param int $height
* @param bool $crop
+ * @param string $mimeType
* @return string
*/
- private function generatePath($width, $height, $crop) {
+ private function generatePath($width, $height, $crop, $mimeType) {
$path = (string)$width . '-' . (string)$height;
if ($crop) {
$path .= '-crop';
}
- $path .= '.png';
+
+ $ext = $this->getExtention($mimeType);
+ $path .= '.' . $ext;
return $path;
}
@@ -340,7 +355,7 @@ class Generator {
}
- $path = $this->generatePath($width, $height, $crop);
+ $path = $this->generatePath($width, $height, $crop, $preview->dataMimeType());
try {
$file = $previewFolder->newFile($path);
$file->putContent($preview->data());
@@ -356,12 +371,13 @@ class Generator {
* @param int $width
* @param int $height
* @param bool $crop
+ * @param string $mimeType
* @return ISimpleFile
*
* @throws NotFoundException
*/
- private function getCachedPreview(ISimpleFolder $previewFolder, $width, $height, $crop) {
- $path = $this->generatePath($width, $height, $crop);
+ private function getCachedPreview(ISimpleFolder $previewFolder, $width, $height, $crop, $mimeType) {
+ $path = $this->generatePath($width, $height, $crop, $mimeType);
return $previewFolder->getFile($path);
}
@@ -381,4 +397,22 @@ class Generator {
return $folder;
}
+
+ /**
+ * @param string $mimeType
+ * @return null|string
+ * @throws \InvalidArgumentException
+ */
+ private function getExtention($mimeType) {
+ switch ($mimeType) {
+ case 'image/png':
+ return 'png';
+ case 'image/jpeg':
+ return 'jpg';
+ case 'image/gif':
+ return 'gif';
+ default:
+ throw new \InvalidArgumentException('Not a valid mimetype');
+ }
+ }
}
diff --git a/lib/private/Settings/Manager.php b/lib/private/Settings/Manager.php
index cd0af5e7bb2..efeedbe6fcc 100644
--- a/lib/private/Settings/Manager.php
+++ b/lib/private/Settings/Manager.php
@@ -47,6 +47,7 @@ use OCP\Lock\ILockingProvider;
use OCP\Settings\ISettings;
use OCP\Settings\IManager;
use OCP\Settings\ISection;
+use OCP\Util;
class Manager implements IManager {
/** @var ILogger */
@@ -344,7 +345,7 @@ class Manager implements IManager {
try {
return \OC::$server->query($className);
} catch (QueryException $e) {
- $this->log->logException($e);
+ $this->log->logException($e, ['level' => Util::INFO]);
throw $e;
}
}
diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php
index 844b36b2994..b9ab7a46873 100644
--- a/lib/private/Share20/DefaultShareProvider.php
+++ b/lib/private/Share20/DefaultShareProvider.php
@@ -452,9 +452,9 @@ class DefaultShareProvider implements IShareProvider {
'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
'parent' => $qb->createNamedParameter($share->getId()),
- 'item_type' => $qb->createNamedParameter($share->getNode() instanceof File ? 'file' : 'folder'),
- 'item_source' => $qb->createNamedParameter($share->getNode()->getId()),
- 'file_source' => $qb->createNamedParameter($share->getNode()->getId()),
+ 'item_type' => $qb->createNamedParameter($share->getNodeType()),
+ 'item_source' => $qb->createNamedParameter($share->getNodeId()),
+ 'file_source' => $qb->createNamedParameter($share->getNodeId()),
'file_target' => $qb->createNamedParameter($share->getTarget()),
'permissions' => $qb->createNamedParameter($share->getPermissions()),
'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
diff --git a/lib/private/legacy/image.php b/lib/private/legacy/image.php
index a7d702ac032..a0159b927f9 100644
--- a/lib/private/legacy/image.php
+++ b/lib/private/legacy/image.php
@@ -305,6 +305,25 @@ class OC_Image implements \OCP\IImage {
}
/**
+ * @return string Returns the mimetype of the data. Returns the empty string
+ * if the data is not valid.
+ */
+ public function dataMimeType() {
+ if (!$this->valid()) {
+ return '';
+ }
+
+ switch ($this->mimeType) {
+ case 'image/png':
+ case 'image/jpeg':
+ case 'image/gif':
+ return $this->mimeType;
+ default:
+ return 'image/png';
+ }
+ }
+
+ /**
* @return null|string Returns the raw image data.
*/
public function data() {
diff --git a/lib/public/IImage.php b/lib/public/IImage.php
index f63a1b8ca60..70e8b3cff75 100644
--- a/lib/public/IImage.php
+++ b/lib/public/IImage.php
@@ -103,6 +103,12 @@ interface IImage {
public function resource();
/**
+ * @return string Returns the raw data mimetype
+ * @since 13.0.0
+ */
+ public function dataMimeType();
+
+ /**
* @return string Returns the raw image data.
* @since 8.1.0
*/
diff --git a/ocs/v1.php b/ocs/v1.php
index 43a1c2d9e0e..b6ed5697a69 100644
--- a/ocs/v1.php
+++ b/ocs/v1.php
@@ -81,7 +81,7 @@ try {
$format = \OC::$server->getRequest()->getParam('format', 'xml');
$txt='Invalid query, please check the syntax. API specifications are here:'
- .' http://www.freedesktop.org/wiki/Specifications/open-collaboration-services. DEBUG OUTPUT:'."\n";
+ .' http://www.freedesktop.org/wiki/Specifications/open-collaboration-services.'."\n";
OC_API::respond(new \OC\OCS\Result(null, \OCP\API::RESPOND_NOT_FOUND, $txt), $format);
} catch (MethodNotAllowedException $e) {
OC_API::setContentType();
@@ -96,7 +96,7 @@ try {
$format = \OC::$server->getRequest()->getParam('format', 'xml');
$txt='Invalid query, please check the syntax. API specifications are here:'
- .' http://www.freedesktop.org/wiki/Specifications/open-collaboration-services. DEBUG OUTPUT:'."\n";
+ .' http://www.freedesktop.org/wiki/Specifications/open-collaboration-services.'."\n";
OC_API::respond(new \OC\OCS\Result(null, \OCP\API::RESPOND_NOT_FOUND, $txt), $format);
}
diff --git a/settings/l10n/es.js b/settings/l10n/es.js
index bef3b13fcf5..041b63f6fc7 100644
--- a/settings/l10n/es.js
+++ b/settings/l10n/es.js
@@ -104,9 +104,15 @@ OC.L10N.register(
"Error: This app can not be enabled because it makes the server unstable" : "Error: Esta app no se puede activar porque desestabiliza el servidor",
"Error: Could not disable broken app" : "Error: No se ha podido desactivar una app estropeada",
"Error while disabling broken app" : "Error mientras deshabilitaba la App dañada",
+ "App up to date" : "App actualizada",
+ "Upgrading …" : "Actualizando...",
+ "Could not upgrade app" : "No se ha podido actualizar la app",
"Updated" : "Actualizado",
"Removing …" : "Eliminando...",
+ "Could not remove app" : "No se ha podido eliminar la app",
"Remove" : "Eliminar",
+ "The app has been enabled but needs to be upgraded. You will be redirected to the upgrade page in 5 seconds." : "La app ha sido activada pero tiene que actualizarse. Serás redirigido a la página de actualización en 5 segundos.",
+ "App upgrade" : "Actualización de la app",
"Approved" : "Aprobado",
"Experimental" : "Experimental",
"No apps found for {query}" : "No se han encontrado apps para {query}",
diff --git a/settings/l10n/es.json b/settings/l10n/es.json
index be7a4ecad19..9c8007c4848 100644
--- a/settings/l10n/es.json
+++ b/settings/l10n/es.json
@@ -102,9 +102,15 @@
"Error: This app can not be enabled because it makes the server unstable" : "Error: Esta app no se puede activar porque desestabiliza el servidor",
"Error: Could not disable broken app" : "Error: No se ha podido desactivar una app estropeada",
"Error while disabling broken app" : "Error mientras deshabilitaba la App dañada",
+ "App up to date" : "App actualizada",
+ "Upgrading …" : "Actualizando...",
+ "Could not upgrade app" : "No se ha podido actualizar la app",
"Updated" : "Actualizado",
"Removing …" : "Eliminando...",
+ "Could not remove app" : "No se ha podido eliminar la app",
"Remove" : "Eliminar",
+ "The app has been enabled but needs to be upgraded. You will be redirected to the upgrade page in 5 seconds." : "La app ha sido activada pero tiene que actualizarse. Serás redirigido a la página de actualización en 5 segundos.",
+ "App upgrade" : "Actualización de la app",
"Approved" : "Aprobado",
"Experimental" : "Experimental",
"No apps found for {query}" : "No se han encontrado apps para {query}",
diff --git a/settings/l10n/nb.js b/settings/l10n/nb.js
index 793ee8ff43b..e889a966092 100644
--- a/settings/l10n/nb.js
+++ b/settings/l10n/nb.js
@@ -104,6 +104,9 @@ OC.L10N.register(
"Error: This app can not be enabled because it makes the server unstable" : "Feil: Dette programmet kan ikke aktiveres fordi det gjør tjeneren ustabil",
"Error: Could not disable broken app" : "Feil: Kunne ikke deaktivere ustabilt program",
"Error while disabling broken app" : "Feil ved deaktivering av ustabilt program",
+ "App up to date" : "Appen er oppdatert",
+ "Upgrading …" : "Oppgraderer…",
+ "Could not upgrade app" : "Kunne ikke oppgradere appen",
"Updated" : "Oppdatert",
"Removing …" : "Fjerner…",
"Remove" : "Fjern",
diff --git a/settings/l10n/nb.json b/settings/l10n/nb.json
index c7f926f4acb..b95a39680b8 100644
--- a/settings/l10n/nb.json
+++ b/settings/l10n/nb.json
@@ -102,6 +102,9 @@
"Error: This app can not be enabled because it makes the server unstable" : "Feil: Dette programmet kan ikke aktiveres fordi det gjør tjeneren ustabil",
"Error: Could not disable broken app" : "Feil: Kunne ikke deaktivere ustabilt program",
"Error while disabling broken app" : "Feil ved deaktivering av ustabilt program",
+ "App up to date" : "Appen er oppdatert",
+ "Upgrading …" : "Oppgraderer…",
+ "Could not upgrade app" : "Kunne ikke oppgradere appen",
"Updated" : "Oppdatert",
"Removing …" : "Fjerner…",
"Remove" : "Fjern",
diff --git a/settings/l10n/ru.js b/settings/l10n/ru.js
index 8c11d9ea667..b5d98373e91 100644
--- a/settings/l10n/ru.js
+++ b/settings/l10n/ru.js
@@ -104,9 +104,15 @@ OC.L10N.register(
"Error: This app can not be enabled because it makes the server unstable" : "Ошибка: это приложение не может быть включено, так как оно сделает сервер нестабильным",
"Error: Could not disable broken app" : "Ошибка: невозможно отключить «сломанное» приложение",
"Error while disabling broken app" : "Ошибка при отключении сломанного приложения",
+ "App up to date" : "Приложение не нуждается в обновлении",
+ "Upgrading …" : "Обновление...",
+ "Could not upgrade app" : "Не удалось обновить приложение",
"Updated" : "Обновлено",
"Removing …" : "Удаление…",
+ "Could not remove app" : "Не удалось удалить приложение.",
"Remove" : "Удалить",
+ "The app has been enabled but needs to be upgraded. You will be redirected to the upgrade page in 5 seconds." : "Приложение было включено, но нуждается в обновлении. В течении 5 секунд будет выполнено перенаправление на страницу обновления.",
+ "App upgrade" : "Обновление приложения",
"Approved" : "Подтвержденное",
"Experimental" : "Экспериментальное",
"No apps found for {query}" : "Приложения не найдены по {query}",
diff --git a/settings/l10n/ru.json b/settings/l10n/ru.json
index bcacfeef65d..387973ffc82 100644
--- a/settings/l10n/ru.json
+++ b/settings/l10n/ru.json
@@ -102,9 +102,15 @@
"Error: This app can not be enabled because it makes the server unstable" : "Ошибка: это приложение не может быть включено, так как оно сделает сервер нестабильным",
"Error: Could not disable broken app" : "Ошибка: невозможно отключить «сломанное» приложение",
"Error while disabling broken app" : "Ошибка при отключении сломанного приложения",
+ "App up to date" : "Приложение не нуждается в обновлении",
+ "Upgrading …" : "Обновление...",
+ "Could not upgrade app" : "Не удалось обновить приложение",
"Updated" : "Обновлено",
"Removing …" : "Удаление…",
+ "Could not remove app" : "Не удалось удалить приложение.",
"Remove" : "Удалить",
+ "The app has been enabled but needs to be upgraded. You will be redirected to the upgrade page in 5 seconds." : "Приложение было включено, но нуждается в обновлении. В течении 5 секунд будет выполнено перенаправление на страницу обновления.",
+ "App upgrade" : "Обновление приложения",
"Approved" : "Подтвержденное",
"Experimental" : "Экспериментальное",
"No apps found for {query}" : "Приложения не найдены по {query}",
diff --git a/settings/l10n/tr.js b/settings/l10n/tr.js
index 58e6952bbee..08b81502141 100644
--- a/settings/l10n/tr.js
+++ b/settings/l10n/tr.js
@@ -104,9 +104,15 @@ OC.L10N.register(
"Error: This app can not be enabled because it makes the server unstable" : "Hata: Bu uygulama sunucuda kararsızlığa yol açtığından etkinleştirilemez",
"Error: Could not disable broken app" : "Hata: Bozuk uygulama devre dışı bırakılamadı",
"Error while disabling broken app" : "Bozuk uygulama devre dışı bırakılırken sorun çıktı",
+ "App up to date" : "Uygulama güncel",
+ "Upgrading …" : "Güncelleniyor …",
+ "Could not upgrade app" : "Uygulama güncellenemedi",
"Updated" : "Güncellendi",
"Removing …" : "Kaldırılıyor...",
+ "Could not remove app" : "Uygulama kaldırılamadı",
"Remove" : "Kaldır",
+ "The app has been enabled but needs to be upgraded. You will be redirected to the upgrade page in 5 seconds." : "Uygulama etkinleştirilmiş fakat güncellenmesi gerekiyor. 5 saniye içinde güncelleme sayfasına yönlendirileceksiniz.",
+ "App upgrade" : "Uygulama güncellemesi",
"Approved" : "Onaylanmış",
"Experimental" : "Deneysel",
"No apps found for {query}" : "{query} aramasına uyan bir uygulama bulunamadı",
diff --git a/settings/l10n/tr.json b/settings/l10n/tr.json
index 0485390111f..a04b466a87b 100644
--- a/settings/l10n/tr.json
+++ b/settings/l10n/tr.json
@@ -102,9 +102,15 @@
"Error: This app can not be enabled because it makes the server unstable" : "Hata: Bu uygulama sunucuda kararsızlığa yol açtığından etkinleştirilemez",
"Error: Could not disable broken app" : "Hata: Bozuk uygulama devre dışı bırakılamadı",
"Error while disabling broken app" : "Bozuk uygulama devre dışı bırakılırken sorun çıktı",
+ "App up to date" : "Uygulama güncel",
+ "Upgrading …" : "Güncelleniyor …",
+ "Could not upgrade app" : "Uygulama güncellenemedi",
"Updated" : "Güncellendi",
"Removing …" : "Kaldırılıyor...",
+ "Could not remove app" : "Uygulama kaldırılamadı",
"Remove" : "Kaldır",
+ "The app has been enabled but needs to be upgraded. You will be redirected to the upgrade page in 5 seconds." : "Uygulama etkinleştirilmiş fakat güncellenmesi gerekiyor. 5 saniye içinde güncelleme sayfasına yönlendirileceksiniz.",
+ "App upgrade" : "Uygulama güncellemesi",
"Approved" : "Onaylanmış",
"Experimental" : "Deneysel",
"No apps found for {query}" : "{query} aramasına uyan bir uygulama bulunamadı",
diff --git a/tests/lib/Preview/GeneratorTest.php b/tests/lib/Preview/GeneratorTest.php
index f1383b0691b..130cccdf09e 100644
--- a/tests/lib/Preview/GeneratorTest.php
+++ b/tests/lib/Preview/GeneratorTest.php
@@ -93,6 +93,8 @@ class GeneratorTest extends \Test\TestCase {
$maxPreview = $this->createMock(ISimpleFile::class);
$maxPreview->method('getName')
->willReturn('1000-1000-max.png');
+ $maxPreview->method('getMimeType')
+ ->willReturn('image/png');
$previewFolder->method('getDirectoryListing')
->willReturn([$maxPreview]);
@@ -170,6 +172,7 @@ class GeneratorTest extends \Test\TestCase {
$image->method('width')->willReturn(2048);
$image->method('height')->willReturn(2048);
$image->method('valid')->willReturn(true);
+ $image->method('dataMimeType')->willReturn('image/png');
$this->helper->method('getThumbnail')
->will($this->returnCallback(function ($provider, $file, $x, $y) use ($invalidProvider, $validProvider, $image) {
@@ -185,6 +188,7 @@ class GeneratorTest extends \Test\TestCase {
$maxPreview = $this->createMock(ISimpleFile::class);
$maxPreview->method('getName')->willReturn('2048-2048-max.png');
+ $maxPreview->method('getMimeType')->willReturn('image/png');
$previewFile = $this->createMock(ISimpleFile::class);
@@ -219,6 +223,7 @@ class GeneratorTest extends \Test\TestCase {
$image->method('data')
->willReturn('my resized data');
$image->method('valid')->willReturn(true);
+ $image->method('dataMimeType')->willReturn('image/png');
$previewFile->expects($this->once())
->method('putContent')
@@ -362,6 +367,8 @@ class GeneratorTest extends \Test\TestCase {
$maxPreview = $this->createMock(ISimpleFile::class);
$maxPreview->method('getName')
->willReturn($maxX . '-' . $maxY . '-max.png');
+ $maxPreview->method('getMimeType')
+ ->willReturn('image/png');
$previewFolder->method('getDirectoryListing')
->willReturn([$maxPreview]);
@@ -382,6 +389,7 @@ class GeneratorTest extends \Test\TestCase {
$image->method('height')->willReturn($maxY);
$image->method('width')->willReturn($maxX);
$image->method('valid')->willReturn(true);
+ $image->method('dataMimeType')->willReturn('image/png');
$preview = $this->createMock(ISimpleFile::class);
$previewFolder->method('newFile')