diff options
136 files changed, 1480 insertions, 421 deletions
diff --git a/3rdparty b/3rdparty -Subproject 696f7683651fa2ee1f59cbde08c6f5fefbcaad0 +Subproject 3cec97e2cbdbac0a07dc6269e7fc80048596b41 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/dav/lib/Upload/UploadHome.php b/apps/dav/lib/Upload/UploadHome.php index 9f4ae0e8a2c..ec31e93f4f9 100644 --- a/apps/dav/lib/Upload/UploadHome.php +++ b/apps/dav/lib/Upload/UploadHome.php @@ -66,7 +66,8 @@ class UploadHome implements ICollection { } function getName() { - return 'uploads'; + list(,$name) = \Sabre\Uri\split($this->principalInfo['uri']); + return $name; } function setName($name) { diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 8e95abcb5e8..ad56492730e 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -235,7 +235,7 @@ OC.FileUpload.prototype = { ) { data.isChunked = true; chunkFolderPromise = this.uploader.davClient.createDirectory( - 'uploads/' + encodeURIComponent(OC.getCurrentUser().uid) + '/' + encodeURIComponent(this.getId()) + 'uploads/' + OC.getCurrentUser().uid + '/' + this.getId() ); // TODO: if fails, it means same id already existed, need to retry } else { @@ -272,8 +272,8 @@ OC.FileUpload.prototype = { } return this.uploader.davClient.move( - 'uploads/' + encodeURIComponent(uid) + '/' + encodeURIComponent(this.getId()) + '/.file', - 'files/' + encodeURIComponent(uid) + '/' + OC.joinPaths(this.getFullPath(), this.getFileName()), + 'uploads/' + uid + '/' + this.getId() + '/.file', + 'files/' + uid + '/' + OC.joinPaths(this.getFullPath(), this.getFileName()), true, headers ); @@ -282,7 +282,7 @@ OC.FileUpload.prototype = { _deleteChunkFolder: function() { // delete transfer directory for this upload this.uploader.davClient.remove( - 'uploads/' + encodeURIComponent(OC.getCurrentUser().uid) + '/' + encodeURIComponent(this.getId()) + 'uploads/' + OC.getCurrentUser().uid + '/' + this.getId() ); }, @@ -1166,9 +1166,9 @@ OC.Uploader.prototype = _.extend({ var chunkId = range.split('/')[0].split('-')[0]; data.url = OC.getRootPath() + '/remote.php/dav/uploads' + - '/' + encodeURIComponent(OC.getCurrentUser().uid) + - '/' + encodeURIComponent(upload.getId()) + - '/' + encodeURIComponent(chunkId); + '/' + OC.getCurrentUser().uid + + '/' + upload.getId() + + '/' + chunkId; delete data.contentRange; delete data.headers['Content-Range']; }); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 7a4ea492752..e50b402dea8 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -2266,7 +2266,7 @@ function updateInList(fileInfo) { self.updateRow(tr, fileInfo); - self._updateDetailsView(fileInfo, false); + self._updateDetailsView(fileInfo.name, false); } // TODO: too many nested blocks, move parts into functions 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/en_GB.js b/apps/files/l10n/en_GB.js index 424d470d970..21f6e339784 100644 --- a/apps/files/l10n/en_GB.js +++ b/apps/files/l10n/en_GB.js @@ -62,8 +62,11 @@ OC.L10N.register( "You don’t have permission to upload or create files here" : "You don’t have permission to upload or create files here", "_Uploading %n file_::_Uploading %n files_" : ["Uploading %n file","Uploading %n files"], "New" : "New", + "{used} of {quota} used" : "{used} of {quota} used", + "{used} used" : "{used} used", "\"{name}\" is an invalid file name." : "\"{name}\" is an invalid file name.", "File name cannot be empty." : "File name cannot be empty.", + "\"/\" is not allowed inside a file name." : "\"/\" is not allowed inside a file name.", "\"{name}\" is not an allowed filetype" : "\"{name}\" is not an allowed filetype", "Storage of {owner} is full, files can not be updated or synced anymore!" : "Storage of {owner} is full, files can not be updated or synced anymore!", "Your storage is full, files can not be updated or synced anymore!" : "Your storage is full, files can not be updated or synced anymore!", diff --git a/apps/files/l10n/en_GB.json b/apps/files/l10n/en_GB.json index 1ac5d53c7a6..f8ff0a553b5 100644 --- a/apps/files/l10n/en_GB.json +++ b/apps/files/l10n/en_GB.json @@ -60,8 +60,11 @@ "You don’t have permission to upload or create files here" : "You don’t have permission to upload or create files here", "_Uploading %n file_::_Uploading %n files_" : ["Uploading %n file","Uploading %n files"], "New" : "New", + "{used} of {quota} used" : "{used} of {quota} used", + "{used} used" : "{used} used", "\"{name}\" is an invalid file name." : "\"{name}\" is an invalid file name.", "File name cannot be empty." : "File name cannot be empty.", + "\"/\" is not allowed inside a file name." : "\"/\" is not allowed inside a file name.", "\"{name}\" is not an allowed filetype" : "\"{name}\" is not an allowed filetype", "Storage of {owner} is full, files can not be updated or synced anymore!" : "Storage of {owner} is full, files can not be updated or synced anymore!", "Your storage is full, files can not be updated or synced anymore!" : "Your storage is full, files can not be updated or synced anymore!", 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/fr.js b/apps/files/l10n/fr.js index b5035e6a1a3..ccce056d631 100644 --- a/apps/files/l10n/fr.js +++ b/apps/files/l10n/fr.js @@ -62,9 +62,11 @@ OC.L10N.register( "You don’t have permission to upload or create files here" : "Vous n'avez pas la permission d'envoyer ou de créer des fichiers ici", "_Uploading %n file_::_Uploading %n files_" : ["Téléversement de %n fichier","Téléversement de %n fichiers"], "New" : "Nouveau", + "{used} of {quota} used" : "{used} de {quota} utilisé", "{used} used" : "{used} utilisé", "\"{name}\" is an invalid file name." : "\"{name}\" n'est pas un nom de fichier valide.", "File name cannot be empty." : "Le nom de fichier ne peut être vide.", + "\"/\" is not allowed inside a file name." : "\"/\" n'est pas autorisé dans les noms de fichier.", "\"{name}\" is not an allowed filetype" : "\"{name}\" n'est pas un nom de fichier autorisé.", "Storage of {owner} is full, files can not be updated or synced anymore!" : "L'espace de stockage de {owner} est plein. Les fichiers ne peuvent plus être mis à jour ni synchronisés !", "Your storage is full, files can not be updated or synced anymore!" : "Votre espace de stockage est plein. Les fichiers ne peuvent plus être mis à jour ni synchronisés !", diff --git a/apps/files/l10n/fr.json b/apps/files/l10n/fr.json index d65a471164e..d1e1320c0dc 100644 --- a/apps/files/l10n/fr.json +++ b/apps/files/l10n/fr.json @@ -60,9 +60,11 @@ "You don’t have permission to upload or create files here" : "Vous n'avez pas la permission d'envoyer ou de créer des fichiers ici", "_Uploading %n file_::_Uploading %n files_" : ["Téléversement de %n fichier","Téléversement de %n fichiers"], "New" : "Nouveau", + "{used} of {quota} used" : "{used} de {quota} utilisé", "{used} used" : "{used} utilisé", "\"{name}\" is an invalid file name." : "\"{name}\" n'est pas un nom de fichier valide.", "File name cannot be empty." : "Le nom de fichier ne peut être vide.", + "\"/\" is not allowed inside a file name." : "\"/\" n'est pas autorisé dans les noms de fichier.", "\"{name}\" is not an allowed filetype" : "\"{name}\" n'est pas un nom de fichier autorisé.", "Storage of {owner} is full, files can not be updated or synced anymore!" : "L'espace de stockage de {owner} est plein. Les fichiers ne peuvent plus être mis à jour ni synchronisés !", "Your storage is full, files can not be updated or synced anymore!" : "Votre espace de stockage est plein. Les fichiers ne peuvent plus être mis à jour ni synchronisés !", diff --git a/apps/files/l10n/ka_GE.js b/apps/files/l10n/ka_GE.js index 401b93a0e6a..b2b68baa747 100644 --- a/apps/files/l10n/ka_GE.js +++ b/apps/files/l10n/ka_GE.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 ფაილს"], "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/ka_GE.json b/apps/files/l10n/ka_GE.json index 36f2400edd8..5c496dc08cf 100644 --- a/apps/files/l10n/ka_GE.json +++ b/apps/files/l10n/ka_GE.json @@ -60,8 +60,11 @@ "You don’t have permission to upload or create files here" : "აქ ფაილების შექმნის ან ატვირთვის უფლება არ გაქვთ", "_Uploading %n file_::_Uploading %n files_" : ["ვტვირთავთ %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/ko.js b/apps/files/l10n/ko.js index 8b6ab2b51b0..cfe3b955df7 100644 --- a/apps/files/l10n/ko.js +++ b/apps/files/l10n/ko.js @@ -13,9 +13,9 @@ OC.L10N.register( "Could not create folder \"{dir}\"" : "폴더 \"{dir}\"을(를) 만들 수 없음", "Upload cancelled." : "업로드가 취소되었습니다.", "Unable to upload {filename} as it is a directory or has 0 bytes" : "{filename}을(를) 업로드할 수 없습니다. 폴더이거나 0 바이트 파일입니다.", - "Not enough free space, you are uploading {size1} but only {size2} is left" : "빈 공간이 부족합니다. 업로드할 파일 크기는 {size1}이지만 현재 {size2}만큼 비었습니다", + "Not enough free space, you are uploading {size1} but only {size2} is left" : "남은 공간이 부족합니다. 업로드할 파일 크기는 {size1}이지만 현재 {size2}만큼 남아 있습니다", "Target folder \"{dir}\" does not exist any more" : "대상 폴더 \"{dir}\"이(가) 더 이상 존재하지 않습니다", - "Not enough free space" : "빈 공간이 부족합니다", + "Not enough free space" : "남은 공간이 부족함", "Uploading …" : "업로드 중…", "…" : "…", "{loadedSize} of {totalSize} ({bitrate})" : "{loadedSize}/{totalSize}({bitrate})", diff --git a/apps/files/l10n/ko.json b/apps/files/l10n/ko.json index b9ea23ab0b9..0854c201bf9 100644 --- a/apps/files/l10n/ko.json +++ b/apps/files/l10n/ko.json @@ -11,9 +11,9 @@ "Could not create folder \"{dir}\"" : "폴더 \"{dir}\"을(를) 만들 수 없음", "Upload cancelled." : "업로드가 취소되었습니다.", "Unable to upload {filename} as it is a directory or has 0 bytes" : "{filename}을(를) 업로드할 수 없습니다. 폴더이거나 0 바이트 파일입니다.", - "Not enough free space, you are uploading {size1} but only {size2} is left" : "빈 공간이 부족합니다. 업로드할 파일 크기는 {size1}이지만 현재 {size2}만큼 비었습니다", + "Not enough free space, you are uploading {size1} but only {size2} is left" : "남은 공간이 부족합니다. 업로드할 파일 크기는 {size1}이지만 현재 {size2}만큼 남아 있습니다", "Target folder \"{dir}\" does not exist any more" : "대상 폴더 \"{dir}\"이(가) 더 이상 존재하지 않습니다", - "Not enough free space" : "빈 공간이 부족합니다", + "Not enough free space" : "남은 공간이 부족함", "Uploading …" : "업로드 중…", "…" : "…", "{loadedSize} of {totalSize} ({bitrate})" : "{loadedSize}/{totalSize}({bitrate})", 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/nl.js b/apps/files/l10n/nl.js index 73a52503cef..5ee5d8c6181 100644 --- a/apps/files/l10n/nl.js +++ b/apps/files/l10n/nl.js @@ -62,8 +62,11 @@ OC.L10N.register( "You don’t have permission to upload or create files here" : "Je hebt geen toestemming om hier te uploaden of bestanden te maken", "_Uploading %n file_::_Uploading %n files_" : ["%n bestand aan het uploaden","%n bestanden aan het uploaden"], "New" : "Nieuw", + "{used} of {quota} used" : "{used} van {quota} gebruikt", + "{used} used" : "{used} gebruikt", "\"{name}\" is an invalid file name." : "\"{name}\" is een ongeldige bestandsnaam.", "File name cannot be empty." : "Bestandsnaam kan niet leeg zijn.", + "\"/\" is not allowed inside a file name." : "\"/\" is niet toegestaan binnen een bestandsnaam.", "\"{name}\" is not an allowed filetype" : "\"{name}\" bestandstype is een niet toegestaande", "Storage of {owner} is full, files can not be updated or synced anymore!" : "Opslagruimte van {owner} zit vol, bestanden kunnen niet meer worden geüpload of gesynchroniseerd!", "Your storage is full, files can not be updated or synced anymore!" : "Je opslagruimte zit vol. Bestanden kunnen niet meer worden gewijzigd of gesynchroniseerd!", diff --git a/apps/files/l10n/nl.json b/apps/files/l10n/nl.json index 55c83f970ba..2cc15f1f564 100644 --- a/apps/files/l10n/nl.json +++ b/apps/files/l10n/nl.json @@ -60,8 +60,11 @@ "You don’t have permission to upload or create files here" : "Je hebt geen toestemming om hier te uploaden of bestanden te maken", "_Uploading %n file_::_Uploading %n files_" : ["%n bestand aan het uploaden","%n bestanden aan het uploaden"], "New" : "Nieuw", + "{used} of {quota} used" : "{used} van {quota} gebruikt", + "{used} used" : "{used} gebruikt", "\"{name}\" is an invalid file name." : "\"{name}\" is een ongeldige bestandsnaam.", "File name cannot be empty." : "Bestandsnaam kan niet leeg zijn.", + "\"/\" is not allowed inside a file name." : "\"/\" is niet toegestaan binnen een bestandsnaam.", "\"{name}\" is not an allowed filetype" : "\"{name}\" bestandstype is een niet toegestaande", "Storage of {owner} is full, files can not be updated or synced anymore!" : "Opslagruimte van {owner} zit vol, bestanden kunnen niet meer worden geüpload of gesynchroniseerd!", "Your storage is full, files can not be updated or synced anymore!" : "Je opslagruimte zit vol. Bestanden kunnen niet meer worden gewijzigd of gesynchroniseerd!", diff --git a/apps/files/l10n/ro.js b/apps/files/l10n/ro.js index e7b93b7b005..285604b1ea1 100644 --- a/apps/files/l10n/ro.js +++ b/apps/files/l10n/ro.js @@ -1,10 +1,12 @@ OC.L10N.register( "files", { + "Storage is temporarily not available" : "Spațiu de stocare este indisponibil temporar", "Storage invalid" : "Spațiu de stocare invalid", "Unknown error" : "Eroare necunoscută", - "Files" : "Fișiere", - "All files" : "Toate fișierele.", + "All files" : "Toate fișierele", + "Recent" : "Recente", + "File could not be found" : "Fișierul nu a fost găsit", "Home" : "Acasă", "Close" : "Închide", "Favorites" : "Favorite", @@ -12,21 +14,21 @@ OC.L10N.register( "Upload cancelled." : "Încărcare anulată.", "Unable to upload {filename} as it is a directory or has 0 bytes" : "Nu se poate încărca {filename} deoarece este un director sau are mărimea de 0 octeți", "Not enough free space, you are uploading {size1} but only {size2} is left" : "Spațiu liber insuficient, încărcați {size1} însă doar {size2} disponibil rămas", - "Uploading..." : "Încărcare", - "..." : "...", - "{hours}:{minutes}h" : "{hours}:{minutes}h", - "{minutes}:{seconds}m" : "{minutes}:{seconds}m", - "{seconds} second{plural_s} left" : "{seconds} secunde{plural_s} rămase", - "{seconds}s" : "{seconds}s", - "Any moment now..." : "În orice moment...", - "Soon..." : "În curând...", - "File upload is in progress. Leaving the page now will cancel the upload." : "Fișierul este în curs de încărcare. Părăsirea paginii va întrerupe încărcarea.", + "Target folder \"{dir}\" does not exist any more" : "Directorul \"{dir}\" nu mai există", + "Not enough free space" : "Spațiu insuficient", + "Uploading …" : "Încărcare...", + "…" : "...", + "{loadedSize} of {totalSize} ({bitrate})" : "{loadedSize} din {totalSize} ({bitrate})", + "Target folder does not exist any more" : "Directorul destinație nu mai există", "Actions" : "Acțiuni", "Download" : "Descarcă", "Rename" : "Redenumește", + "Move or copy" : "Mută sau copiază", + "Target folder" : "Directorul destinație", "Delete" : "Șterge", "Disconnect storage" : "Deconectează stocarea", "Unshare" : "Nu mai partaja", + "Files" : "Fișiere", "Details" : "Detalii", "Select" : "Alege", "Pending" : "În așteptare", @@ -35,6 +37,8 @@ OC.L10N.register( "This directory is unavailable, please check the logs or contact the administrator" : "Acest director nu este disponibil, te rugăm verifică logurile sau contactează un administrator", "Could not move \"{file}\", target exists" : "Nu s-a putut muta fișierul \"{file}\", există deja un altul cu același nume în directorul destinație", "Could not move \"{file}\"" : "Nu s-a putut muta fișierul \"{file}\"", + "Could not copy \"{file}\"" : "Nu s-a putut copia \"{file}\"", + "Copied {origin} inside {destination}" : "S-a copiat {origin} în {destination}", "{newName} already exists" : "{newName} există deja", "Could not rename \"{fileName}\", it does not exist any more" : "Nu s-a putut redenumi \"{fileName}\", fișierul nu mai există", "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Numele \"{targetName}\" este deja folosit în directorul \"{dir}\". Alege un nume diferit.", @@ -52,73 +56,98 @@ OC.L10N.register( "You don’t have permission to upload or create files here" : "Nu aveți permisiunea de a încărca sau crea fișiere aici", "_Uploading %n file_::_Uploading %n files_" : ["Se încarcă %n fișier.","Se încarcă %n fișiere.","Se încarcă %n fișiere."], "New" : "Nou", + "{used} of {quota} used" : "{used} din {quota} folosiți", + "{used} used" : "{used} folosiți", "\"{name}\" is an invalid file name." : "\"{name}\" este un nume de fișier nevalid.", "File name cannot be empty." : "Numele fișierului nu poate rămâne gol.", + "\"{name}\" is not an allowed filetype" : "\"{name}\" ", + "Storage of {owner} is full, files can not be updated or synced anymore!" : "Spațiul de stocare pentru {owner} este plin, fișierele nu mai pot fi incărcate sau sincronizate!", "Your storage is full, files can not be updated or synced anymore!" : "Spațiul de stocare este plin, fișierele nu mai pot fi actualizate sau sincronizate!", + "Storage of {owner} is almost full ({usedSpacePercent}%)" : "Spațiul de stocare pentru {owner} este aproape plin ({usedSpacePercent}%)", "Your storage is almost full ({usedSpacePercent}%)" : "Spațiul de stocare este aproape plin ({usedSpacePercent}%)", + "Copied!" : "S-a copiat!", + "Copy direct link (only works for users who have access to this file/folder)" : "Copiază link direct (funcționeză numai pentru utilizatorii care au acces la acest fișier/director)", "Path" : "Cale", "_%n byte_::_%n bytes_" : ["%n octet","%n octeți","%n octeți"], "Favorited" : "Adăugate la favorite", "Favorite" : "Favorit", - "Local link" : "Legătură locală", - "Folder" : "Dosar", "New folder" : "Un nou dosar", - "Upload" : "Încărcă", + "Upload file" : "Încarcă fișier", + "Remove from favorites" : "Șterge din favorite", + "Add to favorites" : "Adaugă la favorite", "An error occurred while trying to update the tags" : "A apărut o eroare în timpul actualizării etichetelor", - "A new file or folder has been <strong>created</strong>" : "Un nou fișier sau dosar a fost <strong>creat</strong>", - "A file or folder has been <strong>deleted</strong>" : "Un nou fișier sau dosar a fost <strong>șters</strong>", + "Added to favorites" : "Adaugat la favorite", + "Removed from favorites" : "Șters din favorite", + "You added {file} to your favorites" : "Ai adaugat {file} în favorite", + "You removed {file} from your favorites" : "Ai șters {file} din favorite", + "Created by {user}" : "Creat de {user}", + "Changed by {user}" : "Modificat de {user}", + "Deleted by {user}" : "Șters de {user}", + "Restored by {user}" : "Restaurat de {user}", + "Renamed by {user}" : "Redenumit de {user}", + "Moved by {user}" : "Mutat de {user}", + "\"remote user\"" : "\"utilizator la distanță\"", + "You created {file}" : "Ai creat {file}", + "{user} created {file}" : "{user} a creat {file}", + "{file} was created in a public folder" : "{file} a fost creat într-un director public ", + "You changed {file}" : "Ai modificat {file}", + "{user} changed {file}" : "{user} a modificat {file}", + "You deleted {file}" : "Ai șters {file}", + "{user} deleted {file}" : "{user} a șters {file}", + "You restored {file}" : "Ai restaurat {file}", + "{user} restored {file}" : "{user} a restaurat {file}", + "You renamed {oldfile} to {newfile}" : "Ai redenumit {oldfile} în {newfile}", + "{user} renamed {oldfile} to {newfile}" : "{user} a redenumit {oldfile} în {newfile}", + "You moved {oldfile} to {newfile}" : "Ai mutat{oldfile} în {newfile}", + "{user} moved {oldfile} to {newfile}" : "{user} a mutat {oldfile} în {newfile}", + "A file has been added to or removed from your <strong>favorites</strong>" : "Un fișier a fost adăugat în sau șters din <strong>favorites</strong>", + "A file or folder has been <strong>changed</strong> or <strong>renamed</strong>" : "Un fișier sau director a fost <strong>modificat</strong> sau <strong>redenumit</strong>", + "A new file or folder has been <strong>created</strong>" : "Un nou fișier sau director a fost <strong>creat</strong>", + "A file or folder has been <strong>deleted</strong>" : "Un fișier sau director a fost <strong>șters</strong>", "A file or folder has been <strong>restored</strong>" : "Un fișier sau dosar a fost <strong>restaurat</strong>", - "You created %1$s" : "Ai creat %1$s", - "%2$s created %1$s" : "%2$s a creat %1$s", - "%1$s was created in a public folder" : "%1$s a fost creat în dosarul public", - "You changed %1$s" : "Ai schimbat %1$s", - "%2$s changed %1$s" : "%2$s a schimbat %1$s", - "You deleted %1$s" : "Ai şters %1$s", - "%2$s deleted %1$s" : "%2$s a șters %1$s", - "You restored %1$s" : "Ai restaurat %1$s", - "%2$s restored %1$s" : "%2$s a restaurat %1$s", - "Changed by %2$s" : "Modificat de %2$s", - "Deleted by %2$s" : "Șters de %2$s", - "Restored by %2$s" : "Restaurat de %2$s", "Upload (max. %s)" : "Încarcă (max. %s)", "File handling" : "Manipulare fișiere", "Maximum upload size" : "Dimensiune maximă admisă la încărcare", "max. possible: " : "max. posibil:", "Save" : "Salvează", + "With PHP-FPM it might take 5 minutes for changes to be applied." : "Cu PHP-FPM poate dura 5 minute pentru a aplica schimbările..", "Missing permissions to edit from here." : "Nu ai permisiuni pentru a edita aici.", + "%s of %s used" : "%s din %s folosiți", + "%s used" : "%s folosiți", "Settings" : "Setări", "Show hidden files" : "Arată fișierele ascunse", "WebDAV" : "WebDAV", + "Use this address to <a href=\"%s\" target=\"_blank\" rel=\"noreferrer noopener\">access your Files via WebDAV</a>" : "Folosește această adresă <a href=\"%s\" target=\"_blank\" rel=\"noreferrer noopener\">pentru a accesa Fișierele prin WebDAV</a>", + "Cancel upload" : "Anulează încărcarea", "No files in here" : "Niciun fișier aici", "Upload some content or sync with your devices!" : "Încarcă ceva conținut sau sincronizează cu dispozitivele tale!", "No entries found in this folder" : "Niciun element găsit în acest director", "Select all" : "Selectează tot", "Upload too large" : "Fișierul încărcat este prea mare", "The files you are trying to upload exceed the maximum size for file uploads on this server." : "Fișierele pe care încerci să le încarci depășesc limita de încărcare maximă admisă pe acest server.", - "No favorites" : "Fără favorite", + "No favorites yet" : "Nu aveți favorite încă", "Files and folders you mark as favorite will show up here" : "FIșierele și directoarele pe care le marchezi ca favorite vor apărea aici", + "Shared with you" : "Partajat cu tine", + "Shared with others" : "Partajat cu alții", + "Shared by link" : "Partajat prin link", + "Tags" : "Etichete", + "Deleted files" : "Fișiere șterse", "Text file" : "Fișier text", "New text file.txt" : "New text file.txt", - "Storage not available" : "Spațiu de stocare indisponibil", - "Unable to set upload directory." : "Imposibil de a seta directorul pentru încărcare.", - "Invalid Token" : "Jeton Invalid", - "No file was uploaded. Unknown error" : "Niciun fișier nu a fost încărcat. Eroare necunoscută", - "There is no error, the file uploaded with success" : "Nu a apărut nici o eroare, fișierul a fost încărcat cu succes", - "The uploaded file exceeds the upload_max_filesize directive in php.ini: " : "Fișierul încărcat depășește directiva upload_max_filesize din php.ini:", - "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Fișierul încărcat depășește directiva MAX_FILE_SIZE specificată în formularul HTML", - "The uploaded file was only partially uploaded" : "Fișierul a fost încărcat doar parțial", - "No file was uploaded" : "Nu a fost încărcat niciun fișier", - "Missing a temporary folder" : "Lipsește un dosar temporar", - "Failed to write to disk" : "Eroare la scrierea pe disc", - "Not enough storage available" : "Nu este disponibil suficient spațiu pe disc", - "The target folder has been moved or deleted." : "Dosarul țintă a fost mutat sau șters.", - "Upload failed. Could not find uploaded file" : "Încărcare eșuată. Nu se poate găsi fișierul încărcat", - "Upload failed. Could not get file info." : "Încărcare eșuată. Nu se pot obține informații despre fișier.", - "Invalid directory." : "Dosar invalid.", - "Total file size {size1} exceeds upload limit {size2}" : "Mărimea fișierului este {size1} ce depășește limita de încărcare de {size2}", - "Could not get result from server." : "Nu se poate obține rezultatul de la server.", - "No entries in this folder match '{filter}'" : "Niciun element din acest director nu se potrivește cu '{filter}'", - "{newname} already exists" : "{newname} există deja", - "A file or folder has been <strong>changed</strong>" : "Un nou fișier sau dosar a fost <strong>modificat</strong>" + "Uploading..." : "Încărcare", + "..." : "...", + "{hours}:{minutes}h" : "{hours}:{minutes}h", + "{minutes}:{seconds}m" : "{minutes}:{seconds}m", + "{seconds}s" : "{seconds}s", + "Any moment now..." : "În orice moment...", + "Soon..." : "În curând...", + "File upload is in progress. Leaving the page now will cancel the upload." : "Fișierul este în curs de încărcare. Părăsirea paginii va întrerupe încărcarea.", + "Move" : "Mută", + "Folder" : "Dosar", + "Upload" : "Încărcă", + "A new file or folder has been <strong>deleted</strong>" : "Un nou fișier sau director a fost <strong>șters</strong>", + "A new file or folder has been <strong>restored</strong>" : "Un fișier sau director a fost <strong>restaurat</strong>", + "Use this address to <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">access your Files via WebDAV</a>" : "Folosește această adresă <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">pentru a accesa Fișierele prin WebDAV</a>", + "No favorites" : "Fără favorite" }, "nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));"); diff --git a/apps/files/l10n/ro.json b/apps/files/l10n/ro.json index 45c55d2becc..8d41bb52adc 100644 --- a/apps/files/l10n/ro.json +++ b/apps/files/l10n/ro.json @@ -1,8 +1,10 @@ { "translations": { + "Storage is temporarily not available" : "Spațiu de stocare este indisponibil temporar", "Storage invalid" : "Spațiu de stocare invalid", "Unknown error" : "Eroare necunoscută", - "Files" : "Fișiere", - "All files" : "Toate fișierele.", + "All files" : "Toate fișierele", + "Recent" : "Recente", + "File could not be found" : "Fișierul nu a fost găsit", "Home" : "Acasă", "Close" : "Închide", "Favorites" : "Favorite", @@ -10,21 +12,21 @@ "Upload cancelled." : "Încărcare anulată.", "Unable to upload {filename} as it is a directory or has 0 bytes" : "Nu se poate încărca {filename} deoarece este un director sau are mărimea de 0 octeți", "Not enough free space, you are uploading {size1} but only {size2} is left" : "Spațiu liber insuficient, încărcați {size1} însă doar {size2} disponibil rămas", - "Uploading..." : "Încărcare", - "..." : "...", - "{hours}:{minutes}h" : "{hours}:{minutes}h", - "{minutes}:{seconds}m" : "{minutes}:{seconds}m", - "{seconds} second{plural_s} left" : "{seconds} secunde{plural_s} rămase", - "{seconds}s" : "{seconds}s", - "Any moment now..." : "În orice moment...", - "Soon..." : "În curând...", - "File upload is in progress. Leaving the page now will cancel the upload." : "Fișierul este în curs de încărcare. Părăsirea paginii va întrerupe încărcarea.", + "Target folder \"{dir}\" does not exist any more" : "Directorul \"{dir}\" nu mai există", + "Not enough free space" : "Spațiu insuficient", + "Uploading …" : "Încărcare...", + "…" : "...", + "{loadedSize} of {totalSize} ({bitrate})" : "{loadedSize} din {totalSize} ({bitrate})", + "Target folder does not exist any more" : "Directorul destinație nu mai există", "Actions" : "Acțiuni", "Download" : "Descarcă", "Rename" : "Redenumește", + "Move or copy" : "Mută sau copiază", + "Target folder" : "Directorul destinație", "Delete" : "Șterge", "Disconnect storage" : "Deconectează stocarea", "Unshare" : "Nu mai partaja", + "Files" : "Fișiere", "Details" : "Detalii", "Select" : "Alege", "Pending" : "În așteptare", @@ -33,6 +35,8 @@ "This directory is unavailable, please check the logs or contact the administrator" : "Acest director nu este disponibil, te rugăm verifică logurile sau contactează un administrator", "Could not move \"{file}\", target exists" : "Nu s-a putut muta fișierul \"{file}\", există deja un altul cu același nume în directorul destinație", "Could not move \"{file}\"" : "Nu s-a putut muta fișierul \"{file}\"", + "Could not copy \"{file}\"" : "Nu s-a putut copia \"{file}\"", + "Copied {origin} inside {destination}" : "S-a copiat {origin} în {destination}", "{newName} already exists" : "{newName} există deja", "Could not rename \"{fileName}\", it does not exist any more" : "Nu s-a putut redenumi \"{fileName}\", fișierul nu mai există", "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Numele \"{targetName}\" este deja folosit în directorul \"{dir}\". Alege un nume diferit.", @@ -50,73 +54,98 @@ "You don’t have permission to upload or create files here" : "Nu aveți permisiunea de a încărca sau crea fișiere aici", "_Uploading %n file_::_Uploading %n files_" : ["Se încarcă %n fișier.","Se încarcă %n fișiere.","Se încarcă %n fișiere."], "New" : "Nou", + "{used} of {quota} used" : "{used} din {quota} folosiți", + "{used} used" : "{used} folosiți", "\"{name}\" is an invalid file name." : "\"{name}\" este un nume de fișier nevalid.", "File name cannot be empty." : "Numele fișierului nu poate rămâne gol.", + "\"{name}\" is not an allowed filetype" : "\"{name}\" ", + "Storage of {owner} is full, files can not be updated or synced anymore!" : "Spațiul de stocare pentru {owner} este plin, fișierele nu mai pot fi incărcate sau sincronizate!", "Your storage is full, files can not be updated or synced anymore!" : "Spațiul de stocare este plin, fișierele nu mai pot fi actualizate sau sincronizate!", + "Storage of {owner} is almost full ({usedSpacePercent}%)" : "Spațiul de stocare pentru {owner} este aproape plin ({usedSpacePercent}%)", "Your storage is almost full ({usedSpacePercent}%)" : "Spațiul de stocare este aproape plin ({usedSpacePercent}%)", + "Copied!" : "S-a copiat!", + "Copy direct link (only works for users who have access to this file/folder)" : "Copiază link direct (funcționeză numai pentru utilizatorii care au acces la acest fișier/director)", "Path" : "Cale", "_%n byte_::_%n bytes_" : ["%n octet","%n octeți","%n octeți"], "Favorited" : "Adăugate la favorite", "Favorite" : "Favorit", - "Local link" : "Legătură locală", - "Folder" : "Dosar", "New folder" : "Un nou dosar", - "Upload" : "Încărcă", + "Upload file" : "Încarcă fișier", + "Remove from favorites" : "Șterge din favorite", + "Add to favorites" : "Adaugă la favorite", "An error occurred while trying to update the tags" : "A apărut o eroare în timpul actualizării etichetelor", - "A new file or folder has been <strong>created</strong>" : "Un nou fișier sau dosar a fost <strong>creat</strong>", - "A file or folder has been <strong>deleted</strong>" : "Un nou fișier sau dosar a fost <strong>șters</strong>", + "Added to favorites" : "Adaugat la favorite", + "Removed from favorites" : "Șters din favorite", + "You added {file} to your favorites" : "Ai adaugat {file} în favorite", + "You removed {file} from your favorites" : "Ai șters {file} din favorite", + "Created by {user}" : "Creat de {user}", + "Changed by {user}" : "Modificat de {user}", + "Deleted by {user}" : "Șters de {user}", + "Restored by {user}" : "Restaurat de {user}", + "Renamed by {user}" : "Redenumit de {user}", + "Moved by {user}" : "Mutat de {user}", + "\"remote user\"" : "\"utilizator la distanță\"", + "You created {file}" : "Ai creat {file}", + "{user} created {file}" : "{user} a creat {file}", + "{file} was created in a public folder" : "{file} a fost creat într-un director public ", + "You changed {file}" : "Ai modificat {file}", + "{user} changed {file}" : "{user} a modificat {file}", + "You deleted {file}" : "Ai șters {file}", + "{user} deleted {file}" : "{user} a șters {file}", + "You restored {file}" : "Ai restaurat {file}", + "{user} restored {file}" : "{user} a restaurat {file}", + "You renamed {oldfile} to {newfile}" : "Ai redenumit {oldfile} în {newfile}", + "{user} renamed {oldfile} to {newfile}" : "{user} a redenumit {oldfile} în {newfile}", + "You moved {oldfile} to {newfile}" : "Ai mutat{oldfile} în {newfile}", + "{user} moved {oldfile} to {newfile}" : "{user} a mutat {oldfile} în {newfile}", + "A file has been added to or removed from your <strong>favorites</strong>" : "Un fișier a fost adăugat în sau șters din <strong>favorites</strong>", + "A file or folder has been <strong>changed</strong> or <strong>renamed</strong>" : "Un fișier sau director a fost <strong>modificat</strong> sau <strong>redenumit</strong>", + "A new file or folder has been <strong>created</strong>" : "Un nou fișier sau director a fost <strong>creat</strong>", + "A file or folder has been <strong>deleted</strong>" : "Un fișier sau director a fost <strong>șters</strong>", "A file or folder has been <strong>restored</strong>" : "Un fișier sau dosar a fost <strong>restaurat</strong>", - "You created %1$s" : "Ai creat %1$s", - "%2$s created %1$s" : "%2$s a creat %1$s", - "%1$s was created in a public folder" : "%1$s a fost creat în dosarul public", - "You changed %1$s" : "Ai schimbat %1$s", - "%2$s changed %1$s" : "%2$s a schimbat %1$s", - "You deleted %1$s" : "Ai şters %1$s", - "%2$s deleted %1$s" : "%2$s a șters %1$s", - "You restored %1$s" : "Ai restaurat %1$s", - "%2$s restored %1$s" : "%2$s a restaurat %1$s", - "Changed by %2$s" : "Modificat de %2$s", - "Deleted by %2$s" : "Șters de %2$s", - "Restored by %2$s" : "Restaurat de %2$s", "Upload (max. %s)" : "Încarcă (max. %s)", "File handling" : "Manipulare fișiere", "Maximum upload size" : "Dimensiune maximă admisă la încărcare", "max. possible: " : "max. posibil:", "Save" : "Salvează", + "With PHP-FPM it might take 5 minutes for changes to be applied." : "Cu PHP-FPM poate dura 5 minute pentru a aplica schimbările..", "Missing permissions to edit from here." : "Nu ai permisiuni pentru a edita aici.", + "%s of %s used" : "%s din %s folosiți", + "%s used" : "%s folosiți", "Settings" : "Setări", "Show hidden files" : "Arată fișierele ascunse", "WebDAV" : "WebDAV", + "Use this address to <a href=\"%s\" target=\"_blank\" rel=\"noreferrer noopener\">access your Files via WebDAV</a>" : "Folosește această adresă <a href=\"%s\" target=\"_blank\" rel=\"noreferrer noopener\">pentru a accesa Fișierele prin WebDAV</a>", + "Cancel upload" : "Anulează încărcarea", "No files in here" : "Niciun fișier aici", "Upload some content or sync with your devices!" : "Încarcă ceva conținut sau sincronizează cu dispozitivele tale!", "No entries found in this folder" : "Niciun element găsit în acest director", "Select all" : "Selectează tot", "Upload too large" : "Fișierul încărcat este prea mare", "The files you are trying to upload exceed the maximum size for file uploads on this server." : "Fișierele pe care încerci să le încarci depășesc limita de încărcare maximă admisă pe acest server.", - "No favorites" : "Fără favorite", + "No favorites yet" : "Nu aveți favorite încă", "Files and folders you mark as favorite will show up here" : "FIșierele și directoarele pe care le marchezi ca favorite vor apărea aici", + "Shared with you" : "Partajat cu tine", + "Shared with others" : "Partajat cu alții", + "Shared by link" : "Partajat prin link", + "Tags" : "Etichete", + "Deleted files" : "Fișiere șterse", "Text file" : "Fișier text", "New text file.txt" : "New text file.txt", - "Storage not available" : "Spațiu de stocare indisponibil", - "Unable to set upload directory." : "Imposibil de a seta directorul pentru încărcare.", - "Invalid Token" : "Jeton Invalid", - "No file was uploaded. Unknown error" : "Niciun fișier nu a fost încărcat. Eroare necunoscută", - "There is no error, the file uploaded with success" : "Nu a apărut nici o eroare, fișierul a fost încărcat cu succes", - "The uploaded file exceeds the upload_max_filesize directive in php.ini: " : "Fișierul încărcat depășește directiva upload_max_filesize din php.ini:", - "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Fișierul încărcat depășește directiva MAX_FILE_SIZE specificată în formularul HTML", - "The uploaded file was only partially uploaded" : "Fișierul a fost încărcat doar parțial", - "No file was uploaded" : "Nu a fost încărcat niciun fișier", - "Missing a temporary folder" : "Lipsește un dosar temporar", - "Failed to write to disk" : "Eroare la scrierea pe disc", - "Not enough storage available" : "Nu este disponibil suficient spațiu pe disc", - "The target folder has been moved or deleted." : "Dosarul țintă a fost mutat sau șters.", - "Upload failed. Could not find uploaded file" : "Încărcare eșuată. Nu se poate găsi fișierul încărcat", - "Upload failed. Could not get file info." : "Încărcare eșuată. Nu se pot obține informații despre fișier.", - "Invalid directory." : "Dosar invalid.", - "Total file size {size1} exceeds upload limit {size2}" : "Mărimea fișierului este {size1} ce depășește limita de încărcare de {size2}", - "Could not get result from server." : "Nu se poate obține rezultatul de la server.", - "No entries in this folder match '{filter}'" : "Niciun element din acest director nu se potrivește cu '{filter}'", - "{newname} already exists" : "{newname} există deja", - "A file or folder has been <strong>changed</strong>" : "Un nou fișier sau dosar a fost <strong>modificat</strong>" + "Uploading..." : "Încărcare", + "..." : "...", + "{hours}:{minutes}h" : "{hours}:{minutes}h", + "{minutes}:{seconds}m" : "{minutes}:{seconds}m", + "{seconds}s" : "{seconds}s", + "Any moment now..." : "În orice moment...", + "Soon..." : "În curând...", + "File upload is in progress. Leaving the page now will cancel the upload." : "Fișierul este în curs de încărcare. Părăsirea paginii va întrerupe încărcarea.", + "Move" : "Mută", + "Folder" : "Dosar", + "Upload" : "Încărcă", + "A new file or folder has been <strong>deleted</strong>" : "Un nou fișier sau director a fost <strong>șters</strong>", + "A new file or folder has been <strong>restored</strong>" : "Un fișier sau director a fost <strong>restaurat</strong>", + "Use this address to <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">access your Files via WebDAV</a>" : "Folosește această adresă <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">pentru a accesa Fișierele prin WebDAV</a>", + "No favorites" : "Fără favorite" },"pluralForm" :"nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));" }
\ No newline at end of file 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/filelistSpec.js b/apps/files/tests/js/filelistSpec.js index fc5a6c18f95..08da15b8a88 100644 --- a/apps/files/tests/js/filelistSpec.js +++ b/apps/files/tests/js/filelistSpec.js @@ -612,6 +612,12 @@ describe('OCA.Files.FileList tests', function() { beforeEach(function() { deferredRename = $.Deferred(); renameStub = sinon.stub(filesClient, 'move').returns(deferredRename.promise()); + + for (var i = 0; i < testFiles.length; i++) { + var file = testFiles[i]; + file.path = '/some/subdir'; + fileList.add(file, {silent: true}); + } }); afterEach(function() { renameStub.restore(); @@ -619,9 +625,6 @@ describe('OCA.Files.FileList tests', function() { function doCancelRename() { var $input; - for (var i = 0; i < testFiles.length; i++) { - fileList.add(testFiles[i]); - } // trigger rename prompt fileList.rename('One.txt'); @@ -636,12 +639,6 @@ describe('OCA.Files.FileList tests', function() { function doRename() { var $input; - for (var i = 0; i < testFiles.length; i++) { - var file = testFiles[i]; - file.path = '/some/subdir'; - fileList.add(file, {silent: true}); - } - // trigger rename prompt fileList.rename('One.txt'); $input = fileList.$fileList.find('input.filename'); @@ -677,6 +674,36 @@ describe('OCA.Files.FileList tests', function() { expect(notificationStub.calledOnce).toEqual(true); }); + it('Shows renamed file details if rename ajax call suceeded', function() { + fileList.showDetailsView('One.txt'); + + expect($('#app-sidebar').hasClass('disappear')).toEqual(false); + expect(fileList._detailsView.getFileInfo().get('id')).toEqual(1); + expect(fileList._detailsView.getFileInfo().get('name')).toEqual('One.txt'); + + doRename(); + + deferredRename.resolve(201); + + expect($('#app-sidebar').hasClass('disappear')).toEqual(false); + expect(fileList._detailsView.getFileInfo().get('id')).toEqual(1); + expect(fileList._detailsView.getFileInfo().get('name')).toEqual('Tu_after_three.txt'); + }); + it('Shows again file details if rename ajax call failed', function() { + fileList.showDetailsView('One.txt'); + + expect($('#app-sidebar').hasClass('disappear')).toEqual(false); + expect(fileList._detailsView.getFileInfo().get('id')).toEqual(1); + expect(fileList._detailsView.getFileInfo().get('name')).toEqual('One.txt'); + + doRename(); + + deferredRename.reject(403); + + expect($('#app-sidebar').hasClass('disappear')).toEqual(false); + expect(fileList._detailsView.getFileInfo().get('id')).toEqual(1); + expect(fileList._detailsView.getFileInfo().get('name')).toEqual('One.txt'); + }); it('Correctly updates file link after rename', function() { var $tr; doRename(); @@ -731,10 +758,6 @@ describe('OCA.Files.FileList tests', function() { it('Validates the file name', function() { var $input, $tr; - for (var i = 0; i < testFiles.length; i++) { - fileList.add(testFiles[i], {silent: true}); - } - $tr = fileList.findFileEl('One.txt'); expect($tr.find('a.name').css('display')).not.toEqual('none'); 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/en_GB.js b/apps/files_external/l10n/en_GB.js index 131c9af43ad..45fe58c0b36 100644 --- a/apps/files_external/l10n/en_GB.js +++ b/apps/files_external/l10n/en_GB.js @@ -75,6 +75,7 @@ OC.L10N.register( "Region" : "Region", "Enable SSL" : "Enable SSL", "Enable Path Style" : "Enable Path Style", + "Legacy (v2) authentication" : "Legacy (v2) authentication", "WebDAV" : "WebDAV", "URL" : "URL", "Remote subfolder" : "Remote subfolder", diff --git a/apps/files_external/l10n/en_GB.json b/apps/files_external/l10n/en_GB.json index 5c60ec6e344..f7ac7290e7a 100644 --- a/apps/files_external/l10n/en_GB.json +++ b/apps/files_external/l10n/en_GB.json @@ -73,6 +73,7 @@ "Region" : "Region", "Enable SSL" : "Enable SSL", "Enable Path Style" : "Enable Path Style", + "Legacy (v2) authentication" : "Legacy (v2) authentication", "WebDAV" : "WebDAV", "URL" : "URL", "Remote subfolder" : "Remote subfolder", 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/hu.js b/apps/files_external/l10n/hu.js index 81eddd893a0..8dcecbaeffb 100644 --- a/apps/files_external/l10n/hu.js +++ b/apps/files_external/l10n/hu.js @@ -75,6 +75,7 @@ OC.L10N.register( "Region" : "Megye", "Enable SSL" : "SSL engedélyezése", "Enable Path Style" : "Útvonal stílus engedélyezés", + "Legacy (v2) authentication" : "Elavult (v2) authetikáció", "WebDAV" : "WebDAV", "URL" : "URL", "Remote subfolder" : "Távoli almappa", diff --git a/apps/files_external/l10n/hu.json b/apps/files_external/l10n/hu.json index f05459b00b9..fc51260454c 100644 --- a/apps/files_external/l10n/hu.json +++ b/apps/files_external/l10n/hu.json @@ -73,6 +73,7 @@ "Region" : "Megye", "Enable SSL" : "SSL engedélyezése", "Enable Path Style" : "Útvonal stílus engedélyezés", + "Legacy (v2) authentication" : "Elavult (v2) authetikáció", "WebDAV" : "WebDAV", "URL" : "URL", "Remote subfolder" : "Távoli almappa", 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/ka_GE.js b/apps/files_external/l10n/ka_GE.js index ff2f4e9afab..782242ae5dc 100644 --- a/apps/files_external/l10n/ka_GE.js +++ b/apps/files_external/l10n/ka_GE.js @@ -75,6 +75,7 @@ OC.L10N.register( "Region" : "რეგიონი", "Enable SSL" : "SSL-ის ამოქმედება", "Enable Path Style" : "ბილიკის სტილის ამოქმედება", + "Legacy (v2) authentication" : "ლეგატი (ვ2) აუტენტიფიკაცია", "WebDAV" : "WebDAV-ი", "URL" : "URL", "Remote subfolder" : "დისტანციური ქვე-დირექტორია", diff --git a/apps/files_external/l10n/ka_GE.json b/apps/files_external/l10n/ka_GE.json index 34f02298e85..7d9ebcdb3b1 100644 --- a/apps/files_external/l10n/ka_GE.json +++ b/apps/files_external/l10n/ka_GE.json @@ -73,6 +73,7 @@ "Region" : "რეგიონი", "Enable SSL" : "SSL-ის ამოქმედება", "Enable Path Style" : "ბილიკის სტილის ამოქმედება", + "Legacy (v2) authentication" : "ლეგატი (ვ2) აუტენტიფიკაცია", "WebDAV" : "WebDAV-ი", "URL" : "URL", "Remote subfolder" : "დისტანციური ქვე-დირექტორია", diff --git a/apps/files_external/l10n/ko.js b/apps/files_external/l10n/ko.js index 192c5a1c5c0..f91f5f72320 100644 --- a/apps/files_external/l10n/ko.js +++ b/apps/files_external/l10n/ko.js @@ -75,6 +75,7 @@ OC.L10N.register( "Region" : "지역", "Enable SSL" : "SSL 사용", "Enable Path Style" : "경로 스타일 사용", + "Legacy (v2) authentication" : "레거시(v2) 인증", "WebDAV" : "WebDAV", "URL" : "URL", "Remote subfolder" : "원격 하위 폴더", diff --git a/apps/files_external/l10n/ko.json b/apps/files_external/l10n/ko.json index aa11d68aa48..4f2bd99e984 100644 --- a/apps/files_external/l10n/ko.json +++ b/apps/files_external/l10n/ko.json @@ -73,6 +73,7 @@ "Region" : "지역", "Enable SSL" : "SSL 사용", "Enable Path Style" : "경로 스타일 사용", + "Legacy (v2) authentication" : "레거시(v2) 인증", "WebDAV" : "WebDAV", "URL" : "URL", "Remote subfolder" : "원격 하위 폴더", diff --git a/apps/files_external/l10n/nl.js b/apps/files_external/l10n/nl.js index 2749e59e11b..5e53b24ddc6 100644 --- a/apps/files_external/l10n/nl.js +++ b/apps/files_external/l10n/nl.js @@ -75,6 +75,7 @@ OC.L10N.register( "Region" : "Regio", "Enable SSL" : "Activeren SSL", "Enable Path Style" : "Activeren pad stijl", + "Legacy (v2) authentication" : "Legacy (v2) authenticatie", "WebDAV" : "WebDAV", "URL" : "URL", "Remote subfolder" : "Externe submap", diff --git a/apps/files_external/l10n/nl.json b/apps/files_external/l10n/nl.json index 8258427503a..ee2ffa25a93 100644 --- a/apps/files_external/l10n/nl.json +++ b/apps/files_external/l10n/nl.json @@ -73,6 +73,7 @@ "Region" : "Regio", "Enable SSL" : "Activeren SSL", "Enable Path Style" : "Activeren pad stijl", + "Legacy (v2) authentication" : "Legacy (v2) authenticatie", "WebDAV" : "WebDAV", "URL" : "URL", "Remote subfolder" : "Externe submap", diff --git a/apps/files_external/l10n/pt_BR.js b/apps/files_external/l10n/pt_BR.js index 077b89e1cf7..c2fe422e7b3 100644 --- a/apps/files_external/l10n/pt_BR.js +++ b/apps/files_external/l10n/pt_BR.js @@ -75,6 +75,7 @@ OC.L10N.register( "Region" : "Região", "Enable SSL" : "Habilitar SSL", "Enable Path Style" : "Habilitar Estilo do Caminho", + "Legacy (v2) authentication" : "Autenticação (v2) herdada", "WebDAV" : "WebDAV", "URL" : "URL", "Remote subfolder" : "Subpasta remota", diff --git a/apps/files_external/l10n/pt_BR.json b/apps/files_external/l10n/pt_BR.json index f9472a5c79d..4f5550870c5 100644 --- a/apps/files_external/l10n/pt_BR.json +++ b/apps/files_external/l10n/pt_BR.json @@ -73,6 +73,7 @@ "Region" : "Região", "Enable SSL" : "Habilitar SSL", "Enable Path Style" : "Habilitar Estilo do Caminho", + "Legacy (v2) authentication" : "Autenticação (v2) herdada", "WebDAV" : "WebDAV", "URL" : "URL", "Remote subfolder" : "Subpasta remota", diff --git a/apps/files_external/l10n/sr.js b/apps/files_external/l10n/sr.js index ddff1a6a318..bd94b6dd843 100644 --- a/apps/files_external/l10n/sr.js +++ b/apps/files_external/l10n/sr.js @@ -75,6 +75,7 @@ OC.L10N.register( "Region" : "Регија", "Enable SSL" : "Омогући ССЛ", "Enable Path Style" : "Омогући стил путање", + "Legacy (v2) authentication" : "Стара (v2) провера идентитета", "WebDAV" : "ВебДАВ", "URL" : "УРЛ", "Remote subfolder" : "Удаљена потфасцикла", diff --git a/apps/files_external/l10n/sr.json b/apps/files_external/l10n/sr.json index 15b84d8ac51..7aa88bfaade 100644 --- a/apps/files_external/l10n/sr.json +++ b/apps/files_external/l10n/sr.json @@ -73,6 +73,7 @@ "Region" : "Регија", "Enable SSL" : "Омогући ССЛ", "Enable Path Style" : "Омогући стил путање", + "Legacy (v2) authentication" : "Стара (v2) провера идентитета", "WebDAV" : "ВебДАВ", "URL" : "УРЛ", "Remote subfolder" : "Удаљена потфасцикла", 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/files_sharing/l10n/ko.js b/apps/files_sharing/l10n/ko.js index aad92ab9ef6..5f8e4374ab8 100644 --- a/apps/files_sharing/l10n/ko.js +++ b/apps/files_sharing/l10n/ko.js @@ -15,7 +15,7 @@ OC.L10N.register( "Invalid server URL" : "잘못된 서버 URL", "Failed to add the public link to your Nextcloud" : "Nextcloud에 공개 링크를 추가할 수 없음", "Share" : "공유", - "No expiration date set" : "만료 날짜가 설정되지 않음", + "No expiration date set" : "만료 날짜가 설정되지 않았음", "Shared by" : "공유한 사용자:", "Sharing" : "공유", "File shares" : "파일 공유", @@ -71,17 +71,17 @@ OC.L10N.register( "Wrong path, file/folder doesn't exist" : "잘못된 경로, 파일/폴더가 존재하지 않음", "Could not create share" : "공유를 만들 수 없음", "invalid permissions" : "잘못된 권한", - "Please specify a valid user" : "유효한 사용자를 지정하십시오", - "Group sharing is disabled by the administrator" : "그룹 공유는 관리자에 의해 비활성화되어 있음", - "Please specify a valid group" : "유효한 그룹을 지정하십시오", - "Public link sharing is disabled by the administrator" : "공개 링크 공유는 관리자에 의해 비활성화되어 있음", + "Please specify a valid user" : "올바른 사용자를 지정하십시오", + "Group sharing is disabled by the administrator" : "관리자가 그룹 공유를 비활성화함", + "Please specify a valid group" : "올바른 그룹을 지정하십시오", + "Public link sharing is disabled by the administrator" : "관리자가 공개 링크 공유를 비활성화함", "Public upload disabled by the administrator" : "공개 업로드는 관리자에 의해 비활성화되어 있음", "Public upload is only possible for publicly shared folders" : "공개 공유 폴더에만 공개 업로드를 사용할 수 있음", "Invalid date, date format must be YYYY-MM-DD" : "잘못된 날짜, YYYY-MM-DD 형식이어야 합니다.", "Sharing %s failed because the back end does not allow shares from type %s" : "%s을(를) 공유할 수 없습니다. 백엔드에서 %s 형식의 공유를 지원하지 않습니다", "You cannot share to a Circle if the app is not enabled" : "서클 앱이 활성화되어 있지 않으면 서클로 공유할 수 없음", "Please specify a valid circle" : "올바른 서클을 지정하십시오", - "Unknown share type" : "알 수 없는 공유 유형", + "Unknown share type" : "알 수 없는 공유 형식", "Not a directory" : "디렉터리가 아님", "Could not lock path" : "경로를 잠글 수 없음", "Wrong or no update parameter given" : "업데이트 인자가 잘못되었거나 지정되지 않았음", diff --git a/apps/files_sharing/l10n/ko.json b/apps/files_sharing/l10n/ko.json index d60145d5faf..00c5d8b954a 100644 --- a/apps/files_sharing/l10n/ko.json +++ b/apps/files_sharing/l10n/ko.json @@ -13,7 +13,7 @@ "Invalid server URL" : "잘못된 서버 URL", "Failed to add the public link to your Nextcloud" : "Nextcloud에 공개 링크를 추가할 수 없음", "Share" : "공유", - "No expiration date set" : "만료 날짜가 설정되지 않음", + "No expiration date set" : "만료 날짜가 설정되지 않았음", "Shared by" : "공유한 사용자:", "Sharing" : "공유", "File shares" : "파일 공유", @@ -69,17 +69,17 @@ "Wrong path, file/folder doesn't exist" : "잘못된 경로, 파일/폴더가 존재하지 않음", "Could not create share" : "공유를 만들 수 없음", "invalid permissions" : "잘못된 권한", - "Please specify a valid user" : "유효한 사용자를 지정하십시오", - "Group sharing is disabled by the administrator" : "그룹 공유는 관리자에 의해 비활성화되어 있음", - "Please specify a valid group" : "유효한 그룹을 지정하십시오", - "Public link sharing is disabled by the administrator" : "공개 링크 공유는 관리자에 의해 비활성화되어 있음", + "Please specify a valid user" : "올바른 사용자를 지정하십시오", + "Group sharing is disabled by the administrator" : "관리자가 그룹 공유를 비활성화함", + "Please specify a valid group" : "올바른 그룹을 지정하십시오", + "Public link sharing is disabled by the administrator" : "관리자가 공개 링크 공유를 비활성화함", "Public upload disabled by the administrator" : "공개 업로드는 관리자에 의해 비활성화되어 있음", "Public upload is only possible for publicly shared folders" : "공개 공유 폴더에만 공개 업로드를 사용할 수 있음", "Invalid date, date format must be YYYY-MM-DD" : "잘못된 날짜, YYYY-MM-DD 형식이어야 합니다.", "Sharing %s failed because the back end does not allow shares from type %s" : "%s을(를) 공유할 수 없습니다. 백엔드에서 %s 형식의 공유를 지원하지 않습니다", "You cannot share to a Circle if the app is not enabled" : "서클 앱이 활성화되어 있지 않으면 서클로 공유할 수 없음", "Please specify a valid circle" : "올바른 서클을 지정하십시오", - "Unknown share type" : "알 수 없는 공유 유형", + "Unknown share type" : "알 수 없는 공유 형식", "Not a directory" : "디렉터리가 아님", "Could not lock path" : "경로를 잠글 수 없음", "Wrong or no update parameter given" : "업데이트 인자가 잘못되었거나 지정되지 않았음", diff --git a/apps/files_sharing/lib/External/Mount.php b/apps/files_sharing/lib/External/Mount.php index d756a1830b2..e12f8823cd8 100644 --- a/apps/files_sharing/lib/External/Mount.php +++ b/apps/files_sharing/lib/External/Mount.php @@ -68,4 +68,14 @@ class Mount extends MountPoint implements MoveableMount { public function removeMount() { return $this->manager->removeShare($this->mountPoint); } + + /** + * Get the type of mount point, used to distinguish things like shares and external storages + * in the web interface + * + * @return string + */ + public function getMountType() { + return 'shared'; + } } diff --git a/apps/sharebymail/l10n/sv.js b/apps/sharebymail/l10n/sv.js index eb3b8451e6b..03789a8af96 100644 --- a/apps/sharebymail/l10n/sv.js +++ b/apps/sharebymail/l10n/sv.js @@ -5,9 +5,9 @@ OC.L10N.register( "Shared with {email}" : "Delad med {email}", "Shared with %1$s by %2$s" : "Delad med %1$s av %2$s", "Shared with {email} by {actor}" : "Delad med {email} av {actor}", - "Password for mail share sent to %1$s" : "Lösenord för maildelning sändes till %1$s", - "Password for mail share sent to {email}" : "Lösenord för maildelning skickat till {email}", - "Password for mail share sent to you" : "Lösenord för epostdelning skickat till dig", + "Password for mail share sent to %1$s" : "Lösenord för e-postdelning sändes till %1$s", + "Password for mail share sent to {email}" : "Lösenord för e-postdelning skickat till {email}", + "Password for mail share sent to you" : "Lösenord för e-postdelning skickat till dig", "You shared %1$s with %2$s by mail" : "Du delade %1$s med %2$s via e-post", "You shared {file} with {email} by mail" : "Du delade {file} med {email} via e-post", "%3$s shared %1$s with %2$s by mail" : "%3$s delade %1$s med %2$s via e-post", @@ -17,15 +17,15 @@ OC.L10N.register( "Password to access %1$s was sent to you" : "Lösenord för att nå %1$s sändes till dig", "Password to access {file} was sent to you" : "Lösenord till {file} sändes till dig", "Sharing %s failed, this item is already shared with %s" : "Delning av %s misslyckades, det är redan delat med %s", - "We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again." : "Vi kan inte sända dig det autogenererade lösenordet. Vänligen ange en giltig epost-adress i dina personliga inställningar och försök igen.", + "We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again." : "Vi kan inte skicka det autogenererade lösenordet till dig. Vänligen ange en giltig e-postadress i dina personliga inställningar och försök igen.", "Failed to send share by email" : "Misslyckades att sända delning med e-post", "%s shared »%s« with you" : "%s delade »%s« med dig", "%s shared »%s« with you." : "%s delade »%s« med dig.", - "Click the button below to open it." : "Klicka knappen nedan för att öppna det.", + "Click the button below to open it." : "Klicka på knappen nedan för att öppna det.", "Open »%s«" : "Öppna »%s«", "%s via %s" : "%s via %s", - "%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n" : "%s delade »%s« med dig.\nDu ska redan ha fått ett separat epost med en länk för att nå det.\n", - "%s shared »%s« with you. You should have already received a separate mail with a link to access it." : "%s delade »%s« med dig. Du ska redan ha fått ett separat e-post med en länk för att nå det.", + "%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n" : "%s delade »%s« med dig.\nDu ska redan ha fått ett separat mail med en länk för att komma åt det.\n", + "%s shared »%s« with you. You should have already received a separate mail with a link to access it." : "%s delade »%s« med dig. Du ska redan ha fått ett separat mail med en länk för att komma åt det.", "Password to access »%s« shared to you by %s" : "Lösenord för att få tillgång till »%s« delat med dig av %s", "Password to access »%s«" : "Lösenord för att nå »%s«", "It is protected with the following password: %s" : "Den är skyddad med följande lösenord: %s", @@ -35,8 +35,8 @@ OC.L10N.register( "You can choose a different password at any time in the share dialog." : "Du kan välja ett annat lösenord när som helst i delningsdialogen.", "Could not find share" : "Kunde inte hitta delning", "Share by mail" : "Dela via e-post", - "Allows users to share a personalized link to a file or folder by putting in an email address." : "Tillåt användare att dela en personlig länk till en fil eller mapp genom att ange en e-post-adress", - "Send password by mail" : "Skicka lösenord med e-post", + "Allows users to share a personalized link to a file or folder by putting in an email address." : "Tillåt användare att dela en personlig länk till en fil eller mapp genom att ange en e-postadress", + "Send password by mail" : "Skicka lösenord via e-post", "Enforce password protection" : "Tvinga lösenordsskydd", "Failed to send share by E-mail" : "Misslyckades att dela via e-post", "%s shared »%s« with you on behalf of %s" : "%s delade »%s« med dig på begäran av %s", diff --git a/apps/sharebymail/l10n/sv.json b/apps/sharebymail/l10n/sv.json index 75e3bb9d299..ced3fece128 100644 --- a/apps/sharebymail/l10n/sv.json +++ b/apps/sharebymail/l10n/sv.json @@ -3,9 +3,9 @@ "Shared with {email}" : "Delad med {email}", "Shared with %1$s by %2$s" : "Delad med %1$s av %2$s", "Shared with {email} by {actor}" : "Delad med {email} av {actor}", - "Password for mail share sent to %1$s" : "Lösenord för maildelning sändes till %1$s", - "Password for mail share sent to {email}" : "Lösenord för maildelning skickat till {email}", - "Password for mail share sent to you" : "Lösenord för epostdelning skickat till dig", + "Password for mail share sent to %1$s" : "Lösenord för e-postdelning sändes till %1$s", + "Password for mail share sent to {email}" : "Lösenord för e-postdelning skickat till {email}", + "Password for mail share sent to you" : "Lösenord för e-postdelning skickat till dig", "You shared %1$s with %2$s by mail" : "Du delade %1$s med %2$s via e-post", "You shared {file} with {email} by mail" : "Du delade {file} med {email} via e-post", "%3$s shared %1$s with %2$s by mail" : "%3$s delade %1$s med %2$s via e-post", @@ -15,15 +15,15 @@ "Password to access %1$s was sent to you" : "Lösenord för att nå %1$s sändes till dig", "Password to access {file} was sent to you" : "Lösenord till {file} sändes till dig", "Sharing %s failed, this item is already shared with %s" : "Delning av %s misslyckades, det är redan delat med %s", - "We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again." : "Vi kan inte sända dig det autogenererade lösenordet. Vänligen ange en giltig epost-adress i dina personliga inställningar och försök igen.", + "We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again." : "Vi kan inte skicka det autogenererade lösenordet till dig. Vänligen ange en giltig e-postadress i dina personliga inställningar och försök igen.", "Failed to send share by email" : "Misslyckades att sända delning med e-post", "%s shared »%s« with you" : "%s delade »%s« med dig", "%s shared »%s« with you." : "%s delade »%s« med dig.", - "Click the button below to open it." : "Klicka knappen nedan för att öppna det.", + "Click the button below to open it." : "Klicka på knappen nedan för att öppna det.", "Open »%s«" : "Öppna »%s«", "%s via %s" : "%s via %s", - "%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n" : "%s delade »%s« med dig.\nDu ska redan ha fått ett separat epost med en länk för att nå det.\n", - "%s shared »%s« with you. You should have already received a separate mail with a link to access it." : "%s delade »%s« med dig. Du ska redan ha fått ett separat e-post med en länk för att nå det.", + "%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n" : "%s delade »%s« med dig.\nDu ska redan ha fått ett separat mail med en länk för att komma åt det.\n", + "%s shared »%s« with you. You should have already received a separate mail with a link to access it." : "%s delade »%s« med dig. Du ska redan ha fått ett separat mail med en länk för att komma åt det.", "Password to access »%s« shared to you by %s" : "Lösenord för att få tillgång till »%s« delat med dig av %s", "Password to access »%s«" : "Lösenord för att nå »%s«", "It is protected with the following password: %s" : "Den är skyddad med följande lösenord: %s", @@ -33,8 +33,8 @@ "You can choose a different password at any time in the share dialog." : "Du kan välja ett annat lösenord när som helst i delningsdialogen.", "Could not find share" : "Kunde inte hitta delning", "Share by mail" : "Dela via e-post", - "Allows users to share a personalized link to a file or folder by putting in an email address." : "Tillåt användare att dela en personlig länk till en fil eller mapp genom att ange en e-post-adress", - "Send password by mail" : "Skicka lösenord med e-post", + "Allows users to share a personalized link to a file or folder by putting in an email address." : "Tillåt användare att dela en personlig länk till en fil eller mapp genom att ange en e-postadress", + "Send password by mail" : "Skicka lösenord via e-post", "Enforce password protection" : "Tvinga lösenordsskydd", "Failed to send share by E-mail" : "Misslyckades att dela via e-post", "%s shared »%s« with you on behalf of %s" : "%s delade »%s« med dig på begäran av %s", 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/Capabilities.php b/apps/theming/lib/Capabilities.php index 1b6bb8927be..a75403a1fd5 100644 --- a/apps/theming/lib/Capabilities.php +++ b/apps/theming/lib/Capabilities.php @@ -81,6 +81,8 @@ class Capabilities implements IPublicCapability { 'background' => $backgroundLogo === 'backgroundColor' ? $this->theming->getColorPrimary() : $this->url->getAbsoluteURL($this->theming->getBackground()), + 'background-plain' => $backgroundLogo === 'backgroundColor', + 'background-default' => !$this->util->isBackgroundThemed(), ], ]; } diff --git a/apps/theming/lib/Controller/ThemingController.php b/apps/theming/lib/Controller/ThemingController.php index b7777f1f907..6592eb7f7ab 100644 --- a/apps/theming/lib/Controller/ThemingController.php +++ b/apps/theming/lib/Controller/ThemingController.php @@ -176,7 +176,7 @@ class ThemingController extends Controller { $this->themingDefaults->set($setting, $value); // 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'); return new DataResponse( [ @@ -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/ThemingDefaults.php b/apps/theming/lib/ThemingDefaults.php index 05d387e6273..9dcc981817e 100644 --- a/apps/theming/lib/ThemingDefaults.php +++ b/apps/theming/lib/ThemingDefaults.php @@ -196,22 +196,13 @@ class ThemingDefaults extends \OC_Defaults { * @return string */ public function getBackground() { - $backgroundLogo = $this->config->getAppValue('theming', 'backgroundMime',false); - - $backgroundExists = true; - try { - $this->appData->getFolder('images')->getFile('background'); - } catch (\Exception $e) { - $backgroundExists = false; - } - $cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0'); - if(!$backgroundLogo || !$backgroundExists) { - return $this->urlGenerator->imagePath('core','background.png') . '?v=' . $cacheBusterCounter; + if($this->util->isBackgroundThemed()) { + return $this->urlGenerator->linkToRoute('theming.Theming.getLoginBackground') . '?v=' . $cacheBusterCounter; } - return $this->urlGenerator->linkToRoute('theming.Theming.getLoginBackground') . '?v=' . $cacheBusterCounter; + return $this->urlGenerator->imagePath('core','background.png') . '?v=' . $cacheBusterCounter; } /** diff --git a/apps/theming/lib/Util.php b/apps/theming/lib/Util.php index 194b5eeb0d0..2f6f4128365 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)) + ]; } /** @@ -216,4 +235,16 @@ class Util { return false; } + public function isBackgroundThemed() { + $backgroundLogo = $this->config->getAppValue('theming', 'backgroundMime',false); + + $backgroundExists = true; + try { + $this->appData->getFolder('images')->getFile('background'); + } catch (\Exception $e) { + $backgroundExists = false; + } + return $backgroundLogo && $backgroundLogo !== 'backgroundColor' && $backgroundExists; + } + } diff --git a/apps/theming/tests/CapabilitiesTest.php b/apps/theming/tests/CapabilitiesTest.php index c760c896425..31e0ae79970 100644 --- a/apps/theming/tests/CapabilitiesTest.php +++ b/apps/theming/tests/CapabilitiesTest.php @@ -50,6 +50,9 @@ class CapabilitiesTest extends TestCase { /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ protected $config; + /** @var Util|\PHPUnit_Framework_MockObject_MockObject */ + protected $util; + /** @var Capabilities */ protected $capabilities; @@ -59,13 +62,13 @@ class CapabilitiesTest extends TestCase { $this->theming = $this->createMock(ThemingDefaults::class); $this->url = $this->getMockBuilder(IURLGenerator::class)->getMock(); $this->config = $this->createMock(IConfig::class); - $util = new Util($this->config, $this->createMock(IAppManager::class), $this->createMock(IAppData::class)); - $this->capabilities = new Capabilities($this->theming, $util, $this->url, $this->config); + $this->util = $this->createMock(Util::class); + $this->capabilities = new Capabilities($this->theming, $this->util, $this->url, $this->config); } public function dataGetCapabilities() { return [ - ['name', 'url', 'slogan', '#FFFFFF', '#000000', 'logo', 'background', 'http://absolute/', [ + ['name', 'url', 'slogan', '#FFFFFF', '#000000', 'logo', 'background', 'http://absolute/', true, [ 'name' => 'name', 'url' => 'url', 'slogan' => 'slogan', @@ -74,8 +77,10 @@ class CapabilitiesTest extends TestCase { 'color-element' => '#555555', 'logo' => 'http://absolute/logo', 'background' => 'http://absolute/background', + 'background-plain' => false, + 'background-default' => false, ]], - ['name1', 'url2', 'slogan3', '#01e4a0', '#ffffff', 'logo5', 'background6', 'http://localhost/', [ + ['name1', 'url2', 'slogan3', '#01e4a0', '#ffffff', 'logo5', 'background6', 'http://localhost/', false, [ 'name' => 'name1', 'url' => 'url2', 'slogan' => 'slogan3', @@ -84,8 +89,22 @@ class CapabilitiesTest extends TestCase { 'color-element' => '#01e4a0', 'logo' => 'http://localhost/logo5', 'background' => 'http://localhost/background6', + 'background-plain' => false, + 'background-default' => true, + ]], + ['name1', 'url2', 'slogan3', '#000000', '#ffffff', 'logo5', 'backgroundColor', 'http://localhost/', true, [ + 'name' => 'name1', + 'url' => 'url2', + 'slogan' => 'slogan3', + 'color' => '#000000', + 'color-text' => '#ffffff', + 'color-element' => '#000000', + 'logo' => 'http://localhost/logo5', + 'background' => '#000000', + 'background-plain' => true, + 'background-default' => false, ]], - ['name1', 'url2', 'slogan3', '#000000', '#ffffff', 'logo5', 'backgroundColor', 'http://localhost/', [ + ['name1', 'url2', 'slogan3', '#000000', '#ffffff', 'logo5', 'backgroundColor', 'http://localhost/', false, [ 'name' => 'name1', 'url' => 'url2', 'slogan' => 'slogan3', @@ -94,6 +113,8 @@ class CapabilitiesTest extends TestCase { 'color-element' => '#000000', 'logo' => 'http://localhost/logo5', 'background' => '#000000', + 'background-plain' => true, + 'background-default' => true, ]], ]; } @@ -104,13 +125,14 @@ class CapabilitiesTest extends TestCase { * @param string $url * @param string $slogan * @param string $color - * @param string $logo * @param string $textColor + * @param string $logo * @param string $background * @param string $baseUrl + * @param bool $backgroundThemed * @param string[] $expected */ - public function testGetCapabilities($name, $url, $slogan, $color, $textColor, $logo, $background, $baseUrl, array $expected) { + public function testGetCapabilities($name, $url, $slogan, $color, $textColor, $logo, $background, $baseUrl, $backgroundThemed, array $expected) { $this->config->expects($this->once()) ->method('getAppValue') ->willReturn($background); @@ -133,6 +155,16 @@ class CapabilitiesTest extends TestCase { ->method('getTextColorPrimary') ->willReturn($textColor); + $util = new Util($this->config, $this->createMock(IAppManager::class), $this->createMock(IAppData::class)); + $this->util->expects($this->once()) + ->method('elementColor') + ->with($color) + ->willReturn($util->elementColor($color)); + + $this->util->expects($this->once()) + ->method('isBackgroundThemed') + ->willReturn($backgroundThemed); + if($background !== 'backgroundColor') { $this->theming->expects($this->once()) ->method('getBackground') diff --git a/apps/theming/tests/ThemingDefaultsTest.php b/apps/theming/tests/ThemingDefaultsTest.php index 843c1d34f9e..d0dc6587f74 100644 --- a/apps/theming/tests/ThemingDefaultsTest.php +++ b/apps/theming/tests/ThemingDefaultsTest.php @@ -391,24 +391,14 @@ class ThemingDefaultsTest extends TestCase { } public function testGetBackgroundDefault() { - $this->appData->expects($this->once()) - ->method('getFolder') - ->willThrowException(new NotFoundException()); - $this->config - ->expects($this->at(0)) - ->method('getAppValue') - ->with('theming', 'backgroundMime') - ->willReturn(''); $this->config - ->expects($this->at(1)) + ->expects($this->once()) ->method('getAppValue') ->with('theming', 'cachebuster', '0') ->willReturn('0'); - $this->appData - ->expects($this->once()) - ->method('getFolder') - ->with('images') - ->willThrowException(new \Exception()); + $this->util->expects($this->once()) + ->method('isBackgroundThemed') + ->willReturn(false); $this->urlGenerator->expects($this->once()) ->method('imagePath') ->with('core', 'background.png') @@ -417,24 +407,14 @@ class ThemingDefaultsTest extends TestCase { } public function testGetBackgroundCustom() { - $folder = $this->createMock(ISimpleFolder::class); - $file = $this->createMock(ISimpleFile::class); - $folder->expects($this->once()) - ->method('getFile') - ->willReturn($file); - $this->appData->expects($this->once()) - ->method('getFolder') - ->willReturn($folder); $this->config - ->expects($this->at(0)) - ->method('getAppValue') - ->with('theming', 'backgroundMime', false) - ->willReturn('image/svg+xml'); - $this->config - ->expects($this->at(1)) + ->expects($this->once()) ->method('getAppValue') ->with('theming', 'cachebuster', '0') ->willReturn('0'); + $this->util->expects($this->once()) + ->method('isBackgroundThemed') + ->willReturn(true); $this->urlGenerator->expects($this->once()) ->method('linkToRoute') ->with('theming.Theming.getLoginBackground') @@ -513,12 +493,12 @@ class ThemingDefaultsTest extends TestCase { $this->config->expects($this->at(2))->method('getAppValue')->with('theming', 'backgroundMime', false)->willReturn('jpeg'); $this->config->expects($this->at(3))->method('getAppValue')->with('theming', 'logoMime', false)->willReturn('jpeg'); $this->config->expects($this->at(4))->method('getAppValue')->with('theming', 'cachebuster', '0')->willReturn('0'); - $this->config->expects($this->at(5))->method('getAppValue')->with('theming', 'backgroundMime', false)->willReturn('jpeg'); - $this->config->expects($this->at(6))->method('getAppValue')->with('theming', 'cachebuster', '0')->willReturn('0'); - $this->config->expects($this->at(7))->method('getAppValue')->with('theming', 'color', null)->willReturn($this->defaults->getColorPrimary()); + $this->util->expects($this->once())->method('isBackgroundThemed')->willReturn(true); + $this->config->expects($this->at(5))->method('getAppValue')->with('theming', 'cachebuster', '0')->willReturn('0'); + $this->config->expects($this->at(6))->method('getAppValue')->with('theming', 'color', null)->willReturn($this->defaults->getColorPrimary()); + $this->config->expects($this->at(7))->method('getAppValue')->with('theming', 'color', $this->defaults->getColorPrimary())->willReturn($this->defaults->getColorPrimary()); $this->config->expects($this->at(8))->method('getAppValue')->with('theming', 'color', $this->defaults->getColorPrimary())->willReturn($this->defaults->getColorPrimary()); $this->config->expects($this->at(9))->method('getAppValue')->with('theming', 'color', $this->defaults->getColorPrimary())->willReturn($this->defaults->getColorPrimary()); - $this->config->expects($this->at(10))->method('getAppValue')->with('theming', 'color', $this->defaults->getColorPrimary())->willReturn($this->defaults->getColorPrimary()); $this->util->expects($this->any())->method('invertTextColor')->with($this->defaults->getColorPrimary())->willReturn(false); $this->util->expects($this->any())->method('elementColor')->with($this->defaults->getColorPrimary())->willReturn('#aaaaaa'); diff --git a/apps/theming/tests/UtilTest.php b/apps/theming/tests/UtilTest.php index 94e4c9cbcc8..247bcbae0b2 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() { @@ -183,7 +189,6 @@ class UtilTest extends TestCase { } public function testIsAlreadyThemedFalse() { - $theme = $this->config->getSystemValue('theme', ''); $this->config->expects($this->once()) ->method('getSystemValue') ->with('theme', '') @@ -193,7 +198,6 @@ class UtilTest extends TestCase { } public function testIsAlreadyThemedTrue() { - $theme = $this->config->getSystemValue('theme', ''); $this->config->expects($this->once()) ->method('getSystemValue') ->with('theme', '') @@ -202,4 +206,35 @@ class UtilTest extends TestCase { $this->assertTrue($actual); } + public function dataIsBackgroundThemed() { + return [ + [false, false, false], + ['png', true, true], + ['backgroundColor', false, false], + ]; + } + /** + * @dataProvider dataIsBackgroundThemed + */ + public function testIsBackgroundThemed($backgroundMime, $fileFound, $expected) { + $this->config->expects($this->once()) + ->method('getAppValue') + ->with('theming', 'backgroundMime', false) + ->willReturn($backgroundMime); + $folder = $this->createMock(ISimpleFolder::class); + if ($fileFound) { + $folder->expects($this->once()) + ->method('getFile') + ->willReturn($this->createMock(ISimpleFile::class)); + } else { + $folder->expects($this->once()) + ->method('getFile') + ->willThrowException(new NotFoundException()); + } + $this->appData->expects($this->once()) + ->method('getFolder') + ->willReturn($folder); + $this->assertEquals($expected, $this->util->isBackgroundThemed()); + } + } diff --git a/apps/updatenotification/js/notification.js b/apps/updatenotification/js/notification.js index a2e78cecb09..a588ede5ffd 100644 --- a/apps/updatenotification/js/notification.js +++ b/apps/updatenotification/js/notification.js @@ -17,10 +17,5 @@ $(document).ready(function(){ var text = t('core', '{version} is available. Get more information on how to update.', {version: oc_updateState.updateVersion}), element = $('<a>').attr('href', oc_updateState.updateLink).attr('target','_blank').text(text); - OC.Notification.show(element, - { - isHTML: true, - type: 'error' - } - ); + OC.Notification.showHtml(element, { type: 'error' }); }); 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/Controller/OCJSController.php b/core/Controller/OCJSController.php index de880968a69..8db26dd3d38 100644 --- a/core/Controller/OCJSController.php +++ b/core/Controller/OCJSController.php @@ -34,11 +34,11 @@ use OCP\AppFramework\Http\DataDisplayResponse; use OCP\Defaults; use OCP\IConfig; use OCP\IGroupManager; -use OCP\IL10N; use OCP\IRequest; use OCP\ISession; use OCP\IURLGenerator; use OCP\IUserSession; +use OCP\L10N\IFactory; class OCJSController extends Controller { @@ -50,7 +50,7 @@ class OCJSController extends Controller { * * @param string $appName * @param IRequest $request - * @param IL10N $l + * @param IFactory $l10nFactory * @param Defaults $defaults * @param IAppManager $appManager * @param ISession $session @@ -62,7 +62,7 @@ class OCJSController extends Controller { */ public function __construct($appName, IRequest $request, - IL10N $l, + IFactory $l10nFactory, Defaults $defaults, IAppManager $appManager, ISession $session, @@ -74,7 +74,7 @@ class OCJSController extends Controller { parent::__construct($appName, $request); $this->helper = new JSConfigHelper( - $l, + $l10nFactory->get('lib'), $defaults, $appManager, $session, 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/guest.css b/core/css/guest.css index 4256c66302f..ecc3da9d081 100644 --- a/core/css/guest.css +++ b/core/css/guest.css @@ -93,7 +93,7 @@ body { form { position: relative; width: 280px; - margin: 16px auto; + margin: auto; padding: 0; } form fieldset { @@ -410,7 +410,6 @@ form .warning input[type='checkbox']+label { padding: 12px; margin-top: -6px; color: #fff; - opacity: .7; } #forgot-password { padding: 11px; @@ -579,8 +578,7 @@ fieldset.update legend + p { margin-bottom: 15px; } p.info { - margin: 0 auto; - padding-top: 20px; + margin: 20px auto; text-shadow: 0 0 2px rgba(0, 0, 0, .4); // better readability on bright background -webkit-user-select: none; -moz-user-select: none; diff --git a/core/css/header.scss b/core/css/header.scss index 98ea71f2ed2..7021762bf7f 100644 --- a/core/css/header.scss +++ b/core/css/header.scss @@ -206,12 +206,15 @@ nav { margin-left: -54px; } -.header-left #navigation, -.ui-datepicker, -.ui-timepicker.ui-widget { +.header-left #navigation { position: relative; left: -100%; width: 160px; +} + +.header-left #navigation, +.ui-datepicker, +.ui-timepicker.ui-widget { background-color: $color-main-background; filter: drop-shadow(0 1px 10px $color-box-shadow); &:after { diff --git a/core/css/styles.scss b/core/css/styles.scss index c479d6df888..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; @@ -1035,15 +1035,6 @@ code { background-color: inherit; } td { - > * { - border-radius: 50%; - text-align: center; - font-weight: normal; - color: $color-main-text; - padding: 6px; - line-height: 12px; - } - &.ui-datepicker-today a:not(.ui-state-hover) { background-color: nc-lighten($color-main-text, 86%); } @@ -1056,7 +1047,7 @@ code { font-weight: bold; } - &.ui-datepicker-week-end :not(.ui-state-hover), + &.ui-datepicker-week-end:not(.ui-state-disabled) :not(.ui-state-hover), .ui-priority-secondary:not(.ui-state-hover) { color: nc-lighten($color-main-text, 33%); opacity: .8; @@ -1099,26 +1090,25 @@ code { font-weight: 300; } } + /* AM/PM fix */ + table.ui-timepicker tr .ui-timepicker-hour-cell:first-child { + margin-left: 30px; + } .ui-timepicker-table { th { font-weight: normal; color: nc-lighten($color-main-text, 33%); opacity: .8; + &.periods { + padding: 0; + width: 30px; + line-height: 30px; + } } tr:hover { background-color: inherit; } td { - > * { - border-radius: 50%; - text-align: center; - font-weight: normal; - color: $color-main-text; - padding: 8px 7px; - font-size: .9em; - line-height: 12px; - } - &.ui-timepicker-hour-cell a.ui-state-active, &.ui-timepicker-minute-cell a.ui-state-active, .ui-state-hover, @@ -1139,6 +1129,37 @@ code { } } +/* ---- jQuery UI datepicker & timepicker global rules ---- */ +.ui-widget.ui-datepicker .ui-datepicker-calendar, +.ui-widget.ui-timepicker table.ui-timepicker { + tr { + display: flex; + flex-wrap: nowrap; + justify-content: space-between; + td { + display: block; + flex: 1 1; + margin: 0; + padding: 2px; + display: flex; + align-items: center; + justify-content: center; + > * { + border-radius: 50%; + text-align: center; + font-weight: normal; + color: $color-main-text; + display: block; + line-height: 18px; + width: 18px; + height: 18px; + padding: 3px; + font-size: .9em; + } + } + } +} + /* ---- DIALOGS ---- */ #oc-dialog-filepicker-content { 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/js.js b/core/js/js.js index f9a5f2b3381..872761c02bb 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -1675,13 +1675,16 @@ function initCore() { OC.PasswordConfirmation = { callback: null, - + pageLoadTime: null, init: function() { $('.password-confirm-required').on('click', _.bind(this.requirePasswordConfirmation, this)); + this.pageLoadTime = moment.now(); }, requiresPasswordConfirmation: function() { - var timeSinceLogin = moment.now() - (nc_lastLogin * 1000); + var serverTimeDiff = this.pageLoadTime - (nc_pageLoad * 1000); + var timeSinceLogin = moment.now() - (serverTimeDiff + (nc_lastLogin * 1000)); + // if timeSinceLogin > 30 minutes and user backend allows password confirmation return (backendAllowsPasswordConfirmation && timeSinceLogin > 30 * 60 * 1000); }, diff --git a/core/js/lostpassword.js b/core/js/lostpassword.js index b44962f552e..8c770047444 100644 --- a/core/js/lostpassword.js +++ b/core/js/lostpassword.js @@ -50,6 +50,7 @@ OC.Lostpassword = { event.preventDefault(); $('#lost-password').hide(); + $('.wrongPasswordMsg').hide(); $('#lost-password-back').slideDown().fadeIn(); $('.remember-login-container').slideUp().fadeOut(); $('#submit-wrapper').slideUp().fadeOut(); 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..ac0bf8e0ab7 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" rel="noopener noreferrer" href="' + url + '">' + linkText + '</a>'; }); }, diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js index b4828e3b0dc..30cbeff3c64 100644 --- a/core/js/sharedialogshareelistview.js +++ b/core/js/sharedialogshareelistview.js @@ -116,7 +116,7 @@ '<label for="expireDate-{{cid}}-{{shareId}}">{{expireDateLabel}}</label>' + '<div class="expirationDateContainer-{{cid}}-{{shareId}} {{#unless hasExpireDate}}hidden{{/unless}}">' + ' <label for="expirationDatePicker-{{cid}}-{{shareId}}" class="hidden-visually" value="{{expirationDate}}">{{expirationLabel}}</label>' + - ' <input id="expirationDatePicker-{{cid}}-{{shareId}}" class="datepicker" type="text" placeholder="{{expirationDatePlaceholder}}" value="{{expireDate}}" />' + + ' <input id="expirationDatePicker-{{cid}}-{{shareId}}" class="datepicker" type="text" placeholder="{{expirationDatePlaceholder}}" value="{{#if hasExpireDate}}{{expireDate}}{{else}}{{defaultExpireDate}}{{/if}}" />' + '</div>' + '</span>' + '</li>' + @@ -266,6 +266,8 @@ expireDateLabel: t('core', 'Set expiration date'), passwordLabel: t('core', 'Password protect'), crudsLabel: t('core', 'Access control'), + expirationDatePlaceholder: t('core', 'Expiration date'), + defaultExpireDate: moment().add(1, 'day').format('DD-MM-YYYY'), // Can't expire today triangleSImage: OC.imagePath('core', 'actions/triangle-s'), isResharingAllowed: this.configModel.get('isResharingAllowed'), isPasswordForMailSharesRequired: this.configModel.get('isPasswordForMailSharesRequired'), @@ -513,19 +515,14 @@ var shareId = li.data('share-id'); var expirationDatePicker = '#expirationDatePicker-' + this.cid + '-' + shareId; var view = this; - $(expirationDatePicker).closest('div').datepicker({ + $(expirationDatePicker).datepicker({ dateFormat : 'dd-mm-yy', - onSelect: - function (expireDate) { - view.setExpirationDate(shareId, expireDate); - }, - onClose: - function () { - $(expirationDatePicker).removeClass('hidden-visually'); - } + onSelect: function (expireDate) { + view.setExpirationDate(shareId, expireDate); + } }); + $(expirationDatePicker).focus(); - $(expirationDatePicker).addClass('hidden-visually'); }, setExpirationDate: function(shareId, expireDate) { diff --git a/core/js/tests/specs/coreSpec.js b/core/js/tests/specs/coreSpec.js index b6c617303cf..616e7509f7c 100644 --- a/core/js/tests/specs/coreSpec.js +++ b/core/js/tests/specs/coreSpec.js @@ -1539,4 +1539,65 @@ describe('Core base tests', function() { expect(snapperStub.close.calledTwice).toBe(true); }); }); + describe('Requires password confirmation', function () { + var stubMomentNow; + var stubJsPageLoadTime; + + afterEach(function () { + delete window.nc_pageLoad; + delete window.nc_lastLogin; + delete window.backendAllowsPasswordConfirmation; + + stubMomentNow.restore(); + stubJsPageLoadTime.restore(); + }); + + it('should not show the password confirmation dialog when server time is earlier than local time', function () { + // add server variables + window.nc_pageLoad = parseInt(new Date(2018, 0, 3, 1, 15, 0).getTime() / 1000); + window.nc_lastLogin = parseInt(new Date(2018, 0, 3, 1, 0, 0).getTime() / 1000); + window.backendAllowsPasswordConfirmation = true; + + stubJsPageLoadTime = sinon.stub(OC.PasswordConfirmation, 'pageLoadTime').value(new Date(2018, 0, 3, 12, 15, 0).getTime()); + stubMomentNow = sinon.stub(moment, 'now').returns(new Date(2018, 0, 3, 12, 20, 0).getTime()); + + expect(OC.PasswordConfirmation.requiresPasswordConfirmation()).toBeFalsy(); + }); + + it('should show the password confirmation dialog when server time is earlier than local time', function () { + // add server variables + window.nc_pageLoad = parseInt(new Date(2018, 0, 3, 1, 15, 0).getTime() / 1000); + window.nc_lastLogin = parseInt(new Date(2018, 0, 3, 1, 0, 0).getTime() / 1000); + window.backendAllowsPasswordConfirmation = true; + + stubJsPageLoadTime = sinon.stub(OC.PasswordConfirmation, 'pageLoadTime').value(new Date(2018, 0, 3, 12, 15, 0).getTime()); + stubMomentNow = sinon.stub(moment, 'now').returns(new Date(2018, 0, 3, 12, 31, 0).getTime()); + + expect(OC.PasswordConfirmation.requiresPasswordConfirmation()).toBeTruthy(); + }); + + it('should not show the password confirmation dialog when server time is later than local time', function () { + // add server variables + window.nc_pageLoad = parseInt(new Date(2018, 0, 3, 23, 15, 0).getTime() / 1000); + window.nc_lastLogin = parseInt(new Date(2018, 0, 3, 23, 0, 0).getTime() / 1000); + window.backendAllowsPasswordConfirmation = true; + + stubJsPageLoadTime = sinon.stub(OC.PasswordConfirmation, 'pageLoadTime').value(new Date(2018, 0, 3, 12, 15, 0).getTime()); + stubMomentNow = sinon.stub(moment, 'now').returns(new Date(2018, 0, 3, 12, 20, 0).getTime()); + + expect(OC.PasswordConfirmation.requiresPasswordConfirmation()).toBeFalsy(); + }); + + it('should show the password confirmation dialog when server time is later than local time', function () { + // add server variables + window.nc_pageLoad = parseInt(new Date(2018, 0, 3, 23, 15, 0).getTime() / 1000); + window.nc_lastLogin = parseInt(new Date(2018, 0, 3, 23, 0, 0).getTime() / 1000); + window.backendAllowsPasswordConfirmation = true; + + stubJsPageLoadTime = sinon.stub(OC.PasswordConfirmation, 'pageLoadTime').value(new Date(2018, 0, 3, 12, 15, 0).getTime()); + stubMomentNow = sinon.stub(moment, 'now').returns(new Date(2018, 0, 3, 12, 31, 0).getTime()); + + expect(OC.PasswordConfirmation.requiresPasswordConfirmation()).toBeTruthy(); + }); + }); }); 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/core/templates/login.php b/core/templates/login.php index d28c92e36ef..de991e08d97 100644 --- a/core/templates/login.php +++ b/core/templates/login.php @@ -51,17 +51,17 @@ script('core', 'merged-login'); <label for="password" class="infield"><?php p($l->t('Password')); ?></label> </p> - <?php if (!empty($_['invalidpassword'])) { ?> - <p class="warning"> - <?php p($l->t('Wrong password.')); ?> - </p> - <?php } ?> - <div id="submit-wrapper"> <input type="submit" id="submit" class="login primary" title="" value="<?php p($l->t('Log in')); ?>" disabled="disabled" /> <div class="submit-icon icon-confirm-white"></div> </div> + <?php if (!empty($_['invalidpassword'])) { ?> + <p class="warning wrongPasswordMsg"> + <?php p($l->t('Wrong password.')); ?> + </p> + <?php } ?> + <?php if (!empty($_['canResetPassword'])) { ?> <div id="reset-password-wrapper" style="display: none;"> <input type="submit" id="reset-password-submit" class="login primary" title="" value="<?php p($l->t('Reset password')); ?>" disabled="disabled" /> diff --git a/lib/l10n/ko.js b/lib/l10n/ko.js index ae4bb8404e7..8593058bf68 100644 --- a/lib/l10n/ko.js +++ b/lib/l10n/ko.js @@ -227,9 +227,9 @@ OC.L10N.register( "Could not obtain lock type %d on \"%s\"." : "잠금 형식 %d을(를) \"%s\"에 대해 얻을 수 없습니다.", "Storage unauthorized. %s" : "저장소가 인증되지 않았습니다. %s", "Storage incomplete configuration. %s" : "저장소 설정이 완전하지 않습니다. %s", - "Storage connection error. %s" : "저장소 연결 오류. %s", + "Storage connection error. %s" : "저장소 연결 오류입니다. %s", "Storage is temporarily not available" : "저장소를 임시로 사용할 수 없음", - "Storage connection timeout. %s" : "저장소 연결 시간 초과. %s", + "Storage connection timeout. %s" : "저장소 연결 시간이 초과되었습니다. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "%sconfig 디렉터리에 웹 서버 쓰기 권한%s을 주면 해결됩니다.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "ID: %s인 모듈이 존재하지 않습니다. 앱 설정에서 활성화하거나 관리자에게 연락하십시오.", "Server settings" : "서버 설정", diff --git a/lib/l10n/ko.json b/lib/l10n/ko.json index 22a4b05f37a..c8b3d0e8ee9 100644 --- a/lib/l10n/ko.json +++ b/lib/l10n/ko.json @@ -225,9 +225,9 @@ "Could not obtain lock type %d on \"%s\"." : "잠금 형식 %d을(를) \"%s\"에 대해 얻을 수 없습니다.", "Storage unauthorized. %s" : "저장소가 인증되지 않았습니다. %s", "Storage incomplete configuration. %s" : "저장소 설정이 완전하지 않습니다. %s", - "Storage connection error. %s" : "저장소 연결 오류. %s", + "Storage connection error. %s" : "저장소 연결 오류입니다. %s", "Storage is temporarily not available" : "저장소를 임시로 사용할 수 없음", - "Storage connection timeout. %s" : "저장소 연결 시간 초과. %s", + "Storage connection timeout. %s" : "저장소 연결 시간이 초과되었습니다. %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "%sconfig 디렉터리에 웹 서버 쓰기 권한%s을 주면 해결됩니다.", "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "ID: %s인 모듈이 존재하지 않습니다. 앱 설정에서 활성화하거나 관리자에게 연락하십시오.", "Server settings" : "서버 설정", diff --git a/lib/l10n/ru.js b/lib/l10n/ru.js index ed34349936f..e63656b4494 100644 --- a/lib/l10n/ru.js +++ b/lib/l10n/ru.js @@ -16,8 +16,8 @@ OC.L10N.register( "Enterprise bundle" : "Корпоративный пакет", "Groupware bundle" : "Пакет для групп", "Social sharing bundle" : "Пакет для соц. сетей", - "PHP %s or higher is required." : "Требуется PHP %s или выше", - "PHP with a version lower than %s is required." : "Требуется версия PHP ниже %s.", + "PHP %s or higher is required." : "Требуется PHP версии %s или выше.", + "PHP with a version lower than %s is required." : "Требуется PHP версии ниже %s.", "%sbit or higher PHP required." : "Требуется PHP с разрядностью %s бит или более.", "Following databases are supported: %s" : "Поддерживаются следующие СУБД: %s", "The command line tool %s could not be found" : "Утилита командной строки %s не найдена", diff --git a/lib/l10n/ru.json b/lib/l10n/ru.json index 9b867ec5285..980cd6b46d8 100644 --- a/lib/l10n/ru.json +++ b/lib/l10n/ru.json @@ -14,8 +14,8 @@ "Enterprise bundle" : "Корпоративный пакет", "Groupware bundle" : "Пакет для групп", "Social sharing bundle" : "Пакет для соц. сетей", - "PHP %s or higher is required." : "Требуется PHP %s или выше", - "PHP with a version lower than %s is required." : "Требуется версия PHP ниже %s.", + "PHP %s or higher is required." : "Требуется PHP версии %s или выше.", + "PHP with a version lower than %s is required." : "Требуется PHP версии ниже %s.", "%sbit or higher PHP required." : "Требуется PHP с разрядностью %s бит или более.", "Following databases are supported: %s" : "Поддерживаются следующие СУБД: %s", "The command line tool %s could not be found" : "Утилита командной строки %s не найдена", diff --git a/lib/l10n/sv.js b/lib/l10n/sv.js index bda2c46d149..2e6c4defce8 100644 --- a/lib/l10n/sv.js +++ b/lib/l10n/sv.js @@ -30,9 +30,9 @@ OC.L10N.register( "Unknown filetype" : "Okänd filtyp", "Invalid image" : "Ogiltig bild", "Avatar image is not square" : "Profilbilden är inte fyrkantig", - "today" : "i dag", - "tomorrow" : "i morgon", - "yesterday" : "i går", + "today" : "idag", + "tomorrow" : "imorgon", + "yesterday" : "igår", "_in %n day_::_in %n days_" : ["om %n dag","om %n dagar"], "_%n day ago_::_%n days ago_" : ["%n dag sedan","%n dagar sedan"], "next month" : "nästa månad", @@ -66,7 +66,7 @@ OC.L10N.register( "Unknown user" : "Okänd användare", "APCu" : "APCu", "Redis" : "Redis", - "Basic settings" : "Vanliga inställningar", + "Basic settings" : "Generella inställningar", "Sharing" : "Delning", "Security" : "Säkerhet", "Encryption" : "Kryptering", diff --git a/lib/l10n/sv.json b/lib/l10n/sv.json index 17b2ad1f22b..ab2183b6d86 100644 --- a/lib/l10n/sv.json +++ b/lib/l10n/sv.json @@ -28,9 +28,9 @@ "Unknown filetype" : "Okänd filtyp", "Invalid image" : "Ogiltig bild", "Avatar image is not square" : "Profilbilden är inte fyrkantig", - "today" : "i dag", - "tomorrow" : "i morgon", - "yesterday" : "i går", + "today" : "idag", + "tomorrow" : "imorgon", + "yesterday" : "igår", "_in %n day_::_in %n days_" : ["om %n dag","om %n dagar"], "_%n day ago_::_%n days ago_" : ["%n dag sedan","%n dagar sedan"], "next month" : "nästa månad", @@ -64,7 +64,7 @@ "Unknown user" : "Okänd användare", "APCu" : "APCu", "Redis" : "Redis", - "Basic settings" : "Vanliga inställningar", + "Basic settings" : "Generella inställningar", "Sharing" : "Delning", "Security" : "Säkerhet", "Encryption" : "Kryptering", 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/Template/JSConfigHelper.php b/lib/private/Template/JSConfigHelper.php index bdb747e1c9f..9f93ef784bc 100644 --- a/lib/private/Template/JSConfigHelper.php +++ b/lib/private/Template/JSConfigHelper.php @@ -155,6 +155,7 @@ class JSConfigHelper { "oc_appswebroots" => str_replace('\\/', '/', json_encode($apps_paths)), // Ugly unescape slashes waiting for better solution "datepickerFormatDate" => json_encode($this->l->l('jsdate', null)), 'nc_lastLogin' => $lastConfirmTimestamp, + 'nc_pageLoad' => time(), "dayNames" => json_encode([ (string)$this->l->t('Sunday'), (string)$this->l->t('Monday'), diff --git a/lib/private/TemplateLayout.php b/lib/private/TemplateLayout.php index a8c4bbb0d16..5e6d432c805 100644 --- a/lib/private/TemplateLayout.php +++ b/lib/private/TemplateLayout.php @@ -146,7 +146,7 @@ class TemplateLayout extends \OC_Template { if ($this->config->getSystemValue('installed', false) && $renderAs != 'error') { if (\OC::$server->getContentSecurityPolicyNonceManager()->browserSupportsCspV3()) { $jsConfigHelper = new JSConfigHelper( - \OC::$server->getL10N('core'), + \OC::$server->getL10N('lib'), \OC::$server->query(Defaults::class), \OC::$server->getAppManager(), \OC::$server->getSession(), 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/css/settings.scss b/settings/css/settings.scss index 268cb0eee5d..d2dd973e887 100644 --- a/settings/css/settings.scss +++ b/settings/css/settings.scss @@ -347,7 +347,7 @@ table.nostyle { } .token-list { td > a.icon { - opacity: 0; + transition: opacity 0.5s; } a.icon { @@ -563,36 +563,26 @@ tr:hover > td { } td.userActions { - width: 25px; - text-align: center; - position: relative; - .action { - position: relative; - top: 3px; - } + width: 44px; .toggleUserActions { - border: none; - background-color: rgba(0, 0, 0, 0); - width: 34px; - height: 34px; - margin: 0; - opacity: 0.5; - &:hover, - &:focus { - background-color: transparent; - opacity: 1; + width: 44px; + height: 44px; + position: relative; + .action { + display: block; + padding: 14px; + opacity: 0.5; + .icon-more { + display: inline-block; + } + &:hover, + &:focus { + opacity: 1; + } } } } -tr.active td.userActions .action { - opacity: 1; -} - -td.userActions .action:hover { - cursor: pointer; -} - div.recoveryPassword { left: 50em; display: block; diff --git a/settings/js/users/users.js b/settings/js/users/users.js index 1d6cb93452a..5a337c38556 100644 --- a/settings/js/users/users.js +++ b/settings/js/users/users.js @@ -101,16 +101,9 @@ var UserList = { $tdSubadmins.find('.action').tooltip({placement: 'top'}); /** - * user actions menu + * hide user actions menu for current user */ - if ($tr.find('td.userActions > span > img').length === 0 && OC.currentUser !== user.name) { - var menuImage = $('<img class="svg action">').attr({ - src: OC.imagePath('core', 'actions/more') - }); - var menuLink = $('<span class="toggleUserActions"></span>') - .append(menuImage); - $tr.find('td.userActions > span').replaceWith(menuLink); - } else if (OC.currentUser === user.name) { + if (OC.currentUser === user.name) { $tr.find('td.userActions').empty(); } diff --git a/settings/l10n/en_GB.js b/settings/l10n/en_GB.js index a245b80eb8e..58bc68070e3 100644 --- a/settings/l10n/en_GB.js +++ b/settings/l10n/en_GB.js @@ -104,9 +104,15 @@ OC.L10N.register( "Error: This app can not be enabled because it makes the server unstable" : "Error: This app can not be enabled because it makes the server unstable", "Error: Could not disable broken app" : "Error: Could not disable broken app", "Error while disabling broken app" : "Error whilst disabling broken app", + "App up to date" : "App up to date", + "Upgrading …" : "Upgrading …", + "Could not upgrade app" : "Could not upgrade app", "Updated" : "Updated", "Removing …" : "Removing …", + "Could not remove app" : "Could not remove app", "Remove" : "Remove", + "The app has been enabled but needs to be upgraded. You will be redirected to the upgrade 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.", + "App upgrade" : "App upgrade", "Approved" : "Approved", "Experimental" : "Experimental", "No apps found for {query}" : "No apps found for {query}", diff --git a/settings/l10n/en_GB.json b/settings/l10n/en_GB.json index 9d70d91483a..abe5fba5ed7 100644 --- a/settings/l10n/en_GB.json +++ b/settings/l10n/en_GB.json @@ -102,9 +102,15 @@ "Error: This app can not be enabled because it makes the server unstable" : "Error: This app can not be enabled because it makes the server unstable", "Error: Could not disable broken app" : "Error: Could not disable broken app", "Error while disabling broken app" : "Error whilst disabling broken app", + "App up to date" : "App up to date", + "Upgrading …" : "Upgrading …", + "Could not upgrade app" : "Could not upgrade app", "Updated" : "Updated", "Removing …" : "Removing …", + "Could not remove app" : "Could not remove app", "Remove" : "Remove", + "The app has been enabled but needs to be upgraded. You will be redirected to the upgrade 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.", + "App upgrade" : "App upgrade", "Approved" : "Approved", "Experimental" : "Experimental", "No apps found for {query}" : "No apps found for {query}", 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/ka_GE.js b/settings/l10n/ka_GE.js index 16d388195de..d395e0d6a2f 100644 --- a/settings/l10n/ka_GE.js +++ b/settings/l10n/ka_GE.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/ka_GE.json b/settings/l10n/ka_GE.json index fdd581935bc..144fc0046d4 100644 --- a/settings/l10n/ka_GE.json +++ b/settings/l10n/ka_GE.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/ko.js b/settings/l10n/ko.js index a03387d6acb..620ad0900c0 100644 --- a/settings/l10n/ko.js +++ b/settings/l10n/ko.js @@ -228,7 +228,7 @@ OC.L10N.register( "Authentication required" : "인증 필요함", "Server address" : "서버 주소", "Port" : "포트", - "Credentials" : "자격 정보", + "Credentials" : "인증 정보", "SMTP Username" : "SMTP 사용자 이름", "SMTP Password" : "SMTP 암호", "Store credentials" : "인증 정보 저장", @@ -424,7 +424,7 @@ OC.L10N.register( "Last cron job execution: %s." : "마지막 cron 작업 실행: %s.", "Last cron job execution: %s. Something seems wrong." : "마지막 cron 작업 실행: %s. 문제가 발생한 것 같습니다.", "Cron was not executed yet!" : "Cron이 실행되지 않았습니다!", - "cron.php is registered at a webcron service to call cron.php every 15 minutes over http." : "cron.php는 webcron 서비스에 등록되어 HTTP로 15분마다 cron.php에 접근합니다.", + "cron.php is registered at a webcron service to call cron.php every 15 minutes over http." : "15분마다 HTTP를 통해 cron.php를 실행하는 webcron 서비스에 cron.php가 등록되었습니다.", "Use system's cron service to call the cron.php file every 15 minutes." : "시스템의 cron 서비스를 통하여 15분마다 cron.php 파일을 실행합니다.", "To run this you need the PHP posix extension. See {linkstart}PHP documentation{linkend} for more details." : "실행하려면 PHP POSIX 확장이 필요합니다. 자세한 내용은 {linkstart}PHP 사용 설명서{linkend}를 참고하십시오.", "Allow username autocompletion in share dialog. If this is disabled the full username needs to be entered." : "공유 대화 상자에서 사용자 이름 자동 완성을 사용합니다. 이 설정을 사용하지 않으면 전체 사용자 이름을 입력해야 합니다.", diff --git a/settings/l10n/ko.json b/settings/l10n/ko.json index d2fd97c6ab7..e1b56c2ee16 100644 --- a/settings/l10n/ko.json +++ b/settings/l10n/ko.json @@ -226,7 +226,7 @@ "Authentication required" : "인증 필요함", "Server address" : "서버 주소", "Port" : "포트", - "Credentials" : "자격 정보", + "Credentials" : "인증 정보", "SMTP Username" : "SMTP 사용자 이름", "SMTP Password" : "SMTP 암호", "Store credentials" : "인증 정보 저장", @@ -422,7 +422,7 @@ "Last cron job execution: %s." : "마지막 cron 작업 실행: %s.", "Last cron job execution: %s. Something seems wrong." : "마지막 cron 작업 실행: %s. 문제가 발생한 것 같습니다.", "Cron was not executed yet!" : "Cron이 실행되지 않았습니다!", - "cron.php is registered at a webcron service to call cron.php every 15 minutes over http." : "cron.php는 webcron 서비스에 등록되어 HTTP로 15분마다 cron.php에 접근합니다.", + "cron.php is registered at a webcron service to call cron.php every 15 minutes over http." : "15분마다 HTTP를 통해 cron.php를 실행하는 webcron 서비스에 cron.php가 등록되었습니다.", "Use system's cron service to call the cron.php file every 15 minutes." : "시스템의 cron 서비스를 통하여 15분마다 cron.php 파일을 실행합니다.", "To run this you need the PHP posix extension. See {linkstart}PHP documentation{linkend} for more details." : "실행하려면 PHP POSIX 확장이 필요합니다. 자세한 내용은 {linkstart}PHP 사용 설명서{linkend}를 참고하십시오.", "Allow username autocompletion in share dialog. If this is disabled the full username needs to be entered." : "공유 대화 상자에서 사용자 이름 자동 완성을 사용합니다. 이 설정을 사용하지 않으면 전체 사용자 이름을 입력해야 합니다.", 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/nl.js b/settings/l10n/nl.js index b16fb0c4576..6abb70048b0 100644 --- a/settings/l10n/nl.js +++ b/settings/l10n/nl.js @@ -104,9 +104,15 @@ OC.L10N.register( "Error: This app can not be enabled because it makes the server unstable" : "Fout: Deze app kan niet ingeschakeld worden, omdat die de server onstabiel maakt", "Error: Could not disable broken app" : "Fout: Kan de beschadigde app niet uitschakelen", "Error while disabling broken app" : "Fout bij het uitschakelen van de beschadigde app", + "App up to date" : "App actueel", + "Upgrading …" : "Upgraden …", + "Could not upgrade app" : "Kon de app niet upgraden", "Updated" : "Bijgewerkt", "Removing …" : "Verwijderen ...", + "Could not remove app" : "Kon de app niet verwijderen", "Remove" : "Verwijderen", + "The app has been enabled but needs to be upgraded. You will be redirected to the upgrade page in 5 seconds." : "De app is ingeschakeld maar moet worden ge-upgraded. Je wordt over 5 seconden doorgeleid naar de upgradepagina.", + "App upgrade" : "App upgrade", "Approved" : "Goedgekeurd", "Experimental" : "Experimenteel", "No apps found for {query}" : "Geen apps gevonden voor {query}", diff --git a/settings/l10n/nl.json b/settings/l10n/nl.json index dbe67b7bf6c..f25fbe9dd1c 100644 --- a/settings/l10n/nl.json +++ b/settings/l10n/nl.json @@ -102,9 +102,15 @@ "Error: This app can not be enabled because it makes the server unstable" : "Fout: Deze app kan niet ingeschakeld worden, omdat die de server onstabiel maakt", "Error: Could not disable broken app" : "Fout: Kan de beschadigde app niet uitschakelen", "Error while disabling broken app" : "Fout bij het uitschakelen van de beschadigde app", + "App up to date" : "App actueel", + "Upgrading …" : "Upgraden …", + "Could not upgrade app" : "Kon de app niet upgraden", "Updated" : "Bijgewerkt", "Removing …" : "Verwijderen ...", + "Could not remove app" : "Kon de app niet verwijderen", "Remove" : "Verwijderen", + "The app has been enabled but needs to be upgraded. You will be redirected to the upgrade page in 5 seconds." : "De app is ingeschakeld maar moet worden ge-upgraded. Je wordt over 5 seconden doorgeleid naar de upgradepagina.", + "App upgrade" : "App upgrade", "Approved" : "Goedgekeurd", "Experimental" : "Experimenteel", "No apps found for {query}" : "Geen apps gevonden voor {query}", diff --git a/settings/l10n/ro.js b/settings/l10n/ro.js index e5172eeabc3..874c43c5594 100644 --- a/settings/l10n/ro.js +++ b/settings/l10n/ro.js @@ -1,6 +1,14 @@ OC.L10N.register( "settings", { + "You changed your password" : "Ți-ai schimbat parola", + "You changed your email address" : "Ți-ai schimbat adresa de email", + "Your email address was changed by an administrator" : "Adresa ta de email a fost modificată de un administrator", + "Security" : "Securitate", + "Your apps" : "Aplicațiile tale", + "Updates" : "Actualizări", + "Enabled apps" : "Aplicații active", + "Disabled apps" : "Aplicații inactive", "Wrong password" : "Parolă greșită", "Saved" : "Salvat", "No user supplied" : "Nu a fost furnizat niciun utilizator", @@ -12,24 +20,38 @@ OC.L10N.register( "Group already exists." : "Grupul există deja.", "Unable to add group." : "Nu se poate adăuga grupul.", "Unable to delete group." : "Nu se poate șterge grupul.", + "Invalid SMTP password." : "Parolă SMTP invalidă.", + "Email setting test" : "Test setări email", "A problem occurred while sending the email. Please revise your settings. (Error: %s)" : " A apărut o problemă la trimiterea emailului. Verifică-ți setărie. (Eroare: %s)", "You need to set your user email before being able to send test emails." : "Trebuie să îți setezi emailul de utilizator înainte de a putea să trimiți emailuri.", "Invalid mail address" : "Adresa mail invalidă", + "No valid group selected" : "Niciun grup valid selectat", "A user with that name already exists." : "Există deja un utilizator cu acest nume.", "Unable to create user." : "Imposibil de creat utilizatorul.", "Unable to delete user." : "Imposibil de șters utilizatorul.", + "Error while enabling user." : "Eroare în timpul activării utilizatorului.", + "Error while disabling user." : "Eroare în timpul dezactivării utilizatorului.", "Unable to change full name" : "Nu s-a putut schimba numele complet", "Your full name has been changed." : "Numele tău complet a fost schimbat.", "Forbidden" : "Interzis", "Invalid user" : "Utilizator invalid", "Unable to change mail address" : "Nu s-a putut schimba adresa email", "Email saved" : "E-mail salvat", + "Email address changed for %s" : "Adresa de email schimbată în %s", + "The new email address is %s" : "Adresa de email nouă este%s", "Your %s account was created" : "Contul tău %s a fost creat", + "Your username is: %s" : "Utilizatorul tău este: %s", + "Set your password" : "Setează parola", + "Install Client" : "Instalează client", + "Password confirmation is required" : "Confirmarea parolei este necesară", "Couldn't remove app." : "Nu s-a putut înlătura aplicația.", "Couldn't update app." : "Aplicaţia nu s-a putut actualiza.", + "Are you really sure you want add {domain} as trusted domain?" : "Ești sigur că vrei sa adaugi {domain} ca domeniu de încredere?", "Add trusted domain" : "Adaugă domeniu de încredere", "Migration in progress. Please wait until the migration is finished" : "Migrare în progres. Așteaptă până când migrarea este finalizată", "Migration started …" : "Migrarea a început...", + "Not saved" : "Nu a fost salvat", + "Sending…" : "Se trimite...", "Email sent" : "Mesajul a fost expediat", "Official" : "Oficial", "All" : "Toate ", @@ -43,8 +65,25 @@ OC.L10N.register( "Approved" : "Aprobat", "Experimental" : "Experimental", "Disconnect" : "Deconectare", + "Edge" : "Edge", + "Firefox" : "Firefox", + "Google Chrome" : "Google Chrome", + "Safari" : "Safari", + "Google Chrome for Android" : "Google Chrome for Android", + "iPhone iOS" : "iPhone iOS", + "iPad iOS" : "iPad iOS", + "iOS Client" : "iOS Client", + "Android Client" : "Android Client", + "Copy" : "Copiază", + "Copied!" : "S-a copiat!", + "Not supported!" : "Nu este suportat!", + "Press ⌘-C to copy." : "Apasă ⌘-C pentru copiere.", + "Press Ctrl-C to copy." : "Apasă Ctrl-C pentru copiere.", "Valid until {date}" : "Valabil până la {date}", "Delete" : "Șterge", + "Contacts" : "Contacte", + "Verify" : "Verifică", + "Verifying …" : "Se verifică ...", "Select a profile picture" : "Selectează o imagine de profil", "Very weak password" : "Parolă foarte slabă", "Weak password" : "Parolă slabă", @@ -57,8 +96,13 @@ OC.L10N.register( "A valid group name must be provided" : "Trebuie furnizat un nume de grup valid", "deleted {groupName}" : "{groupName} s-a șters", "undo" : "Anulează ultima acțiune", + "{size} used" : "{size} folosită", "never" : "niciodată", "deleted {userName}" : "{userName} șters", + "Add group" : "Adaugă grup", + "Invalid quota value \"{val}\"" : "Valoare cotă invalidă \"{val}\"", + "no group" : "niciun grup", + "Password successfully changed" : "Parola a fost modificată cu succes.", "Changing the password will result in data loss, because data recovery is not available for this user" : "Schimbarea parolei va rezulta în pierderea datelor deoarece recuperarea acestora nu este disponibilă pentru acest utilizator", "A valid username must be provided" : "Trebuie să furnizaţi un nume de utilizator valid", "Error creating user: {message}" : "Eroare la crearea utilizatorului: {message}", @@ -87,7 +131,10 @@ OC.L10N.register( "Commercial support" : "Suport comercial", "None" : "Niciuna", "Login" : "Autentificare", + "Plain" : "Plain", "NT LAN Manager" : "NT LAN Manager", + "SSL/TLS" : "SSL/TLS", + "STARTTLS" : "STARTTLS", "Email server" : "Server de email", "Open documentation" : "Deschide documentația", "Send mode" : "Modul de expediere", @@ -132,24 +179,38 @@ OC.L10N.register( "Advanced monitoring" : "Monitorizare avansată", "Profile picture" : "Imagine de profil", "Upload new" : "Încarcă una nouă", + "Select from Files" : "Selectează din fișiere", "Remove image" : "Înlătură imagine", "png or jpg, max. 20 MB" : "png sau jpg, max. 20 MB", "Cancel" : "Anulare", + "Choose as profile picture" : "Alege ca imagine de profil", "Full name" : "Nume complet", "Email" : "Email", "Your email address" : "Adresa ta de email", + "Phone number" : "Număr telefon", + "Your phone number" : "Numărul tău de telefon", + "Address" : "Adresă", + "Your postal address" : "Adresă poștală", + "Website" : "Site web", + "Link https://…" : "Link https://…", + "Twitter" : "Twitter", "Language" : "Limba", "Help translate" : "Ajută la traducere", "Password" : "Parolă", "Current password" : "Parola curentă", "New password" : "Noua parolă", "Change password" : "Schimbă parola", + "Device" : "Dispozitiv", + "App name" : "Numele aplicației", "Username" : "Nume utilizator", + "Settings" : "Setări", "E-Mail" : "Email", "Create" : "Crează", "Admin Recovery Password" : "Parolă de recuperare a Administratorului", "Enter the recovery password in order to recover the users files during password change" : "Introdu parola de recuperare pentru a recupera fișierele utilizatorilor în timpul schimbării parolei", + "Everyone" : "Toți", "Admins" : "Administratori", + "Disabled" : "Dezactivați", "Unlimited" : "Nelimitată", "Other" : "Altele", "Quota" : "Cotă", @@ -180,11 +241,15 @@ OC.L10N.register( "Sync clients" : "Sincronizează clienții", "The PHP module 'fileinfo' is missing. We strongly recommend to enable this module to get best results with mime-type detection." : "Modulul PHP \"Fileinfo\" lipsește. Va recomandam sa activaţi acest modul pentru a obține cele mai bune rezultate cu detectarea mime-type.", "Cron" : "Cron", + "Cron was not executed yet!" : "Cron nu a fost executat înca!", + "Uninstall app" : "Dezinstalează aplicația", "Cheers!" : "Noroc!", "Get the apps to sync your files" : "Ia acum aplicatia pentru sincronizarea fisierelor ", "Desktop client" : "Client Desktop", "Android app" : "Aplicatie Android", "iOS app" : "Aplicație iOS", - "Name" : "Nume" + "Name" : "Nume", + "Group name" : "Numele grupului", + "Verifying" : "Se verifică" }, "nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));"); diff --git a/settings/l10n/ro.json b/settings/l10n/ro.json index 6ec743f02fb..44e45dbdc6d 100644 --- a/settings/l10n/ro.json +++ b/settings/l10n/ro.json @@ -1,4 +1,12 @@ { "translations": { + "You changed your password" : "Ți-ai schimbat parola", + "You changed your email address" : "Ți-ai schimbat adresa de email", + "Your email address was changed by an administrator" : "Adresa ta de email a fost modificată de un administrator", + "Security" : "Securitate", + "Your apps" : "Aplicațiile tale", + "Updates" : "Actualizări", + "Enabled apps" : "Aplicații active", + "Disabled apps" : "Aplicații inactive", "Wrong password" : "Parolă greșită", "Saved" : "Salvat", "No user supplied" : "Nu a fost furnizat niciun utilizator", @@ -10,24 +18,38 @@ "Group already exists." : "Grupul există deja.", "Unable to add group." : "Nu se poate adăuga grupul.", "Unable to delete group." : "Nu se poate șterge grupul.", + "Invalid SMTP password." : "Parolă SMTP invalidă.", + "Email setting test" : "Test setări email", "A problem occurred while sending the email. Please revise your settings. (Error: %s)" : " A apărut o problemă la trimiterea emailului. Verifică-ți setărie. (Eroare: %s)", "You need to set your user email before being able to send test emails." : "Trebuie să îți setezi emailul de utilizator înainte de a putea să trimiți emailuri.", "Invalid mail address" : "Adresa mail invalidă", + "No valid group selected" : "Niciun grup valid selectat", "A user with that name already exists." : "Există deja un utilizator cu acest nume.", "Unable to create user." : "Imposibil de creat utilizatorul.", "Unable to delete user." : "Imposibil de șters utilizatorul.", + "Error while enabling user." : "Eroare în timpul activării utilizatorului.", + "Error while disabling user." : "Eroare în timpul dezactivării utilizatorului.", "Unable to change full name" : "Nu s-a putut schimba numele complet", "Your full name has been changed." : "Numele tău complet a fost schimbat.", "Forbidden" : "Interzis", "Invalid user" : "Utilizator invalid", "Unable to change mail address" : "Nu s-a putut schimba adresa email", "Email saved" : "E-mail salvat", + "Email address changed for %s" : "Adresa de email schimbată în %s", + "The new email address is %s" : "Adresa de email nouă este%s", "Your %s account was created" : "Contul tău %s a fost creat", + "Your username is: %s" : "Utilizatorul tău este: %s", + "Set your password" : "Setează parola", + "Install Client" : "Instalează client", + "Password confirmation is required" : "Confirmarea parolei este necesară", "Couldn't remove app." : "Nu s-a putut înlătura aplicația.", "Couldn't update app." : "Aplicaţia nu s-a putut actualiza.", + "Are you really sure you want add {domain} as trusted domain?" : "Ești sigur că vrei sa adaugi {domain} ca domeniu de încredere?", "Add trusted domain" : "Adaugă domeniu de încredere", "Migration in progress. Please wait until the migration is finished" : "Migrare în progres. Așteaptă până când migrarea este finalizată", "Migration started …" : "Migrarea a început...", + "Not saved" : "Nu a fost salvat", + "Sending…" : "Se trimite...", "Email sent" : "Mesajul a fost expediat", "Official" : "Oficial", "All" : "Toate ", @@ -41,8 +63,25 @@ "Approved" : "Aprobat", "Experimental" : "Experimental", "Disconnect" : "Deconectare", + "Edge" : "Edge", + "Firefox" : "Firefox", + "Google Chrome" : "Google Chrome", + "Safari" : "Safari", + "Google Chrome for Android" : "Google Chrome for Android", + "iPhone iOS" : "iPhone iOS", + "iPad iOS" : "iPad iOS", + "iOS Client" : "iOS Client", + "Android Client" : "Android Client", + "Copy" : "Copiază", + "Copied!" : "S-a copiat!", + "Not supported!" : "Nu este suportat!", + "Press ⌘-C to copy." : "Apasă ⌘-C pentru copiere.", + "Press Ctrl-C to copy." : "Apasă Ctrl-C pentru copiere.", "Valid until {date}" : "Valabil până la {date}", "Delete" : "Șterge", + "Contacts" : "Contacte", + "Verify" : "Verifică", + "Verifying …" : "Se verifică ...", "Select a profile picture" : "Selectează o imagine de profil", "Very weak password" : "Parolă foarte slabă", "Weak password" : "Parolă slabă", @@ -55,8 +94,13 @@ "A valid group name must be provided" : "Trebuie furnizat un nume de grup valid", "deleted {groupName}" : "{groupName} s-a șters", "undo" : "Anulează ultima acțiune", + "{size} used" : "{size} folosită", "never" : "niciodată", "deleted {userName}" : "{userName} șters", + "Add group" : "Adaugă grup", + "Invalid quota value \"{val}\"" : "Valoare cotă invalidă \"{val}\"", + "no group" : "niciun grup", + "Password successfully changed" : "Parola a fost modificată cu succes.", "Changing the password will result in data loss, because data recovery is not available for this user" : "Schimbarea parolei va rezulta în pierderea datelor deoarece recuperarea acestora nu este disponibilă pentru acest utilizator", "A valid username must be provided" : "Trebuie să furnizaţi un nume de utilizator valid", "Error creating user: {message}" : "Eroare la crearea utilizatorului: {message}", @@ -85,7 +129,10 @@ "Commercial support" : "Suport comercial", "None" : "Niciuna", "Login" : "Autentificare", + "Plain" : "Plain", "NT LAN Manager" : "NT LAN Manager", + "SSL/TLS" : "SSL/TLS", + "STARTTLS" : "STARTTLS", "Email server" : "Server de email", "Open documentation" : "Deschide documentația", "Send mode" : "Modul de expediere", @@ -130,24 +177,38 @@ "Advanced monitoring" : "Monitorizare avansată", "Profile picture" : "Imagine de profil", "Upload new" : "Încarcă una nouă", + "Select from Files" : "Selectează din fișiere", "Remove image" : "Înlătură imagine", "png or jpg, max. 20 MB" : "png sau jpg, max. 20 MB", "Cancel" : "Anulare", + "Choose as profile picture" : "Alege ca imagine de profil", "Full name" : "Nume complet", "Email" : "Email", "Your email address" : "Adresa ta de email", + "Phone number" : "Număr telefon", + "Your phone number" : "Numărul tău de telefon", + "Address" : "Adresă", + "Your postal address" : "Adresă poștală", + "Website" : "Site web", + "Link https://…" : "Link https://…", + "Twitter" : "Twitter", "Language" : "Limba", "Help translate" : "Ajută la traducere", "Password" : "Parolă", "Current password" : "Parola curentă", "New password" : "Noua parolă", "Change password" : "Schimbă parola", + "Device" : "Dispozitiv", + "App name" : "Numele aplicației", "Username" : "Nume utilizator", + "Settings" : "Setări", "E-Mail" : "Email", "Create" : "Crează", "Admin Recovery Password" : "Parolă de recuperare a Administratorului", "Enter the recovery password in order to recover the users files during password change" : "Introdu parola de recuperare pentru a recupera fișierele utilizatorilor în timpul schimbării parolei", + "Everyone" : "Toți", "Admins" : "Administratori", + "Disabled" : "Dezactivați", "Unlimited" : "Nelimitată", "Other" : "Altele", "Quota" : "Cotă", @@ -178,11 +239,15 @@ "Sync clients" : "Sincronizează clienții", "The PHP module 'fileinfo' is missing. We strongly recommend to enable this module to get best results with mime-type detection." : "Modulul PHP \"Fileinfo\" lipsește. Va recomandam sa activaţi acest modul pentru a obține cele mai bune rezultate cu detectarea mime-type.", "Cron" : "Cron", + "Cron was not executed yet!" : "Cron nu a fost executat înca!", + "Uninstall app" : "Dezinstalează aplicația", "Cheers!" : "Noroc!", "Get the apps to sync your files" : "Ia acum aplicatia pentru sincronizarea fisierelor ", "Desktop client" : "Client Desktop", "Android app" : "Aplicatie Android", "iOS app" : "Aplicație iOS", - "Name" : "Nume" + "Name" : "Nume", + "Group name" : "Numele grupului", + "Verifying" : "Se verifică" },"pluralForm" :"nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));" }
\ No newline at end of file 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/sv.js b/settings/l10n/sv.js index 5f34ce0a9fb..33b3f2ea33d 100644 --- a/settings/l10n/sv.js +++ b/settings/l10n/sv.js @@ -327,7 +327,7 @@ OC.L10N.register( "Twitter handle @…" : "Twitter handle @…", "You are member of the following groups:" : "Du är medlem i följande grupper:", "Language" : "Språk", - "Help translate" : "Hjälp att översätta", + "Help translate" : "Hjälp oss att översätta", "Password" : "Lösenord", "Current password" : "Nuvarande lösenord", "New password" : "Nytt lösenord", diff --git a/settings/l10n/sv.json b/settings/l10n/sv.json index 27dea71ef5b..e8e77546259 100644 --- a/settings/l10n/sv.json +++ b/settings/l10n/sv.json @@ -325,7 +325,7 @@ "Twitter handle @…" : "Twitter handle @…", "You are member of the following groups:" : "Du är medlem i följande grupper:", "Language" : "Språk", - "Help translate" : "Hjälp att översätta", + "Help translate" : "Hjälp oss att översätta", "Password" : "Lösenord", "Current password" : "Nuvarande lösenord", "New password" : "Nytt lösenord", 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/settings/templates/users/part.userlist.php b/settings/templates/users/part.userlist.php index 146e35d11ac..aaf20b6eaef 100644 --- a/settings/templates/users/part.userlist.php +++ b/settings/templates/users/part.userlist.php @@ -1,4 +1,4 @@ -<table id="userlist" class="hascontrols grid" data-groups="<?php p($_['allGroups']);?>"> +<table id="userlist" class="grid" data-groups="<?php p($_['allGroups']);?>"> <thead> <tr> <th id="headerAvatar" scope="col"></th> @@ -64,19 +64,22 @@ <td class="storageLocation"></td> <td class="userBackend"></td> <td class="lastLogin"></td> - <td class="userActions"><span></span> - <div class="popovermenu bubble menu"> - <ul class="userActionsMenu"> - <li> - <a href="#" class="menuitem action-togglestate permanent" data-action="togglestate"></a> - </li> - <li> - <a href="#" class="menuitem action-remove permanent" data-action="remove"> - <span class="icon icon-delete"></span> - <span><?php p($l->t('Delete')); ?></span> - </a> - </li> - </ul> + <td class="userActions"> + <div class="toggleUserActions"> + <a class="action"><span class="icon-more"></span></a> + <div class="popovermenu bubble menu"> + <ul class="userActionsMenu"> + <li> + <a href="#" class="menuitem action-togglestate permanent" data-action="togglestate"></a> + </li> + <li> + <a href="#" class="menuitem action-remove permanent" data-action="remove"> + <span class="icon icon-delete"></span> + <span><?php p($l->t('Delete')); ?></span> + </a> + </li> + </ul> + </div> </div> </td> </tr> diff --git a/tests/acceptance/features/app-files.feature b/tests/acceptance/features/app-files.feature index 2cb43611b9a..ef3d07ae499 100644 --- a/tests/acceptance/features/app-files.feature +++ b/tests/acceptance/features/app-files.feature @@ -23,6 +23,13 @@ Feature: app-files When I open the details view for "welcome.txt" Then I see that the details view for "All files" section is open + Scenario: rename a file with the details view open + Given I am logged in + And I open the details view for "welcome.txt" + When I rename "welcome.txt" to "farewell.txt" + Then I see that the file list contains a file named "farewell.txt" + And I see that the file name shown in the details view is "farewell.txt" + Scenario: open the menu in a public shared link Given I act as John And I am logged in diff --git a/tests/acceptance/features/bootstrap/FilesAppContext.php b/tests/acceptance/features/bootstrap/FilesAppContext.php index 4951dc43f1d..117f3b54fb8 100644 --- a/tests/acceptance/features/bootstrap/FilesAppContext.php +++ b/tests/acceptance/features/bootstrap/FilesAppContext.php @@ -89,6 +89,15 @@ class FilesAppContext implements Context, ActorAwareInterface { /** * @return Locator */ + public static function fileNameInCurrentSectionDetailsView() { + return Locator::forThe()->css(".fileName")-> + descendantOf(self::currentSectionDetailsView())-> + describedAs("File name in current section details view in Files app"); + } + + /** + * @return Locator + */ public static function fileDetailsInCurrentSectionDetailsViewWithText($fileDetailsText) { return Locator::forThe()->xpath("//span[normalize-space() = '$fileDetailsText']")-> descendantOf(self::fileDetailsInCurrentSectionDetailsView())-> @@ -319,6 +328,14 @@ class FilesAppContext implements Context, ActorAwareInterface { /** * @return Locator */ + public static function renameInputForFile($fileName) { + return Locator::forThe()->css("input.filename")->descendantOf(self::rowForFile($fileName))-> + describedAs("Rename input for file $fileName in Files app"); + } + + /** + * @return Locator + */ public static function shareActionForFile($fileName) { return Locator::forThe()->css(".action-share")->descendantOf(self::rowForFile($fileName))-> describedAs("Share action for file $fileName in Files app"); @@ -350,6 +367,13 @@ class FilesAppContext implements Context, ActorAwareInterface { /** * @return Locator */ + public static function renameMenuItem() { + return self::fileActionsMenuItemFor("Rename"); + } + + /** + * @return Locator + */ public static function addToFavoritesMenuItem() { return self::fileActionsMenuItemFor("Add to favorites"); } @@ -418,6 +442,17 @@ class FilesAppContext implements Context, ActorAwareInterface { } /** + * @Given I rename :fileName1 to :fileName2 + */ + public function iRenameTo($fileName1, $fileName2) { + $this->actor->find(self::fileActionsMenuButtonForFile($fileName1), 10)->click(); + + $this->actor->find(self::renameMenuItem(), 2)->click(); + + $this->actor->find(self::renameInputForFile($fileName1), 10)->setValue($fileName2 . "\r"); + } + + /** * @Given I mark :fileName as favorite */ public function iMarkAsFavorite($fileName) { @@ -543,6 +578,13 @@ class FilesAppContext implements Context, ActorAwareInterface { } /** + * @Then I see that the file list contains a file named :fileName + */ + public function iSeeThatTheFileListContainsAFileNamed($fileName) { + PHPUnit_Framework_Assert::assertNotNull($this->actor->find(self::rowForFile($fileName), 10)); + } + + /** * @Then I see that :fileName1 precedes :fileName2 in the file list */ public function iSeeThatPrecedesInTheFileList($fileName1, $fileName2) { @@ -564,6 +606,14 @@ class FilesAppContext implements Context, ActorAwareInterface { } /** + * @Then I see that the file name shown in the details view is :fileName + */ + public function iSeeThatTheFileNameShownInTheDetailsViewIs($fileName) { + PHPUnit_Framework_Assert::assertEquals( + $this->actor->find(self::fileNameInCurrentSectionDetailsView(), 10)->getText(), $fileName); + } + + /** * @Then I see that the input field for tags in the details view is shown */ public function iSeeThatTheInputFieldForTagsInTheDetailsViewIsShown() { diff --git a/tests/acceptance/features/bootstrap/LoginPageContext.php b/tests/acceptance/features/bootstrap/LoginPageContext.php index 560dd83f308..1496e3030c2 100644 --- a/tests/acceptance/features/bootstrap/LoginPageContext.php +++ b/tests/acceptance/features/bootstrap/LoginPageContext.php @@ -66,7 +66,7 @@ class LoginPageContext implements Context, ActorAwareInterface { * @return Locator */ public static function wrongPasswordMessage() { - return Locator::forThe()->xpath("//*[@class = 'warning' and normalize-space() = 'Wrong password.']")-> + return Locator::forThe()->xpath("//*[@class = 'warning wrongPasswordMsg' and normalize-space() = 'Wrong password.']")-> describedAs("Wrong password message in Login page"); } 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') diff --git a/version.php b/version.php index 53272cf32fe..c94a897f4e7 100644 --- a/version.php +++ b/version.php @@ -29,10 +29,10 @@ // between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel // when updating major/minor version number. -$OC_Version = array(13, 0, 0, 8); +$OC_Version = array(13, 0, 0, 9); // The human readable string -$OC_VersionString = '13.0.0 Beta 3'; +$OC_VersionString = '13.0.0 Beta 4'; $OC_VersionCanBeUpgradedFrom = [ 'nextcloud' => [ |