diff options
213 files changed, 8483 insertions, 7257 deletions
diff --git a/3rdparty b/3rdparty -Subproject 9232ccdf02be87d72b4827a2cfa33a2d75b0ee7 +Subproject ae67e91bac6e959fb9666b997c02fb45e63aadf diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2ed7920a830..5f9dce7a3bf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,7 +58,7 @@ like `git config --global alias.ci 'commit -s'`. Now you can commit with In case you are not sure how to add or update the license header correctly please have a look at [contribute/HowToApplyALicense.md][applyalicense] -[devmanual]: https://docs.nextcloud.org/server/11/developer_manual/ +[devmanual]: https://docs.nextcloud.org/server/12/developer_manual/ [dcofile]: https://github.com/nextcloud/server/blob/master/contribute/developer-certificate-of-origin [applyalicense]: https://github.com/nextcloud/server/blob/master/contribute/HowToApplyALicense.md diff --git a/apps/comments/l10n/de_DE.js b/apps/comments/l10n/de_DE.js index ce1e52ffc60..3f7f94fb7c5 100644 --- a/apps/comments/l10n/de_DE.js +++ b/apps/comments/l10n/de_DE.js @@ -20,16 +20,16 @@ OC.L10N.register( "Comment" : "Kommentar", "You commented" : "Sie haben kommentiert", "%1$s commented" : "%1$s kommentierte", - "{author} commented" : "{author} kommentiert", + "{author} commented" : "{author} kommentierte", "You commented on %1$s" : "Sie haben %1$s kommentiert", "You commented on {file}" : "Sie haben {file} kommentiert", "%1$s commented on %2$s" : "%1$s kommentierte %2$s", "{author} commented on {file}" : "{author} hat {file} kommentiert", "<strong>Comments</strong> for files" : "<strong>Kommentare</strong> für Dateien", - "A (now) deleted user mentioned you in a comment on “%s”" : "Ein (nun) gelöschter Nutzer erwähnt Sie in einem Kommentar zu \"%s\"", - "A (now) deleted user mentioned you in a comment on “{file}”" : "Ein (nun) gelöschter Nutzer erwähnt Sie in einem Kommentar zu “{file}”", - "%1$s mentioned you in a comment on “%2$s”" : "%1$s erwähnt Sie in einem Kommentar zu “%2$s”", - "{user} mentioned you in a comment on “{file}”" : "{user} erwähnt Sie in einem Kommentar zu “{file}”", + "A (now) deleted user mentioned you in a comment on “%s”" : "Ein (nun) gelöschter Benutzer hat Sie in einem Kommentar zu \"%s\" erwähnt", + "A (now) deleted user mentioned you in a comment on “{file}”" : "Ein (nun) gelöschter Benutzer hat Sie in einem Kommentar zu “{file}” erwähnt", + "%1$s mentioned you in a comment on “%2$s”" : "%1$s hat Sie in einem Kommentar zu “%2$s” erwähnt.", + "{user} mentioned you in a comment on “{file}”" : "{user} hat Sie in einem Kommentar zu “{file}” erwähnt", "Type in a new comment..." : "Neuen Kommentar eingeben...", "No other comments available" : "Keine weiteren Kommentare verfügbar", "More comments..." : "Weitere Kommentare...", diff --git a/apps/comments/l10n/de_DE.json b/apps/comments/l10n/de_DE.json index e83ee533cb2..de0474199ef 100644 --- a/apps/comments/l10n/de_DE.json +++ b/apps/comments/l10n/de_DE.json @@ -18,16 +18,16 @@ "Comment" : "Kommentar", "You commented" : "Sie haben kommentiert", "%1$s commented" : "%1$s kommentierte", - "{author} commented" : "{author} kommentiert", + "{author} commented" : "{author} kommentierte", "You commented on %1$s" : "Sie haben %1$s kommentiert", "You commented on {file}" : "Sie haben {file} kommentiert", "%1$s commented on %2$s" : "%1$s kommentierte %2$s", "{author} commented on {file}" : "{author} hat {file} kommentiert", "<strong>Comments</strong> for files" : "<strong>Kommentare</strong> für Dateien", - "A (now) deleted user mentioned you in a comment on “%s”" : "Ein (nun) gelöschter Nutzer erwähnt Sie in einem Kommentar zu \"%s\"", - "A (now) deleted user mentioned you in a comment on “{file}”" : "Ein (nun) gelöschter Nutzer erwähnt Sie in einem Kommentar zu “{file}”", - "%1$s mentioned you in a comment on “%2$s”" : "%1$s erwähnt Sie in einem Kommentar zu “%2$s”", - "{user} mentioned you in a comment on “{file}”" : "{user} erwähnt Sie in einem Kommentar zu “{file}”", + "A (now) deleted user mentioned you in a comment on “%s”" : "Ein (nun) gelöschter Benutzer hat Sie in einem Kommentar zu \"%s\" erwähnt", + "A (now) deleted user mentioned you in a comment on “{file}”" : "Ein (nun) gelöschter Benutzer hat Sie in einem Kommentar zu “{file}” erwähnt", + "%1$s mentioned you in a comment on “%2$s”" : "%1$s hat Sie in einem Kommentar zu “%2$s” erwähnt.", + "{user} mentioned you in a comment on “{file}”" : "{user} hat Sie in einem Kommentar zu “{file}” erwähnt", "Type in a new comment..." : "Neuen Kommentar eingeben...", "No other comments available" : "Keine weiteren Kommentare verfügbar", "More comments..." : "Weitere Kommentare...", diff --git a/apps/comments/l10n/sv.js b/apps/comments/l10n/sv.js index e08487f67dd..1f66e311e75 100644 --- a/apps/comments/l10n/sv.js +++ b/apps/comments/l10n/sv.js @@ -9,17 +9,17 @@ OC.L10N.register( "Cancel" : "Avbryt", "Edit comment" : "Redigera kommentar", "[Deleted user]" : "[Raderad användare]", - "No comments yet, start the conversation!" : "Inga kommentarer än, börja konversationen!", + "No comments yet, start the conversation!" : "Inga kommentarer ännu.", "More comments …" : "Fler kommentarer ...", "Save" : "Spara", - "Allowed characters {count} of {max}" : "Tillåtet antal tecken {count} av {max}", - "Error occurred while retrieving comment with id {id}" : "Fel inträffade vid hämtning av kommentar med id {id}", + "Allowed characters {count} of {max}" : "Antal tillåtna tecken, {count} av {max}", + "Error occurred while retrieving comment with id {id}" : "Fel inträffade vid inläsning av kommentar med id {id}", "Error occurred while updating comment with id {id}" : "Fel inträffade vid uppdatering av kommentar med id {id}", "Error occurred while posting comment" : "Fel inträffade vid publicering av kommentar", "_%n unread comment_::_%n unread comments_" : ["%n oläst kommentar","%n olästa kommentarer"], "Comment" : "Kommentar", "You commented" : "Du kommenterade", - "%1$s commented" : "%1$s har kommenterat", + "%1$s commented" : "%1$s kommenterade", "{author} commented" : "{author} kommenterade", "You commented on %1$s" : "Du kommenterade på %1$s", "You commented on {file}" : "Du kommenterade på {file}", @@ -30,7 +30,7 @@ OC.L10N.register( "A (now) deleted user mentioned you in a comment on “{file}”" : "En (nu) raderad användare nämnde dig i en kommentar på \"{file}\"", "%1$s mentioned you in a comment on “%2$s”" : "%1$s nämnde dig i en kommentar på “%2$s”", "{user} mentioned you in a comment on “{file}”" : "{user} nämnde dig i en kommentar på \"{file}\"", - "Type in a new comment..." : "Skriv en ny kommentar", + "Type in a new comment..." : "Skriv en ny kommentar...", "No other comments available" : "Inga andra kommentarer tillgängliga", "More comments..." : "Fler kommentarer...", "{count} unread comments" : "{count} olästa kommentarer", diff --git a/apps/comments/l10n/sv.json b/apps/comments/l10n/sv.json index 1e6dd663ff7..696e8f3a078 100644 --- a/apps/comments/l10n/sv.json +++ b/apps/comments/l10n/sv.json @@ -7,17 +7,17 @@ "Cancel" : "Avbryt", "Edit comment" : "Redigera kommentar", "[Deleted user]" : "[Raderad användare]", - "No comments yet, start the conversation!" : "Inga kommentarer än, börja konversationen!", + "No comments yet, start the conversation!" : "Inga kommentarer ännu.", "More comments …" : "Fler kommentarer ...", "Save" : "Spara", - "Allowed characters {count} of {max}" : "Tillåtet antal tecken {count} av {max}", - "Error occurred while retrieving comment with id {id}" : "Fel inträffade vid hämtning av kommentar med id {id}", + "Allowed characters {count} of {max}" : "Antal tillåtna tecken, {count} av {max}", + "Error occurred while retrieving comment with id {id}" : "Fel inträffade vid inläsning av kommentar med id {id}", "Error occurred while updating comment with id {id}" : "Fel inträffade vid uppdatering av kommentar med id {id}", "Error occurred while posting comment" : "Fel inträffade vid publicering av kommentar", "_%n unread comment_::_%n unread comments_" : ["%n oläst kommentar","%n olästa kommentarer"], "Comment" : "Kommentar", "You commented" : "Du kommenterade", - "%1$s commented" : "%1$s har kommenterat", + "%1$s commented" : "%1$s kommenterade", "{author} commented" : "{author} kommenterade", "You commented on %1$s" : "Du kommenterade på %1$s", "You commented on {file}" : "Du kommenterade på {file}", @@ -28,7 +28,7 @@ "A (now) deleted user mentioned you in a comment on “{file}”" : "En (nu) raderad användare nämnde dig i en kommentar på \"{file}\"", "%1$s mentioned you in a comment on “%2$s”" : "%1$s nämnde dig i en kommentar på “%2$s”", "{user} mentioned you in a comment on “{file}”" : "{user} nämnde dig i en kommentar på \"{file}\"", - "Type in a new comment..." : "Skriv en ny kommentar", + "Type in a new comment..." : "Skriv en ny kommentar...", "No other comments available" : "Inga andra kommentarer tillgängliga", "More comments..." : "Fler kommentarer...", "{count} unread comments" : "{count} olästa kommentarer", diff --git a/apps/comments/l10n/zh_CN.js b/apps/comments/l10n/zh_CN.js index 326fa6407a4..a2eb29f322f 100644 --- a/apps/comments/l10n/zh_CN.js +++ b/apps/comments/l10n/zh_CN.js @@ -1,23 +1,39 @@ OC.L10N.register( "comments", { - "Type in a new comment..." : "添加新评论...", + "Comments" : "评论", + "Unknown user" : "未知用户", + "New comment …" : "新评论 ...", "Delete comment" : "删除评论", "Post" : "发布", "Cancel" : "取消", "Edit comment" : "编辑评论", "[Deleted user]" : "[Deleted user]", - "Comments" : "评论", - "No other comments available" : "没有其他评论", - "More comments..." : "更多评论...", + "No comments yet, start the conversation!" : "还没有评论,开始对话吧!", + "More comments …" : "更多评论 ...", "Save" : "保存", "Allowed characters {count} of {max}" : "当前字数: {count},最大允许:{max}", - "{count} unread comments" : "{count} 条未读评论", + "Error occurred while retrieving comment with id {id}" : "检索 id 为 {id} 的评论出错", + "Error occurred while updating comment with id {id}" : "更新 id 为 {id} 的评论出错", + "Error occurred while posting comment" : "发布评论出错", + "_%n unread comment_::_%n unread comments_" : ["%n 未读评论"], "Comment" : "评论", - "<strong>Comments</strong> for files <em>(always listed in stream)</em>" : "文件的<strong>评论</strong><em>(始终在数据流中列出)</em>", "You commented" : "您的评论", "%1$s commented" : "%1$s 已评论", - "You commented on %2$s" : "你评论了 %2$s", - "%1$s commented on %2$s" : "%1$s 评论了 %2$s" + "{author} commented" : "{author} 评论了", + "You commented on %1$s" : "您在 %1$s 的评论", + "You commented on {file}" : "您对 {file} 的注释", + "%1$s commented on %2$s" : "%1$s 评论了 %2$s", + "{author} commented on {file}" : "{author} 对 {file} 的注释", + "<strong>Comments</strong> for files" : "显示文件的<strong>注释</strong>", + "A (now) deleted user mentioned you in a comment on “%s”" : "一个(正)被删除的用户提醒您注释 “%s”", + "A (now) deleted user mentioned you in a comment on “{file}”" : "一个(正)被删除的用户提醒您注释 “{file}”", + "%1$s mentioned you in a comment on “%2$s”" : "%1$s 提醒您注释 “%2$s”", + "{user} mentioned you in a comment on “{file}”" : "{user} 提醒您注释 “{file}”", + "Type in a new comment..." : "添加新评论...", + "No other comments available" : "没有其他评论", + "More comments..." : "更多评论...", + "{count} unread comments" : "{count} 条未读评论", + "You commented on %2$s" : "你评论了 %2$s" }, "nplurals=1; plural=0;"); diff --git a/apps/comments/l10n/zh_CN.json b/apps/comments/l10n/zh_CN.json index 98aa243e1c2..cbc878d898c 100644 --- a/apps/comments/l10n/zh_CN.json +++ b/apps/comments/l10n/zh_CN.json @@ -1,21 +1,37 @@ { "translations": { - "Type in a new comment..." : "添加新评论...", + "Comments" : "评论", + "Unknown user" : "未知用户", + "New comment …" : "新评论 ...", "Delete comment" : "删除评论", "Post" : "发布", "Cancel" : "取消", "Edit comment" : "编辑评论", "[Deleted user]" : "[Deleted user]", - "Comments" : "评论", - "No other comments available" : "没有其他评论", - "More comments..." : "更多评论...", + "No comments yet, start the conversation!" : "还没有评论,开始对话吧!", + "More comments …" : "更多评论 ...", "Save" : "保存", "Allowed characters {count} of {max}" : "当前字数: {count},最大允许:{max}", - "{count} unread comments" : "{count} 条未读评论", + "Error occurred while retrieving comment with id {id}" : "检索 id 为 {id} 的评论出错", + "Error occurred while updating comment with id {id}" : "更新 id 为 {id} 的评论出错", + "Error occurred while posting comment" : "发布评论出错", + "_%n unread comment_::_%n unread comments_" : ["%n 未读评论"], "Comment" : "评论", - "<strong>Comments</strong> for files <em>(always listed in stream)</em>" : "文件的<strong>评论</strong><em>(始终在数据流中列出)</em>", "You commented" : "您的评论", "%1$s commented" : "%1$s 已评论", - "You commented on %2$s" : "你评论了 %2$s", - "%1$s commented on %2$s" : "%1$s 评论了 %2$s" + "{author} commented" : "{author} 评论了", + "You commented on %1$s" : "您在 %1$s 的评论", + "You commented on {file}" : "您对 {file} 的注释", + "%1$s commented on %2$s" : "%1$s 评论了 %2$s", + "{author} commented on {file}" : "{author} 对 {file} 的注释", + "<strong>Comments</strong> for files" : "显示文件的<strong>注释</strong>", + "A (now) deleted user mentioned you in a comment on “%s”" : "一个(正)被删除的用户提醒您注释 “%s”", + "A (now) deleted user mentioned you in a comment on “{file}”" : "一个(正)被删除的用户提醒您注释 “{file}”", + "%1$s mentioned you in a comment on “%2$s”" : "%1$s 提醒您注释 “%2$s”", + "{user} mentioned you in a comment on “{file}”" : "{user} 提醒您注释 “{file}”", + "Type in a new comment..." : "添加新评论...", + "No other comments available" : "没有其他评论", + "More comments..." : "更多评论...", + "{count} unread comments" : "{count} 条未读评论", + "You commented on %2$s" : "你评论了 %2$s" },"pluralForm" :"nplurals=1; plural=0;" }
\ No newline at end of file diff --git a/apps/comments/lib/Notification/Listener.php b/apps/comments/lib/Notification/Listener.php index d30c59c93d5..365f93ce8dd 100644 --- a/apps/comments/lib/Notification/Listener.php +++ b/apps/comments/lib/Notification/Listener.php @@ -23,7 +23,6 @@ namespace OCA\Comments\Notification; use OCP\Comments\CommentsEvent; use OCP\Comments\IComment; -use OCP\IURLGenerator; use OCP\IUserManager; use OCP\Notification\IManager; @@ -34,25 +33,19 @@ class Listener { /** @var IUserManager */ protected $userManager; - /** @var IURLGenerator */ - protected $urlGenerator; - /** * Listener constructor. * * @param IManager $notificationManager * @param IUserManager $userManager - * @param IURLGenerator $urlGenerator */ public function __construct( IManager $notificationManager, - IUserManager $userManager, - IURLGenerator $urlGenerator + IUserManager $userManager ) { $this->notificationManager = $notificationManager; $this->userManager = $userManager; - $this->urlGenerator = $urlGenerator; } /** @@ -100,11 +93,7 @@ class Listener { ->setApp('comments') ->setObject('comment', $comment->getId()) ->setSubject('mention', [ $comment->getObjectType(), $comment->getObjectId() ]) - ->setDateTime($comment->getCreationDateTime()) - ->setLink($this->urlGenerator->linkToRouteAbsolute( - 'comments.Notifications.view', - ['id' => $comment->getId()]) - ); + ->setDateTime($comment->getCreationDateTime()); return $notification; } diff --git a/apps/comments/lib/Notification/Notifier.php b/apps/comments/lib/Notification/Notifier.php index 170538512d8..a9daef3031f 100644 --- a/apps/comments/lib/Notification/Notifier.php +++ b/apps/comments/lib/Notification/Notifier.php @@ -139,7 +139,11 @@ class Notifier implements INotifier { ] ); } - $notification->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/comment.svg'))); + $notification->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/comment.svg'))) + ->setLink($this->url->linkToRouteAbsolute( + 'comments.Notifications.view', + ['id' => $comment->getId()]) + ); return $notification; break; diff --git a/apps/comments/tests/Unit/Notification/ListenerTest.php b/apps/comments/tests/Unit/Notification/ListenerTest.php index 3007b78cb3d..ef84d1c60de 100644 --- a/apps/comments/tests/Unit/Notification/ListenerTest.php +++ b/apps/comments/tests/Unit/Notification/ListenerTest.php @@ -46,14 +46,12 @@ class ListenerTest extends TestCase { protected function setUp() { parent::setUp(); - $this->notificationManager = $this->getMockBuilder('\OCP\Notification\IManager')->getMock(); - $this->userManager = $this->getMockBuilder('\OCP\IUserManager')->getMock(); - $this->urlGenerator = $this->getMockBuilder('OCP\IURLGenerator')->getMock(); + $this->notificationManager = $this->createMock(\OCP\Notification\IManager::class); + $this->userManager = $this->createMock(\OCP\IUserManager::class); $this->listener = new Listener( $this->notificationManager, - $this->userManager, - $this->urlGenerator + $this->userManager ); } diff --git a/apps/dav/l10n/de_DE.js b/apps/dav/l10n/de_DE.js index 03da3d51f7c..07758a7b122 100644 --- a/apps/dav/l10n/de_DE.js +++ b/apps/dav/l10n/de_DE.js @@ -26,19 +26,19 @@ OC.L10N.register( "You deleted event {event} from calendar {calendar}" : "Sie haben das Ereignis {event} im Kalender {calendar} gelöscht", "{actor} updated event {event} in calendar {calendar}" : "{actor} hat das Ereignis {event} im Kalender {calendar} aktualisiert", "You updated event {event} in calendar {calendar}" : "Sie haben das Ereignis {event} im Kalender {calendar} aktualisiert", - "{actor} created todo {todo} in list {calendar}" : "{actor} hat das ToDo {todo} in der Liste {calendar} erstellt", - "You created todo {todo} in list {calendar}" : "Sie haben das ToDo {todo} in der Liste {calendar} erstellt", - "{actor} deleted todo {todo} from list {calendar}" : "{actor} hat das ToDo {todo} in der Liste {calendar} gelöscht", - "You deleted todo {todo} from list {calendar}" : "Sie haben das ToDo {todo} in der Liste {calendar} gelöscht", - "{actor} updated todo {todo} in list {calendar}" : "{actor} hat das ToDo {todo} in der Liste {calendar} aktualisiert", - "You updated todo {todo} in list {calendar}" : "Sie haben das ToDo {todo} in der Liste {calendar} aktualisiert", - "{actor} solved todo {todo} in list {calendar}" : "{actor} hat das ToDo {todo} in der Liste {calendar} erledigt", - "You solved todo {todo} in list {calendar}" : "Sie haben das ToDo {todo} in der Liste {calendar} erledigt", - "{actor} reopened todo {todo} in list {calendar}" : "{actor} hat das ToDo {todo} in der Liste {calendar} wieder geöffnet", - "You reopened todo {todo} in list {calendar}" : "Sie haben das ToDo {todo} in der Liste {calendar} wieder geöffnet", + "{actor} created todo {todo} in list {calendar}" : "{actor} hat die Aufgabe {todo} in der Liste {calendar} erstellt", + "You created todo {todo} in list {calendar}" : "Sie haben die Aufgabe {todo} in der Liste {calendar} erstellt", + "{actor} deleted todo {todo} from list {calendar}" : "{actor} hat die Aufgabe {todo} in der Liste {calendar} gelöscht", + "You deleted todo {todo} from list {calendar}" : "Sie haben die Aufgabe {todo} in der Liste {calendar} gelöscht", + "{actor} updated todo {todo} in list {calendar}" : "{actor} hat die Aufgabe {todo} in der Liste {calendar} aktualisiert", + "You updated todo {todo} in list {calendar}" : "Sie haben die Aufgabe {todo} in der Liste {calendar} aktualisiert", + "{actor} solved todo {todo} in list {calendar}" : "{actor} hat die Aufgabe {todo} in der Liste {calendar} erledigt", + "You solved todo {todo} in list {calendar}" : "Sie haben die Aufgabe {todo} in der Liste {calendar} erledigt", + "{actor} reopened todo {todo} in list {calendar}" : "{actor} hat die Aufgabe {todo} in der Liste {calendar} wiedereröffnet", + "You reopened todo {todo} in list {calendar}" : "Sie haben die Aufgabe {todo} in der Liste {calendar} wiedereröffnet", "A <strong>calendar</strong> was modified" : "Ein <strong>Kalender</strong> wurde bearbeitet", - "A calendar <strong>event</strong> was modified" : "Ein <strong>Ereignis</strong> wurde bearbeitet", - "A calendar <strong>todo</strong> was modified" : "Eine <strong>Aufgabe</strong> wurde bearbeitet", + "A calendar <strong>event</strong> was modified" : "Ein Kalender-<strong>Ereignis</strong> wurde bearbeitet", + "A calendar <strong>todo</strong> was modified" : "Eine Kalender-<strong>Aufgabe</strong> wurde bearbeitet", "Contact birthdays" : "Geburtstage von Kontakten", "Personal" : "Persönlich", "Contacts" : "Kontakte", diff --git a/apps/dav/l10n/de_DE.json b/apps/dav/l10n/de_DE.json index b7751ee32a4..8440fb0ca50 100644 --- a/apps/dav/l10n/de_DE.json +++ b/apps/dav/l10n/de_DE.json @@ -24,19 +24,19 @@ "You deleted event {event} from calendar {calendar}" : "Sie haben das Ereignis {event} im Kalender {calendar} gelöscht", "{actor} updated event {event} in calendar {calendar}" : "{actor} hat das Ereignis {event} im Kalender {calendar} aktualisiert", "You updated event {event} in calendar {calendar}" : "Sie haben das Ereignis {event} im Kalender {calendar} aktualisiert", - "{actor} created todo {todo} in list {calendar}" : "{actor} hat das ToDo {todo} in der Liste {calendar} erstellt", - "You created todo {todo} in list {calendar}" : "Sie haben das ToDo {todo} in der Liste {calendar} erstellt", - "{actor} deleted todo {todo} from list {calendar}" : "{actor} hat das ToDo {todo} in der Liste {calendar} gelöscht", - "You deleted todo {todo} from list {calendar}" : "Sie haben das ToDo {todo} in der Liste {calendar} gelöscht", - "{actor} updated todo {todo} in list {calendar}" : "{actor} hat das ToDo {todo} in der Liste {calendar} aktualisiert", - "You updated todo {todo} in list {calendar}" : "Sie haben das ToDo {todo} in der Liste {calendar} aktualisiert", - "{actor} solved todo {todo} in list {calendar}" : "{actor} hat das ToDo {todo} in der Liste {calendar} erledigt", - "You solved todo {todo} in list {calendar}" : "Sie haben das ToDo {todo} in der Liste {calendar} erledigt", - "{actor} reopened todo {todo} in list {calendar}" : "{actor} hat das ToDo {todo} in der Liste {calendar} wieder geöffnet", - "You reopened todo {todo} in list {calendar}" : "Sie haben das ToDo {todo} in der Liste {calendar} wieder geöffnet", + "{actor} created todo {todo} in list {calendar}" : "{actor} hat die Aufgabe {todo} in der Liste {calendar} erstellt", + "You created todo {todo} in list {calendar}" : "Sie haben die Aufgabe {todo} in der Liste {calendar} erstellt", + "{actor} deleted todo {todo} from list {calendar}" : "{actor} hat die Aufgabe {todo} in der Liste {calendar} gelöscht", + "You deleted todo {todo} from list {calendar}" : "Sie haben die Aufgabe {todo} in der Liste {calendar} gelöscht", + "{actor} updated todo {todo} in list {calendar}" : "{actor} hat die Aufgabe {todo} in der Liste {calendar} aktualisiert", + "You updated todo {todo} in list {calendar}" : "Sie haben die Aufgabe {todo} in der Liste {calendar} aktualisiert", + "{actor} solved todo {todo} in list {calendar}" : "{actor} hat die Aufgabe {todo} in der Liste {calendar} erledigt", + "You solved todo {todo} in list {calendar}" : "Sie haben die Aufgabe {todo} in der Liste {calendar} erledigt", + "{actor} reopened todo {todo} in list {calendar}" : "{actor} hat die Aufgabe {todo} in der Liste {calendar} wiedereröffnet", + "You reopened todo {todo} in list {calendar}" : "Sie haben die Aufgabe {todo} in der Liste {calendar} wiedereröffnet", "A <strong>calendar</strong> was modified" : "Ein <strong>Kalender</strong> wurde bearbeitet", - "A calendar <strong>event</strong> was modified" : "Ein <strong>Ereignis</strong> wurde bearbeitet", - "A calendar <strong>todo</strong> was modified" : "Eine <strong>Aufgabe</strong> wurde bearbeitet", + "A calendar <strong>event</strong> was modified" : "Ein Kalender-<strong>Ereignis</strong> wurde bearbeitet", + "A calendar <strong>todo</strong> was modified" : "Eine Kalender-<strong>Aufgabe</strong> wurde bearbeitet", "Contact birthdays" : "Geburtstage von Kontakten", "Personal" : "Persönlich", "Contacts" : "Kontakte", diff --git a/apps/dav/l10n/sv.js b/apps/dav/l10n/sv.js index fc67e02ebfb..44ec2399e2b 100644 --- a/apps/dav/l10n/sv.js +++ b/apps/dav/l10n/sv.js @@ -40,10 +40,10 @@ OC.L10N.register( "A calendar <strong>event</strong> was modified" : "En kalender <strong>händelse</strong> modifierades", "A calendar <strong>todo</strong> was modified" : "En kalender <strong>att-göra</strong> modifierades", "Contact birthdays" : "Kontaktfödelsedagar", - "Personal" : "Personligt", + "Personal" : "Privat", "Contacts" : "Kontakter", "Technical details" : "Tekniska detaljer", - "Remote Address: %s" : "Fjärradress: %s", + "Remote Address: %s" : "Extern adress: %s", "Request ID: %s" : "Begär ID: %s" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/dav/l10n/sv.json b/apps/dav/l10n/sv.json index bc7a87fe986..6f77c0a7692 100644 --- a/apps/dav/l10n/sv.json +++ b/apps/dav/l10n/sv.json @@ -38,10 +38,10 @@ "A calendar <strong>event</strong> was modified" : "En kalender <strong>händelse</strong> modifierades", "A calendar <strong>todo</strong> was modified" : "En kalender <strong>att-göra</strong> modifierades", "Contact birthdays" : "Kontaktfödelsedagar", - "Personal" : "Personligt", + "Personal" : "Privat", "Contacts" : "Kontakter", "Technical details" : "Tekniska detaljer", - "Remote Address: %s" : "Fjärradress: %s", + "Remote Address: %s" : "Extern adress: %s", "Request ID: %s" : "Begär ID: %s" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/dav/lib/CalDAV/Activity/Filter/Todo.php b/apps/dav/lib/CalDAV/Activity/Filter/Todo.php index 312bb6c472d..42ee0740851 100644 --- a/apps/dav/lib/CalDAV/Activity/Filter/Todo.php +++ b/apps/dav/lib/CalDAV/Activity/Filter/Todo.php @@ -79,7 +79,7 @@ class Todo implements IFilter { * @since 11.0.0 */ public function filterTypes(array $types) { - return array_intersect(['calendar_todos'], $types); + return array_intersect(['calendar_todo'], $types); } /** diff --git a/apps/dav/lib/CalDAV/Activity/Setting/Todo.php b/apps/dav/lib/CalDAV/Activity/Setting/Todo.php index 0c8215f4843..234774bfea4 100644 --- a/apps/dav/lib/CalDAV/Activity/Setting/Todo.php +++ b/apps/dav/lib/CalDAV/Activity/Setting/Todo.php @@ -42,7 +42,7 @@ class Todo implements ISetting { * @since 11.0.0 */ public function getIdentifier() { - return 'calendar_todos'; + return 'calendar_todo'; } /** diff --git a/apps/dav/lib/CalDAV/BirthdayService.php b/apps/dav/lib/CalDAV/BirthdayService.php index 104eec6b496..702b74bf1b3 100644 --- a/apps/dav/lib/CalDAV/BirthdayService.php +++ b/apps/dav/lib/CalDAV/BirthdayService.php @@ -30,6 +30,11 @@ use Exception; use OCA\DAV\CardDAV\CardDavBackend; use OCA\DAV\DAV\GroupPrincipalBackend; use Sabre\VObject\Component\VCalendar; +use Sabre\VObject\Component\VCard; +use Sabre\VObject\DateTimeParser; +use Sabre\VObject\Document; +use Sabre\VObject\InvalidDataException; +use Sabre\VObject\Property\VCard\DateAndOrTime; use Sabre\VObject\Reader; class BirthdayService { @@ -129,6 +134,12 @@ class BirthdayService { } try { $doc = Reader::read($cardData); + // We're always converting to vCard 4.0 so we can rely on the + // VCardConverter handling the X-APPLE-OMIT-YEAR property for us. + if (!$doc instanceof VCard) { + return null; + } + $doc = $doc->convert(Document::VCARD40); } catch (Exception $e) { return null; } @@ -136,21 +147,43 @@ class BirthdayService { if (!isset($doc->{$dateField})) { return null; } + if (!isset($doc->FN)) { + return null; + } $birthday = $doc->{$dateField}; if (!(string)$birthday) { return null; } - $title = str_replace('{name}', - strtr((string)$doc->FN, array('\,' => ',', '\;' => ';')), - '{name}' - ); + // Skip if the BDAY property is not of the right type. + if (!$birthday instanceof DateAndOrTime) { + return null; + } + + // Skip if we can't parse the BDAY value. + try { + $dateParts = DateTimeParser::parseVCardDateTime($birthday->getValue()); + } catch (InvalidDataException $e) { + return null; + } + + $unknownYear = false; + if (!$dateParts['year']) { + $birthday = '1900-' . $dateParts['month'] . '-' . $dateParts['date']; + + $unknownYear = true; + } + try { $date = new \DateTime($birthday); } catch (Exception $e) { return null; } - - $summary = $title . ' (' . $summarySymbol . $date->format('Y') . ')'; + if ($unknownYear) { + $summary = $doc->FN->getValue() . ' ' . $summarySymbol; + } else { + $year = (int)$date->format('Y'); + $summary = $doc->FN->getValue() . " ($summarySymbol$year)"; + } $vCal = new VCalendar(); $vCal->VERSION = '2.0'; $vEvent = $vCal->createComponent('VEVENT'); diff --git a/apps/dav/lib/SystemTag/SystemTagNode.php b/apps/dav/lib/SystemTag/SystemTagNode.php index 36fddcd8240..bd21082f783 100644 --- a/apps/dav/lib/SystemTag/SystemTagNode.php +++ b/apps/dav/lib/SystemTag/SystemTagNode.php @@ -157,12 +157,13 @@ class SystemTagNode implements \Sabre\DAV\INode { public function delete() { try { + if (!$this->isAdmin) { + throw new Forbidden('No permission to delete tag ' . $this->tag->getId()); + } + if (!$this->tagManager->canUserSeeTag($this->tag, $this->user)) { throw new NotFound('Tag with id ' . $this->tag->getId() . ' not found'); } - if (!$this->tagManager->canUserAssignTag($this->tag, $this->user)) { - throw new Forbidden('No permission to delete tag ' . $this->tag->getId()); - } $this->tagManager->deleteTags($this->tag->getId()); } catch (TagNotFoundException $e) { diff --git a/apps/dav/tests/unit/CalDAV/Activity/Filter/TodoTest.php b/apps/dav/tests/unit/CalDAV/Activity/Filter/TodoTest.php index 3c6ac2a5c55..54a5a6f5f9d 100644 --- a/apps/dav/tests/unit/CalDAV/Activity/Filter/TodoTest.php +++ b/apps/dav/tests/unit/CalDAV/Activity/Filter/TodoTest.php @@ -67,9 +67,9 @@ class TodoTest extends TestCase { public function dataFilterTypes() { return [ [[], []], - [['calendar_todos'], ['calendar_todos']], - [['calendar', 'calendar_event', 'calendar_todos'], ['calendar_todos']], - [['calendar', 'calendar_todos', 'files'], ['calendar_todos']], + [['calendar_todo'], ['calendar_todo']], + [['calendar', 'calendar_event', 'calendar_todo'], ['calendar_todo']], + [['calendar', 'calendar_todo', 'files'], ['calendar_todo']], ]; } diff --git a/apps/dav/tests/unit/CardDAV/BirthdayServiceTest.php b/apps/dav/tests/unit/CardDAV/BirthdayServiceTest.php index 5eeb6772a60..cecf07ef1d8 100644 --- a/apps/dav/tests/unit/CardDAV/BirthdayServiceTest.php +++ b/apps/dav/tests/unit/CardDAV/BirthdayServiceTest.php @@ -55,18 +55,18 @@ class BirthdayServiceTest extends TestCase { /** * @dataProvider providesVCards - * @param boolean $nullExpected + * @param boolean $expectedSummary * @param string | null $data */ - public function testBuildBirthdayFromContact($nullExpected, $data) { + public function testBuildBirthdayFromContact($expectedSummary, $data) { $cal = $this->service->buildDateFromContact($data, 'BDAY', '*'); - if ($nullExpected) { + if ($expectedSummary === null) { $this->assertNull($cal); } else { $this->assertInstanceOf('Sabre\VObject\Component\VCalendar', $cal); $this->assertTrue(isset($cal->VEVENT)); $this->assertEquals('FREQ=YEARLY', $cal->VEVENT->RRULE->getValue()); - $this->assertEquals('12345 (*1900)', $cal->VEVENT->SUMMARY->getValue()); + $this->assertEquals($expectedSummary, $cal->VEVENT->SUMMARY->getValue()); $this->assertEquals('TRANSPARENT', $cal->VEVENT->TRANSP->getValue()); } } @@ -233,14 +233,19 @@ class BirthdayServiceTest extends TestCase { public function providesVCards() { return [ - [true, null], - [true, ''], - [true, 'yasfewf'], - [true, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nEND:VCARD\r\n", "Dr. Foo Bar"], - [true, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:\r\nEND:VCARD\r\n", "Dr. Foo Bar"], - [true, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:someday\r\nEND:VCARD\r\n", "Dr. Foo Bar"], - [false, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:1900-01-01\r\nEND:VCARD\r\n", "Dr. Foo Bar"], - [false, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:1900-12-31\r\nEND:VCARD\r\n", "Dr. Foo Bar"], + [null, null], + [null, ''], + [null, 'yasfewf'], + [null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nEND:VCARD\r\n", "Dr. Foo Bar"], + [null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:\r\nEND:VCARD\r\n", "Dr. Foo Bar"], + [null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:someday\r\nEND:VCARD\r\n", "Dr. Foo Bar"], + ['12345 (*1900)', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19000101\r\nEND:VCARD\r\n", "Dr. Foo Bar"], + ['12345 (*1900)', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", "Dr. Foo Bar"], + ['12345 *', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:--1231\r\nEND:VCARD\r\n", "Dr. Foo Bar"], + ['12345 *', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY;X-APPLE-OMIT-YEAR=1604:16041231\r\nEND:VCARD\r\n", "Dr. Foo Bar"], + [null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:;VALUE=text:circa 1800\r\nEND:VCARD\r\n", "Dr. Foo Bar"], + [null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nN:12345;;;;\r\nBDAY:20031231\r\nEND:VCARD\r\n", "Dr. Foo Bar"], + ['12345 (*900)', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:09001231\r\nEND:VCARD\r\n", "Dr. Foo Bar"], ]; } } diff --git a/apps/dav/tests/unit/SystemTag/SystemTagNodeTest.php b/apps/dav/tests/unit/SystemTag/SystemTagNodeTest.php index 43674f4b795..3722bd9d25a 100644 --- a/apps/dav/tests/unit/SystemTag/SystemTagNodeTest.php +++ b/apps/dav/tests/unit/SystemTag/SystemTagNodeTest.php @@ -24,19 +24,17 @@ namespace OCA\DAV\Tests\unit\SystemTag; -use Sabre\DAV\Exception\NotFound; -use Sabre\DAV\Exception\MethodNotAllowed; -use Sabre\DAV\Exception\Conflict; use OC\SystemTag\SystemTag; use OCP\SystemTag\TagNotFoundException; use OCP\SystemTag\TagAlreadyExistsException; use OCP\SystemTag\ISystemTag; +use Sabre\DAV\Exception\Forbidden; class SystemTagNodeTest extends \Test\TestCase { /** - * @var \OCP\SystemTag\ISystemTagManager + * @var \OCP\SystemTag\ISystemTagManager|\PHPUnit_Framework_MockObject_MockObject */ private $tagManager; @@ -113,7 +111,7 @@ class SystemTagNodeTest extends \Test\TestCase { /** * @dataProvider tagNodeProvider */ - public function testUpdateTag($isAdmin, $originalTag, $changedArgs) { + public function testUpdateTag($isAdmin, ISystemTag $originalTag, $changedArgs) { $this->tagManager->expects($this->once()) ->method('canUserSeeTag') ->with($originalTag) @@ -173,7 +171,7 @@ class SystemTagNodeTest extends \Test\TestCase { /** * @dataProvider tagNodeProviderPermissionException */ - public function testUpdateTagPermissionException($originalTag, $changedArgs, $expectedException = null) { + public function testUpdateTagPermissionException(ISystemTag $originalTag, $changedArgs, $expectedException = null) { $this->tagManager->expects($this->any()) ->method('canUserSeeTag') ->with($originalTag) @@ -242,17 +240,16 @@ class SystemTagNodeTest extends \Test\TestCase { */ public function testDeleteTag($isAdmin) { $tag = new SystemTag(1, 'tag1', true, true); - $this->tagManager->expects($this->once()) + $this->tagManager->expects($isAdmin ? $this->once() : $this->never()) ->method('canUserSeeTag') ->with($tag) ->will($this->returnValue(true)); - $this->tagManager->expects($this->once()) - ->method('canUserAssignTag') - ->with($tag) - ->will($this->returnValue(true)); - $this->tagManager->expects($this->once()) + $this->tagManager->expects($isAdmin ? $this->once() : $this->never()) ->method('deleteTags') ->with('1'); + if (!$isAdmin) { + $this->setExpectedException(Forbidden::class); + } $this->getTagNode($isAdmin, $tag)->delete(); } @@ -261,7 +258,7 @@ class SystemTagNodeTest extends \Test\TestCase { [ // cannot delete invisible tag new SystemTag(1, 'Original', false, true), - 'Sabre\DAV\Exception\NotFound', + 'Sabre\DAV\Exception\Forbidden', ], [ // cannot delete non-assignable tag @@ -279,20 +276,11 @@ class SystemTagNodeTest extends \Test\TestCase { ->method('canUserSeeTag') ->with($tag) ->will($this->returnValue($tag->isUserVisible())); - $this->tagManager->expects($this->any()) - ->method('canUserAssignTag') - ->with($tag) - ->will($this->returnValue($tag->isUserAssignable())); $this->tagManager->expects($this->never()) ->method('deleteTags'); - try { - $this->getTagNode(false, $tag)->delete(); - } catch (\Exception $e) { - $thrown = $e; - } - - $this->assertInstanceOf($expectedException, $thrown); + $this->setExpectedException($expectedException); + $this->getTagNode(false, $tag)->delete(); } /** @@ -304,14 +292,10 @@ class SystemTagNodeTest extends \Test\TestCase { ->method('canUserSeeTag') ->with($tag) ->will($this->returnValue($tag->isUserVisible())); - $this->tagManager->expects($this->any()) - ->method('canUserAssignTag') - ->with($tag) - ->will($this->returnValue($tag->isUserAssignable())); $this->tagManager->expects($this->once()) ->method('deleteTags') ->with('1') ->will($this->throwException(new TagNotFoundException())); - $this->getTagNode(false, $tag)->delete(); + $this->getTagNode(true, $tag)->delete(); } } diff --git a/apps/encryption/l10n/sv.js b/apps/encryption/l10n/sv.js index fe409091d0f..0689661768d 100644 --- a/apps/encryption/l10n/sv.js +++ b/apps/encryption/l10n/sv.js @@ -22,28 +22,32 @@ OC.L10N.register( "The current log-in password was not correct, please try again." : "Det nuvarande inloggningslösenordet var inte korrekt. Vänligen försök igen.", "Private key password successfully updated." : "Den privata nyckelns lösenord uppdaterades.", "You need to migrate your encryption keys from the old encryption (ownCloud <= 8.0) to the new one. Please run 'occ encryption:migrate' or contact your administrator" : "Du behöver migrera dina krypteringsnycklar från den gamla krypteringen (ownCloud <= 8.0) till den nya. Kör 'occ encryption:migrate' eller kontakta din administratör", + "Invalid private key for encryption app. Please update your private key password in your personal settings to recover access to your encrypted files." : "Ogiltig privat nyckel för krypteringsappen. Uppdatera din privata nyckels lösenord i dina personliga inställningar för att återställa tillgång till dina krypterade filer.", + "Encryption app is enabled but your keys are not initialized, please log-out and log-in again" : "Krypteringsappen är aktiverad men dina krypteringsnycklar har inte aktiverats, logga ut och logga in igen.", "Encryption app is enabled and ready" : "Krypteringsfunktionen är aktiverad och redo", "Bad Signature" : "Dålig signatur", "Missing Signature" : "Saknar signatur", "one-time password for server-side-encryption" : "engångslösenord för kryptering på serversidan", "Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Kan ej dekryptera denna fil, förmodligen är det en delad fil. Be ägaren av filen att dela den med dig.", "Can not read this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Filen kan inte läsas, troligtvis är det en delad fil. Be ägaren av filen att dela den med dig igen.", + "Hey there,\n\nthe admin enabled server-side-encryption. Your files were encrypted using the password '%s'.\n\nPlease login to the web interface, go to the section 'basic encryption module' of your personal settings and update your encryption password by entering this password into the 'old log-in password' field and your current login-password.\n\n" : "Hallå där, \n\nAdministratören aktiverade serverkryptering. Alla dina filer har blivit krypterade med lösenordet: %s\n\nGå till i din profil för att ändra krypteringslösenordet till ditt egna lösenord. Ange lösenordet ovan som \"Gammalt lösenord\" och ange sedan ditt egna lösenord.\n\n", "The share will expire on %s." : "Utdelningen kommer att upphöra %s.", "Cheers!" : "Ha de fint!", + "Hey there,<br><br>the admin enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.<br><br>" : "Hallå där, <br> Administratören aktiverade serverkryptering. Alla dina filer har blivit krypterade med lösenordet: <strong>%s</ strong>. <br> Gå till i din profil för att ändra krypteringslösenordet till ditt egna lösenord. Ange lösenordet ovan som \"Gammalt lösenord\" och ange sedan ditt egna lösenord.<br>", "Default encryption module" : "Standard krypteringsfunktion", - "Encrypt the home storage" : "Kryptera hemmalagringen", + "Encrypt the home storage" : "Kryptera alla filer i molnet", "Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Aktivering av det här alternativet krypterar alla filer som är lagrade på huvudlagringsplatsen, annars kommer bara filer på extern lagringsplats att krypteras", "Enable recovery key" : "Aktivera återställningsnyckel", "Disable recovery key" : "Inaktivera återställningsnyckel", "The recovery key is an extra encryption key that is used to encrypt files. It allows recovery of a user's files if the user forgets his or her password." : "Återställningsnyckeln är en extra krypteringsnyckel som används för att kryptera filer. Den gör det möjligt att återställa en användares filer om användaren glömmer sitt lösenord.", - "Recovery key password" : "Lösenord för återställningsnyckel", - "Repeat recovery key password" : "Upprepa lösenord för återställningsnyckeln", - "Change recovery key password:" : "Ändra lösenord för återställningsnyckel:", - "Old recovery key password" : "Gammalt lösenord för återställningsnyckeln", - "New recovery key password" : "Nytt lösenord för återställningsnyckeln", - "Repeat new recovery key password" : "Upprepa nytt lösenord för återställningsnyckeln", + "Recovery key password" : "Ange lösenord", + "Repeat recovery key password" : "Repetera lösenord", + "Change recovery key password:" : "Ändra lösenord för återställningsnyckel", + "Old recovery key password" : "Gammalt lösenord", + "New recovery key password" : "Nytt lösenord", + "Repeat new recovery key password" : "Repetera lösenord", "Change Password" : "Byt lösenord", - "Basic encryption module" : "Vanlig krypteringsfunktion", + "Basic encryption module" : "Krypteringsmodul", "Encryption App is enabled but your keys are not initialized, please log-out and log-in again" : "Krypteringsprogrammet är aktiverat men dina nycklar är inte initierade. Vänligen logga ut och in igen", "Your private key password no longer matches your log-in password." : "Ditt lösenord för din privata nyckel matchar inte längre ditt inloggningslösenord.", "Set your old private key password to your current log-in password:" : "Sätt ditt gamla privatnyckellösenord till ditt aktuella inloggningslösenord:", diff --git a/apps/encryption/l10n/sv.json b/apps/encryption/l10n/sv.json index 7169143ea71..ab1134f64b7 100644 --- a/apps/encryption/l10n/sv.json +++ b/apps/encryption/l10n/sv.json @@ -20,28 +20,32 @@ "The current log-in password was not correct, please try again." : "Det nuvarande inloggningslösenordet var inte korrekt. Vänligen försök igen.", "Private key password successfully updated." : "Den privata nyckelns lösenord uppdaterades.", "You need to migrate your encryption keys from the old encryption (ownCloud <= 8.0) to the new one. Please run 'occ encryption:migrate' or contact your administrator" : "Du behöver migrera dina krypteringsnycklar från den gamla krypteringen (ownCloud <= 8.0) till den nya. Kör 'occ encryption:migrate' eller kontakta din administratör", + "Invalid private key for encryption app. Please update your private key password in your personal settings to recover access to your encrypted files." : "Ogiltig privat nyckel för krypteringsappen. Uppdatera din privata nyckels lösenord i dina personliga inställningar för att återställa tillgång till dina krypterade filer.", + "Encryption app is enabled but your keys are not initialized, please log-out and log-in again" : "Krypteringsappen är aktiverad men dina krypteringsnycklar har inte aktiverats, logga ut och logga in igen.", "Encryption app is enabled and ready" : "Krypteringsfunktionen är aktiverad och redo", "Bad Signature" : "Dålig signatur", "Missing Signature" : "Saknar signatur", "one-time password for server-side-encryption" : "engångslösenord för kryptering på serversidan", "Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Kan ej dekryptera denna fil, förmodligen är det en delad fil. Be ägaren av filen att dela den med dig.", "Can not read this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Filen kan inte läsas, troligtvis är det en delad fil. Be ägaren av filen att dela den med dig igen.", + "Hey there,\n\nthe admin enabled server-side-encryption. Your files were encrypted using the password '%s'.\n\nPlease login to the web interface, go to the section 'basic encryption module' of your personal settings and update your encryption password by entering this password into the 'old log-in password' field and your current login-password.\n\n" : "Hallå där, \n\nAdministratören aktiverade serverkryptering. Alla dina filer har blivit krypterade med lösenordet: %s\n\nGå till i din profil för att ändra krypteringslösenordet till ditt egna lösenord. Ange lösenordet ovan som \"Gammalt lösenord\" och ange sedan ditt egna lösenord.\n\n", "The share will expire on %s." : "Utdelningen kommer att upphöra %s.", "Cheers!" : "Ha de fint!", + "Hey there,<br><br>the admin enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.<br><br>" : "Hallå där, <br> Administratören aktiverade serverkryptering. Alla dina filer har blivit krypterade med lösenordet: <strong>%s</ strong>. <br> Gå till i din profil för att ändra krypteringslösenordet till ditt egna lösenord. Ange lösenordet ovan som \"Gammalt lösenord\" och ange sedan ditt egna lösenord.<br>", "Default encryption module" : "Standard krypteringsfunktion", - "Encrypt the home storage" : "Kryptera hemmalagringen", + "Encrypt the home storage" : "Kryptera alla filer i molnet", "Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Aktivering av det här alternativet krypterar alla filer som är lagrade på huvudlagringsplatsen, annars kommer bara filer på extern lagringsplats att krypteras", "Enable recovery key" : "Aktivera återställningsnyckel", "Disable recovery key" : "Inaktivera återställningsnyckel", "The recovery key is an extra encryption key that is used to encrypt files. It allows recovery of a user's files if the user forgets his or her password." : "Återställningsnyckeln är en extra krypteringsnyckel som används för att kryptera filer. Den gör det möjligt att återställa en användares filer om användaren glömmer sitt lösenord.", - "Recovery key password" : "Lösenord för återställningsnyckel", - "Repeat recovery key password" : "Upprepa lösenord för återställningsnyckeln", - "Change recovery key password:" : "Ändra lösenord för återställningsnyckel:", - "Old recovery key password" : "Gammalt lösenord för återställningsnyckeln", - "New recovery key password" : "Nytt lösenord för återställningsnyckeln", - "Repeat new recovery key password" : "Upprepa nytt lösenord för återställningsnyckeln", + "Recovery key password" : "Ange lösenord", + "Repeat recovery key password" : "Repetera lösenord", + "Change recovery key password:" : "Ändra lösenord för återställningsnyckel", + "Old recovery key password" : "Gammalt lösenord", + "New recovery key password" : "Nytt lösenord", + "Repeat new recovery key password" : "Repetera lösenord", "Change Password" : "Byt lösenord", - "Basic encryption module" : "Vanlig krypteringsfunktion", + "Basic encryption module" : "Krypteringsmodul", "Encryption App is enabled but your keys are not initialized, please log-out and log-in again" : "Krypteringsprogrammet är aktiverat men dina nycklar är inte initierade. Vänligen logga ut och in igen", "Your private key password no longer matches your log-in password." : "Ditt lösenord för din privata nyckel matchar inte längre ditt inloggningslösenord.", "Set your old private key password to your current log-in password:" : "Sätt ditt gamla privatnyckellösenord till ditt aktuella inloggningslösenord:", diff --git a/apps/federatedfilesharing/l10n/sv.js b/apps/federatedfilesharing/l10n/sv.js index 712ffb73e27..f205f0b5741 100644 --- a/apps/federatedfilesharing/l10n/sv.js +++ b/apps/federatedfilesharing/l10n/sv.js @@ -3,16 +3,16 @@ OC.L10N.register( { "Federated sharing" : "Federated Delning", "Do you want to add the remote share {name} from {owner}@{remote}?" : "Vill du lägga till fjärrdelning {name} från {owner}@{remote}?", - "Remote share" : "Fjärrdelning", - "Remote share password" : "Lösenord för fjärrdelning", + "Remote share" : "Extern delning", + "Remote share password" : "Lösenord för extern delning", "Cancel" : "Avbryt", - "Add remote share" : "Lägg till fjärrdelning", + "Add remote share" : "Lägg till extern delning", "Copy" : "Kopiera", "Copied!" : "Kopierad!", "Not supported!" : "Stöds ej!", "Press ⌘-C to copy." : "Tryck ⌘-C för att kopiera.", "Press Ctrl-C to copy." : "Tryck Ctrl-C för att kopiera.", - "Invalid Federated Cloud ID" : "Ogiltig Federerad Cloud-ID", + "Invalid Federated Cloud ID" : "Ogiltig Federerad Moln-ID", "Server to server sharing is not enabled on this server" : "Server-till-server-delning är inte aktiverat på denna server", "Couldn't establish a federated share." : "Kunde inte lägga till en fefererad utdelning", "Couldn't establish a federated share, maybe the password was wrong." : "Kunde inte lägga till en fefererad utdelning, lösenordet kanske var felaktigt.", @@ -20,36 +20,36 @@ OC.L10N.register( "The mountpoint name contains invalid characters." : "Monteringspunktens namn innehåller ogiltiga tecken.", "Not allowed to create a federated share with the owner." : "Ej tillåtet att skapa en federerad delning med ägaren", "Invalid or untrusted SSL certificate" : "Ogiltigt eller ej betrott SSL-certifikat", - "Could not authenticate to remote share, password might be wrong" : "Kunde ej autensiera fjärrdelningen, lösenordet kan vara fel", + "Could not authenticate to remote share, password might be wrong" : "Kunde inte autensiera mot externa servern, lösenordet kan vara fel", "Storage not valid" : "Lagring ogiltig", "Federated Share successfully added" : "Federerad delning lyckades", - "Couldn't add remote share" : "Kunde inte lägga till fjärrutdelning", + "Couldn't add remote share" : "Kunde inte lägga till extern delning", "Sharing %s failed, because this item is already shared with %s" : "Delning %s misslyckades därför att objektet redan är delat med %s", "Not allowed to create a federated share with the same user" : "Ej tillåtet att skapa en federerad delning med samma användare", "File is already shared with %s" : "Filen är redan delad med %s", "Sharing %s failed, could not find %s, maybe the server is currently unreachable or uses a self-signed certificate." : "Misslyckades att dela ut %s kunde inte hitta %s, kanske är servern inte tillgänglig för närvarande eller så används ett självsignerat certifikat", "Could not find share" : "Kunde inte hitta delning", - "You received \"%3$s\" as a remote share from %1$s (on behalf of %2$s)" : "Du tog emot \"%3$s\" som en fjärrutdelning från %1$s (på uppdrag av %2$s)", - "You received {share} as a remote share from {user} (on behalf of {behalf})" : "Du tog emot {share} som en fjärrutedlning {user} (på uppdrag av {behalf})", - "You received \"%3$s\" as a remote share from %1$s" : "Du tog emot \"%3$s\" som en fjärrutdelning från %1$s", - "You received {share} as a remote share from {user}" : "Du tog emot {share} som en fjärrutdelning från {user}", + "You received \"%3$s\" as a remote share from %1$s (on behalf of %2$s)" : "Du tog emot \"%3$s\" som en extern delning från %1$s (på uppdrag av %2$s)", + "You received {share} as a remote share from {user} (on behalf of {behalf})" : "Du tog emot {share} som en extern delning {user} (på uppdrag av {behalf})", + "You received \"%3$s\" as a remote share from %1$s" : "Du tog emot \"%3$s\" som en extern delning från %1$s", + "You received {share} as a remote share from {user}" : "Du tog emot {share} som en extern delning från {user}", "Accept" : "Acceptera", "Decline" : "Neka", - "Share with me through my #Nextcloud Federated Cloud ID, see %s" : "Dela med mig genom mitt #Nextcloud Federated Cloud ID, se %s", - "Share with me through my #Nextcloud Federated Cloud ID" : "Dela med mig genom mitt #Nextcloud Federated Cloud ID", - "Federated Cloud Sharing" : "Federerad Cloud-delning", + "Share with me through my #Nextcloud Federated Cloud ID, see %s" : "Dela med mig genom mitt #Nextcloud Federerade Moln-ID, se %s", + "Share with me through my #Nextcloud Federated Cloud ID" : "Dela med mig genom mitt #Nextcloud Federerade Moln-ID", + "Federated Cloud Sharing" : "Federerad Moln-delning", "Open documentation" : "Öppna dokumentation", "Allow users on this server to send shares to other servers" : "Tillåt användare på denna server att skicka utdelningar till andra servrar", "Allow users on this server to receive shares from other servers" : "Tillåt användare på denna servern att ta emot utdelningar från andra servrar", - "Search global and public address book for users" : "Sök global och publik adressbok för användare", - "Federated Cloud" : "Federerad Cloud", - "Your Federated Cloud ID:" : "Ditt Federerade Cloud-ID", + "Search global and public address book for users" : "Sök global och offentlig adressbok för användare", + "Federated Cloud" : "Federerat Moln", + "Your Federated Cloud ID:" : "Ditt Federerade Moln-ID", "Share it:" : "Dela detta:", "Add to your website" : "Lägg till på din hemsida", "Share with me via Nextcloud" : "Dela med mig via Nextcloud", "HTML Code:" : "HTML Kod:", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Misslyckades dela ut %s, kan inte hitta %s, kanske är servern inte åtkomlig för närvarande.", - "You received \"/%3$s\" as a remote share from %1$s (on behalf of %2$s)" : "Du tog emot \"/%3$s\" som en fjärrdelning från %1$s (via %2$s)", - "You received \"/%3$s\" as a remote share from %1$s" : "Du tog emot \"/%3$s\" som en fjärrdelning från %1$s" + "You received \"/%3$s\" as a remote share from %1$s (on behalf of %2$s)" : "Du tog emot \"/%3$s\" som en extern delning från %1$s (via %2$s)", + "You received \"/%3$s\" as a remote share from %1$s" : "Du tog emot \"/%3$s\" som en extern delning från %1$s" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/federatedfilesharing/l10n/sv.json b/apps/federatedfilesharing/l10n/sv.json index e5827ffcab2..47c8ebf23b7 100644 --- a/apps/federatedfilesharing/l10n/sv.json +++ b/apps/federatedfilesharing/l10n/sv.json @@ -1,16 +1,16 @@ { "translations": { "Federated sharing" : "Federated Delning", "Do you want to add the remote share {name} from {owner}@{remote}?" : "Vill du lägga till fjärrdelning {name} från {owner}@{remote}?", - "Remote share" : "Fjärrdelning", - "Remote share password" : "Lösenord för fjärrdelning", + "Remote share" : "Extern delning", + "Remote share password" : "Lösenord för extern delning", "Cancel" : "Avbryt", - "Add remote share" : "Lägg till fjärrdelning", + "Add remote share" : "Lägg till extern delning", "Copy" : "Kopiera", "Copied!" : "Kopierad!", "Not supported!" : "Stöds ej!", "Press ⌘-C to copy." : "Tryck ⌘-C för att kopiera.", "Press Ctrl-C to copy." : "Tryck Ctrl-C för att kopiera.", - "Invalid Federated Cloud ID" : "Ogiltig Federerad Cloud-ID", + "Invalid Federated Cloud ID" : "Ogiltig Federerad Moln-ID", "Server to server sharing is not enabled on this server" : "Server-till-server-delning är inte aktiverat på denna server", "Couldn't establish a federated share." : "Kunde inte lägga till en fefererad utdelning", "Couldn't establish a federated share, maybe the password was wrong." : "Kunde inte lägga till en fefererad utdelning, lösenordet kanske var felaktigt.", @@ -18,36 +18,36 @@ "The mountpoint name contains invalid characters." : "Monteringspunktens namn innehåller ogiltiga tecken.", "Not allowed to create a federated share with the owner." : "Ej tillåtet att skapa en federerad delning med ägaren", "Invalid or untrusted SSL certificate" : "Ogiltigt eller ej betrott SSL-certifikat", - "Could not authenticate to remote share, password might be wrong" : "Kunde ej autensiera fjärrdelningen, lösenordet kan vara fel", + "Could not authenticate to remote share, password might be wrong" : "Kunde inte autensiera mot externa servern, lösenordet kan vara fel", "Storage not valid" : "Lagring ogiltig", "Federated Share successfully added" : "Federerad delning lyckades", - "Couldn't add remote share" : "Kunde inte lägga till fjärrutdelning", + "Couldn't add remote share" : "Kunde inte lägga till extern delning", "Sharing %s failed, because this item is already shared with %s" : "Delning %s misslyckades därför att objektet redan är delat med %s", "Not allowed to create a federated share with the same user" : "Ej tillåtet att skapa en federerad delning med samma användare", "File is already shared with %s" : "Filen är redan delad med %s", "Sharing %s failed, could not find %s, maybe the server is currently unreachable or uses a self-signed certificate." : "Misslyckades att dela ut %s kunde inte hitta %s, kanske är servern inte tillgänglig för närvarande eller så används ett självsignerat certifikat", "Could not find share" : "Kunde inte hitta delning", - "You received \"%3$s\" as a remote share from %1$s (on behalf of %2$s)" : "Du tog emot \"%3$s\" som en fjärrutdelning från %1$s (på uppdrag av %2$s)", - "You received {share} as a remote share from {user} (on behalf of {behalf})" : "Du tog emot {share} som en fjärrutedlning {user} (på uppdrag av {behalf})", - "You received \"%3$s\" as a remote share from %1$s" : "Du tog emot \"%3$s\" som en fjärrutdelning från %1$s", - "You received {share} as a remote share from {user}" : "Du tog emot {share} som en fjärrutdelning från {user}", + "You received \"%3$s\" as a remote share from %1$s (on behalf of %2$s)" : "Du tog emot \"%3$s\" som en extern delning från %1$s (på uppdrag av %2$s)", + "You received {share} as a remote share from {user} (on behalf of {behalf})" : "Du tog emot {share} som en extern delning {user} (på uppdrag av {behalf})", + "You received \"%3$s\" as a remote share from %1$s" : "Du tog emot \"%3$s\" som en extern delning från %1$s", + "You received {share} as a remote share from {user}" : "Du tog emot {share} som en extern delning från {user}", "Accept" : "Acceptera", "Decline" : "Neka", - "Share with me through my #Nextcloud Federated Cloud ID, see %s" : "Dela med mig genom mitt #Nextcloud Federated Cloud ID, se %s", - "Share with me through my #Nextcloud Federated Cloud ID" : "Dela med mig genom mitt #Nextcloud Federated Cloud ID", - "Federated Cloud Sharing" : "Federerad Cloud-delning", + "Share with me through my #Nextcloud Federated Cloud ID, see %s" : "Dela med mig genom mitt #Nextcloud Federerade Moln-ID, se %s", + "Share with me through my #Nextcloud Federated Cloud ID" : "Dela med mig genom mitt #Nextcloud Federerade Moln-ID", + "Federated Cloud Sharing" : "Federerad Moln-delning", "Open documentation" : "Öppna dokumentation", "Allow users on this server to send shares to other servers" : "Tillåt användare på denna server att skicka utdelningar till andra servrar", "Allow users on this server to receive shares from other servers" : "Tillåt användare på denna servern att ta emot utdelningar från andra servrar", - "Search global and public address book for users" : "Sök global och publik adressbok för användare", - "Federated Cloud" : "Federerad Cloud", - "Your Federated Cloud ID:" : "Ditt Federerade Cloud-ID", + "Search global and public address book for users" : "Sök global och offentlig adressbok för användare", + "Federated Cloud" : "Federerat Moln", + "Your Federated Cloud ID:" : "Ditt Federerade Moln-ID", "Share it:" : "Dela detta:", "Add to your website" : "Lägg till på din hemsida", "Share with me via Nextcloud" : "Dela med mig via Nextcloud", "HTML Code:" : "HTML Kod:", "Sharing %s failed, could not find %s, maybe the server is currently unreachable." : "Misslyckades dela ut %s, kan inte hitta %s, kanske är servern inte åtkomlig för närvarande.", - "You received \"/%3$s\" as a remote share from %1$s (on behalf of %2$s)" : "Du tog emot \"/%3$s\" som en fjärrdelning från %1$s (via %2$s)", - "You received \"/%3$s\" as a remote share from %1$s" : "Du tog emot \"/%3$s\" som en fjärrdelning från %1$s" + "You received \"/%3$s\" as a remote share from %1$s (on behalf of %2$s)" : "Du tog emot \"/%3$s\" som en extern delning från %1$s (via %2$s)", + "You received \"/%3$s\" as a remote share from %1$s" : "Du tog emot \"/%3$s\" som en extern delning från %1$s" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/federation/l10n/sv.js b/apps/federation/l10n/sv.js index 93a47e49c19..1c6684daec6 100644 --- a/apps/federation/l10n/sv.js +++ b/apps/federation/l10n/sv.js @@ -3,7 +3,7 @@ OC.L10N.register( { "Added to the list of trusted servers" : "Tillagd i listan med betrodda servrar", "Server is already in the list of trusted servers." : "Servern finns redan i listan", - "No server to federate with found" : "Ingen server att federerad med hittad", + "No server to federate with found" : "Ingen server att federera med hittades", "Could not add server" : "Kunde inte lägga till server", "Federation" : "Federerad delning", "Federation allows you to connect with other trusted servers to exchange the user directory. For example this will be used to auto-complete external users for federated sharing." : "Federation låter dig ansluta till andra betrodda servrar för att utbyta användarinformation. Till exempel kommer detta användas för att auto-komplettera externa användare för federerad delning.", diff --git a/apps/federation/l10n/sv.json b/apps/federation/l10n/sv.json index cd4dd809283..68e4f055f3d 100644 --- a/apps/federation/l10n/sv.json +++ b/apps/federation/l10n/sv.json @@ -1,7 +1,7 @@ { "translations": { "Added to the list of trusted servers" : "Tillagd i listan med betrodda servrar", "Server is already in the list of trusted servers." : "Servern finns redan i listan", - "No server to federate with found" : "Ingen server att federerad med hittad", + "No server to federate with found" : "Ingen server att federera med hittades", "Could not add server" : "Kunde inte lägga till server", "Federation" : "Federerad delning", "Federation allows you to connect with other trusted servers to exchange the user directory. For example this will be used to auto-complete external users for federated sharing." : "Federation låter dig ansluta till andra betrodda servrar för att utbyta användarinformation. Till exempel kommer detta användas för att auto-komplettera externa användare för federerad delning.", diff --git a/apps/federation/l10n/zh_CN.js b/apps/federation/l10n/zh_CN.js index a84f6350671..893dab97b1d 100644 --- a/apps/federation/l10n/zh_CN.js +++ b/apps/federation/l10n/zh_CN.js @@ -1,15 +1,19 @@ OC.L10N.register( "federation", { - "Server added to the list of trusted ownClouds" : "服务器已成功添加至信任服务器列表。", + "Added to the list of trusted servers" : "添加到可信任服务器列表中", "Server is already in the list of trusted servers." : "服务器在线,并已成功添加至信任服务器列表。", - "No ownCloud server found" : "没有找到对应服务器", + "No server to federate with found" : "没有找到联盟服务器", "Could not add server" : "无法添加服务器", "Federation" : "联合", - "ownCloud Federation allows you to connect with other trusted ownClouds to exchange the user directory. For example this will be used to auto-complete external users for federated sharing." : "联合云系统使您可以方便快速的和其他用户共享文件。", + "Federation allows you to connect with other trusted servers to exchange the user directory. For example this will be used to auto-complete external users for federated sharing." : "联盟允许您与其他受信任的服务器连接并交换用户目录。 例如,这将用于自动完成外部用户组成共享联盟。", "Add server automatically once a federated share was created successfully" : "一旦联合共享创建成功自动添加服务器", - "Trusted ownCloud Servers" : "可信 ownCloud 服务器", - "+ Add ownCloud server" : "+ 添加 ownCloud 服务器", - "ownCloud Server" : "ownCloud 服务器" + "Trusted servers" : "可信任服务器", + "+ Add trusted server" : "+ 添加可信任服务器", + "Trusted server" : "可信任服务器", + "Add" : "添加", + "Trusted Servers" : "可信任服务器", + "+ Add Nextcloud server" : "+ 添加 Nextcloud 服务器 ", + "Nextcloud Server" : "Nextcloud 服务器" }, "nplurals=1; plural=0;"); diff --git a/apps/federation/l10n/zh_CN.json b/apps/federation/l10n/zh_CN.json index de8663eb7ab..147269fa63e 100644 --- a/apps/federation/l10n/zh_CN.json +++ b/apps/federation/l10n/zh_CN.json @@ -1,13 +1,17 @@ { "translations": { - "Server added to the list of trusted ownClouds" : "服务器已成功添加至信任服务器列表。", + "Added to the list of trusted servers" : "添加到可信任服务器列表中", "Server is already in the list of trusted servers." : "服务器在线,并已成功添加至信任服务器列表。", - "No ownCloud server found" : "没有找到对应服务器", + "No server to federate with found" : "没有找到联盟服务器", "Could not add server" : "无法添加服务器", "Federation" : "联合", - "ownCloud Federation allows you to connect with other trusted ownClouds to exchange the user directory. For example this will be used to auto-complete external users for federated sharing." : "联合云系统使您可以方便快速的和其他用户共享文件。", + "Federation allows you to connect with other trusted servers to exchange the user directory. For example this will be used to auto-complete external users for federated sharing." : "联盟允许您与其他受信任的服务器连接并交换用户目录。 例如,这将用于自动完成外部用户组成共享联盟。", "Add server automatically once a federated share was created successfully" : "一旦联合共享创建成功自动添加服务器", - "Trusted ownCloud Servers" : "可信 ownCloud 服务器", - "+ Add ownCloud server" : "+ 添加 ownCloud 服务器", - "ownCloud Server" : "ownCloud 服务器" + "Trusted servers" : "可信任服务器", + "+ Add trusted server" : "+ 添加可信任服务器", + "Trusted server" : "可信任服务器", + "Add" : "添加", + "Trusted Servers" : "可信任服务器", + "+ Add Nextcloud server" : "+ 添加 Nextcloud 服务器 ", + "Nextcloud Server" : "Nextcloud 服务器" },"pluralForm" :"nplurals=1; plural=0;" }
\ No newline at end of file diff --git a/apps/files/l10n/de_DE.js b/apps/files/l10n/de_DE.js index 148531d81ea..9ef2aed2802 100644 --- a/apps/files/l10n/de_DE.js +++ b/apps/files/l10n/de_DE.js @@ -52,7 +52,7 @@ OC.L10N.register( "_%n folder_::_%n folders_" : ["%n Ordner","%n Ordner"], "_%n file_::_%n files_" : ["%n Datei","%n Dateien"], "{dirs} and {files}" : "{dirs} und {files}", - "_including %n hidden_::_including %n hidden_" : ["%n versteckten eingeschlossen","%n versteckten eingeschlossen"], + "_including %n hidden_::_including %n hidden_" : ["%n versteckte eingeschlossen","%n versteckten eingeschlossen"], "You don’t have permission to upload or create files here" : "Sie haben keine Berechtigung, hier Dateien hochzuladen oder zu erstellen", "_Uploading %n file_::_Uploading %n files_" : ["%n Datei wird hoch geladen","%n Dateien werden hoch geladen"], "New" : "Neu", @@ -86,7 +86,7 @@ OC.L10N.register( "Moved by {user}" : "Verschoben durch {user}", "You created {file}" : "Sie haben {file} erstellt", "{user} created {file}" : "{user} hat {file} erstellt", - "{file} was created in a public folder" : "{file} wurde in einem öffentlichen Verzeichnis erstellt", + "{file} was created in a public folder" : "{file} wurde in einem öffentlichen Ordner erstellt", "You changed {file}" : "Sie haben {file} geändert", "{user} changed {file}" : "{user} hat {file} geändert", "You deleted {file}" : "Sie haben {file} gelöscht", @@ -98,11 +98,11 @@ OC.L10N.register( "You moved {oldfile} to {newfile}" : "Sie haben {oldfile} nach {newfile} verschoben", "{user} moved {oldfile} to {newfile}" : "{user} hat {oldfile} nach {newfile} verschoben", "A file has been added to or removed from your <strong>favorites</strong>" : "Eine Datei wurde Ihren <strong>Favoriten</strong> hinzugefügt oder daraus entfernt", - "A file or folder has been <strong>changed</strong> or <strong>renamed</strong>" : "Eine Datei oder Ordner wurden <strong>geändert</strong> oder <strong>umbenannt</strong>", + "A file or folder has been <strong>changed</strong> or <strong>renamed</strong>" : "Eine Datei oder Ordner wurde <strong>geändert</strong> oder <strong>umbenannt</strong>", "A new file or folder has been <strong>created</strong>" : "Eine neue Datei oder ein neuer Ordner wurde <strong>erstellt</strong>", - "A new file or folder has been <strong>deleted</strong>" : "Eine neue Datei oder ein neuer Ordner wurde <strong>gelöscht</strong>", + "A new file or folder has been <strong>deleted</strong>" : "Eine neue Datei oder Ordner wurde <strong>gelöscht</strong>", "Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>" : "Benachrichtigungen über Neues und Änderungen zu Ihren <strong>favorisierten Dateien</strong> beschränken <em>(nur im Stream)</em>", - "A new file or folder has been <strong>restored</strong>" : "Eine neue Datei oder ein neuer Ordner wurde <strong>wiederhergestellt</strong>", + "A new file or folder has been <strong>restored</strong>" : "Eine neue Datei oder Ordner wurde <strong>wiederhergestellt</strong>", "Upload (max. %s)" : "Hochladen (max. %s)", "File handling" : "Dateibehandlung", "Maximum upload size" : "Maximale Upload-Größe", diff --git a/apps/files/l10n/de_DE.json b/apps/files/l10n/de_DE.json index f9e9f545641..3466d7ebd9a 100644 --- a/apps/files/l10n/de_DE.json +++ b/apps/files/l10n/de_DE.json @@ -50,7 +50,7 @@ "_%n folder_::_%n folders_" : ["%n Ordner","%n Ordner"], "_%n file_::_%n files_" : ["%n Datei","%n Dateien"], "{dirs} and {files}" : "{dirs} und {files}", - "_including %n hidden_::_including %n hidden_" : ["%n versteckten eingeschlossen","%n versteckten eingeschlossen"], + "_including %n hidden_::_including %n hidden_" : ["%n versteckte eingeschlossen","%n versteckten eingeschlossen"], "You don’t have permission to upload or create files here" : "Sie haben keine Berechtigung, hier Dateien hochzuladen oder zu erstellen", "_Uploading %n file_::_Uploading %n files_" : ["%n Datei wird hoch geladen","%n Dateien werden hoch geladen"], "New" : "Neu", @@ -84,7 +84,7 @@ "Moved by {user}" : "Verschoben durch {user}", "You created {file}" : "Sie haben {file} erstellt", "{user} created {file}" : "{user} hat {file} erstellt", - "{file} was created in a public folder" : "{file} wurde in einem öffentlichen Verzeichnis erstellt", + "{file} was created in a public folder" : "{file} wurde in einem öffentlichen Ordner erstellt", "You changed {file}" : "Sie haben {file} geändert", "{user} changed {file}" : "{user} hat {file} geändert", "You deleted {file}" : "Sie haben {file} gelöscht", @@ -96,11 +96,11 @@ "You moved {oldfile} to {newfile}" : "Sie haben {oldfile} nach {newfile} verschoben", "{user} moved {oldfile} to {newfile}" : "{user} hat {oldfile} nach {newfile} verschoben", "A file has been added to or removed from your <strong>favorites</strong>" : "Eine Datei wurde Ihren <strong>Favoriten</strong> hinzugefügt oder daraus entfernt", - "A file or folder has been <strong>changed</strong> or <strong>renamed</strong>" : "Eine Datei oder Ordner wurden <strong>geändert</strong> oder <strong>umbenannt</strong>", + "A file or folder has been <strong>changed</strong> or <strong>renamed</strong>" : "Eine Datei oder Ordner wurde <strong>geändert</strong> oder <strong>umbenannt</strong>", "A new file or folder has been <strong>created</strong>" : "Eine neue Datei oder ein neuer Ordner wurde <strong>erstellt</strong>", - "A new file or folder has been <strong>deleted</strong>" : "Eine neue Datei oder ein neuer Ordner wurde <strong>gelöscht</strong>", + "A new file or folder has been <strong>deleted</strong>" : "Eine neue Datei oder Ordner wurde <strong>gelöscht</strong>", "Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>" : "Benachrichtigungen über Neues und Änderungen zu Ihren <strong>favorisierten Dateien</strong> beschränken <em>(nur im Stream)</em>", - "A new file or folder has been <strong>restored</strong>" : "Eine neue Datei oder ein neuer Ordner wurde <strong>wiederhergestellt</strong>", + "A new file or folder has been <strong>restored</strong>" : "Eine neue Datei oder Ordner wurde <strong>wiederhergestellt</strong>", "Upload (max. %s)" : "Hochladen (max. %s)", "File handling" : "Dateibehandlung", "Maximum upload size" : "Maximale Upload-Größe", diff --git a/apps/files/l10n/es.js b/apps/files/l10n/es.js index 8c7bb26edd5..756d3f1c0fc 100644 --- a/apps/files/l10n/es.js +++ b/apps/files/l10n/es.js @@ -28,6 +28,7 @@ OC.L10N.register( "Delete" : "Eliminar", "Disconnect storage" : "Desconectar almacenamiento", "Unshare" : "Dejar de compartir", + "Could not load info for file \"{file}\"" : "No se pudo cargar información para el archivo \"{file}\"", "Details" : "Detalles", "Select" : "Seleccionar", "Pending" : "Pendiente", diff --git a/apps/files/l10n/es.json b/apps/files/l10n/es.json index 2c95014b88f..69a24737364 100644 --- a/apps/files/l10n/es.json +++ b/apps/files/l10n/es.json @@ -26,6 +26,7 @@ "Delete" : "Eliminar", "Disconnect storage" : "Desconectar almacenamiento", "Unshare" : "Dejar de compartir", + "Could not load info for file \"{file}\"" : "No se pudo cargar información para el archivo \"{file}\"", "Details" : "Detalles", "Select" : "Seleccionar", "Pending" : "Pendiente", diff --git a/apps/files/l10n/sk_SK.js b/apps/files/l10n/sk_SK.js index 1f28568250f..eab25004bb3 100644 --- a/apps/files/l10n/sk_SK.js +++ b/apps/files/l10n/sk_SK.js @@ -144,6 +144,8 @@ OC.L10N.register( "Total file size {size1} exceeds upload limit {size2}" : "Celková veľkosť súboru {size1} prekračuje upload limit {size2}", "Error uploading file \"{fileName}\": {message}" : "Chyba pri nahrávaní súboru \"{fileName}\": {message}", "Could not get result from server." : "Nepodarilo sa dostať výsledky zo servera.", + "Any moment now..." : "Už každú chvíľu…", + "Soon..." : "Čoskoro…", "File upload is in progress. Leaving the page now will cancel the upload." : "Opustenie stránky zruší práve prebiehajúce odosielanie súboru.", "No entries in this folder match '{filter}'" : "V tomto priečinku nič nezodpovedá '{filter}'", "Local link" : "Lokálny odkaz", diff --git a/apps/files/l10n/sk_SK.json b/apps/files/l10n/sk_SK.json index dd3e5ed91fd..7a920a37d1b 100644 --- a/apps/files/l10n/sk_SK.json +++ b/apps/files/l10n/sk_SK.json @@ -142,6 +142,8 @@ "Total file size {size1} exceeds upload limit {size2}" : "Celková veľkosť súboru {size1} prekračuje upload limit {size2}", "Error uploading file \"{fileName}\": {message}" : "Chyba pri nahrávaní súboru \"{fileName}\": {message}", "Could not get result from server." : "Nepodarilo sa dostať výsledky zo servera.", + "Any moment now..." : "Už každú chvíľu…", + "Soon..." : "Čoskoro…", "File upload is in progress. Leaving the page now will cancel the upload." : "Opustenie stránky zruší práve prebiehajúce odosielanie súboru.", "No entries in this folder match '{filter}'" : "V tomto priečinku nič nezodpovedá '{filter}'", "Local link" : "Lokálny odkaz", diff --git a/apps/files/l10n/sq.js b/apps/files/l10n/sq.js index 94184267974..52bf5f7accf 100644 --- a/apps/files/l10n/sq.js +++ b/apps/files/l10n/sq.js @@ -28,6 +28,7 @@ OC.L10N.register( "Delete" : "Fshije", "Disconnect storage" : "Shkëpute depozitën", "Unshare" : "Hiqe ndarjen", + "Could not load info for file \"{file}\"" : "Nuk mund të ngarkohet informacioni për skedarin \"{file}\"", "Details" : "Hollësi", "Select" : "Përzgjidhe", "Pending" : "Në pritje", diff --git a/apps/files/l10n/sq.json b/apps/files/l10n/sq.json index dff531d53b6..0ecb279e692 100644 --- a/apps/files/l10n/sq.json +++ b/apps/files/l10n/sq.json @@ -26,6 +26,7 @@ "Delete" : "Fshije", "Disconnect storage" : "Shkëpute depozitën", "Unshare" : "Hiqe ndarjen", + "Could not load info for file \"{file}\"" : "Nuk mund të ngarkohet informacioni për skedarin \"{file}\"", "Details" : "Hollësi", "Select" : "Përzgjidhe", "Pending" : "Në pritje", diff --git a/apps/files/l10n/sv.js b/apps/files/l10n/sv.js index ae25c1e5321..2325929ac21 100644 --- a/apps/files/l10n/sv.js +++ b/apps/files/l10n/sv.js @@ -6,7 +6,7 @@ OC.L10N.register( "Unknown error" : "Okänt fel", "Files" : "Filer", "All files" : "Alla filer", - "Recent" : "Nyligen", + "Recent" : "Senaste", "File could not be found" : "Fil kunde inte hittas", "Home" : "Hem", "Close" : "Stäng", @@ -21,7 +21,7 @@ OC.L10N.register( "..." : "...", "{loadedSize} of {totalSize} ({bitrate})" : "{loadedSize} av {totalSize} ({bitrate})", "Actions" : "Åtgärder", - "Download" : "Ladda ner", + "Download" : "Ladda ned", "Rename" : "Byt namn", "Move" : "Flytta", "Target folder" : "Målmapp", @@ -68,7 +68,7 @@ OC.L10N.register( "_%n byte_::_%n bytes_" : ["%n bytes","%n bytes"], "Favorited" : "Favoriserad", "Favorite" : "Favorit", - "Copy local link" : "Kopiera lokal länk", + "Copy local link" : "Kopiera den lokala länken", "Folder" : "Mapp", "New folder" : "Ny mapp", "Upload" : "Ladda upp", @@ -86,7 +86,7 @@ OC.L10N.register( "Moved by {user}" : "Flyttad av {user}", "You created {file}" : "Du skapade {file}", "{user} created {file}" : "{user} skapade {file}", - "{file} was created in a public folder" : "{file} skapades i en publik mapp", + "{file} was created in a public folder" : "{file} skapades i en offentlig mapp", "You changed {file}" : "Du ändrade {file}", "{user} changed {file}" : "{user} ändrade {file}", "You deleted {file}" : "Du raderade {file}", @@ -101,7 +101,7 @@ OC.L10N.register( "A file or folder has been <strong>changed</strong> or <strong>renamed</strong>" : "En fil har blivit <strong>ändrad</strong> eller <strong>bytt namn</strong>", "A new file or folder has been <strong>created</strong>" : "En ny fil eller mapp har blivit <strong>skapad</strong>", "A new file or folder has been <strong>deleted</strong>" : "En ny fil har blivit <strong>raderad</strong>", - "Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>" : "Begränsa notiser om skapande och ändringar till dina <strong>favoritfiler</strong> <em>(Endast ström)</em>", + "Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>" : "Begränsa notiser om skapande och ändringar till dina <strong>favoritfiler</strong> <em>(Endast i flödet)</em>", "A new file or folder has been <strong>restored</strong>" : "En ny fil eller mapp har blivit <strong>återställd</strong>", "Upload (max. %s)" : "Ladda upp (max. %s)", "File handling" : "Filhantering", @@ -121,7 +121,7 @@ OC.L10N.register( "Upload too large" : "För stor uppladdning", "The files you are trying to upload exceed the maximum size for file uploads on this server." : "Filerna du försöker ladda upp överstiger den maximala storleken för filöverföringar på servern.", "No favorites" : "Inga favoriter", - "Files and folders you mark as favorite will show up here" : "Filer och mappar du markerat som favoriter kommer att visas här", + "Files and folders you mark as favorite will show up here" : "Filer och mappar markerade som favoriter kommer att visas här", "Shared with you" : "Delad med dig", "Shared with others" : "Delad med andra", "Shared by link" : "Delad via länk", @@ -165,7 +165,7 @@ OC.L10N.register( "A file or folder has been <strong>restored</strong>" : "En ny fil eller mapp har blivit <strong>återskapad</strong>", "You created %1$s" : "Du skapade %1$s", "%2$s created %1$s" : "%2$s skapade %1$s", - "%1$s was created in a public folder" : "%1$s skapades i en publik mapp", + "%1$s was created in a public folder" : "%1$s skapades i en offentlig mapp", "You changed %1$s" : "Du ändrade %1$s", "%2$s changed %1$s" : "%2$s ändrade %1$s", "You deleted %1$s" : "Du raderade %1$s", diff --git a/apps/files/l10n/sv.json b/apps/files/l10n/sv.json index 3c8a43bbf79..cb66007b384 100644 --- a/apps/files/l10n/sv.json +++ b/apps/files/l10n/sv.json @@ -4,7 +4,7 @@ "Unknown error" : "Okänt fel", "Files" : "Filer", "All files" : "Alla filer", - "Recent" : "Nyligen", + "Recent" : "Senaste", "File could not be found" : "Fil kunde inte hittas", "Home" : "Hem", "Close" : "Stäng", @@ -19,7 +19,7 @@ "..." : "...", "{loadedSize} of {totalSize} ({bitrate})" : "{loadedSize} av {totalSize} ({bitrate})", "Actions" : "Åtgärder", - "Download" : "Ladda ner", + "Download" : "Ladda ned", "Rename" : "Byt namn", "Move" : "Flytta", "Target folder" : "Målmapp", @@ -66,7 +66,7 @@ "_%n byte_::_%n bytes_" : ["%n bytes","%n bytes"], "Favorited" : "Favoriserad", "Favorite" : "Favorit", - "Copy local link" : "Kopiera lokal länk", + "Copy local link" : "Kopiera den lokala länken", "Folder" : "Mapp", "New folder" : "Ny mapp", "Upload" : "Ladda upp", @@ -84,7 +84,7 @@ "Moved by {user}" : "Flyttad av {user}", "You created {file}" : "Du skapade {file}", "{user} created {file}" : "{user} skapade {file}", - "{file} was created in a public folder" : "{file} skapades i en publik mapp", + "{file} was created in a public folder" : "{file} skapades i en offentlig mapp", "You changed {file}" : "Du ändrade {file}", "{user} changed {file}" : "{user} ändrade {file}", "You deleted {file}" : "Du raderade {file}", @@ -99,7 +99,7 @@ "A file or folder has been <strong>changed</strong> or <strong>renamed</strong>" : "En fil har blivit <strong>ändrad</strong> eller <strong>bytt namn</strong>", "A new file or folder has been <strong>created</strong>" : "En ny fil eller mapp har blivit <strong>skapad</strong>", "A new file or folder has been <strong>deleted</strong>" : "En ny fil har blivit <strong>raderad</strong>", - "Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>" : "Begränsa notiser om skapande och ändringar till dina <strong>favoritfiler</strong> <em>(Endast ström)</em>", + "Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>" : "Begränsa notiser om skapande och ändringar till dina <strong>favoritfiler</strong> <em>(Endast i flödet)</em>", "A new file or folder has been <strong>restored</strong>" : "En ny fil eller mapp har blivit <strong>återställd</strong>", "Upload (max. %s)" : "Ladda upp (max. %s)", "File handling" : "Filhantering", @@ -119,7 +119,7 @@ "Upload too large" : "För stor uppladdning", "The files you are trying to upload exceed the maximum size for file uploads on this server." : "Filerna du försöker ladda upp överstiger den maximala storleken för filöverföringar på servern.", "No favorites" : "Inga favoriter", - "Files and folders you mark as favorite will show up here" : "Filer och mappar du markerat som favoriter kommer att visas här", + "Files and folders you mark as favorite will show up here" : "Filer och mappar markerade som favoriter kommer att visas här", "Shared with you" : "Delad med dig", "Shared with others" : "Delad med andra", "Shared by link" : "Delad via länk", @@ -163,7 +163,7 @@ "A file or folder has been <strong>restored</strong>" : "En ny fil eller mapp har blivit <strong>återskapad</strong>", "You created %1$s" : "Du skapade %1$s", "%2$s created %1$s" : "%2$s skapade %1$s", - "%1$s was created in a public folder" : "%1$s skapades i en publik mapp", + "%1$s was created in a public folder" : "%1$s skapades i en offentlig mapp", "You changed %1$s" : "Du ändrade %1$s", "%2$s changed %1$s" : "%2$s ändrade %1$s", "You deleted %1$s" : "Du raderade %1$s", diff --git a/apps/files/l10n/zh_CN.js b/apps/files/l10n/zh_CN.js index f0b12008912..8f053b231d6 100644 --- a/apps/files/l10n/zh_CN.js +++ b/apps/files/l10n/zh_CN.js @@ -1,10 +1,12 @@ OC.L10N.register( "files", { + "Storage is temporarily not available" : "存储器当前无法访问", "Storage invalid" : "存储空间无效", "Unknown error" : "未知错误", "Files" : "文件", "All files" : "全部文件", + "Recent" : "最近", "File could not be found" : "文件未找到", "Home" : "家庭", "Close" : "关闭", @@ -13,15 +15,20 @@ OC.L10N.register( "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} 可用。", + "Target folder \"{dir}\" does not exist any more" : "目标目录\"{dir}\" 不存在", + "Not enough free space" : "没有足够空间", "Uploading..." : "上传中...", "..." : "...", "{loadedSize} of {totalSize} ({bitrate})" : "{loadedSize} / {totalSize} ({bitrate})", "Actions" : "动作", "Download" : "下载", "Rename" : "重命名", + "Move" : "移动", + "Target folder" : "目标目录", "Delete" : "删除", "Disconnect storage" : "断开储存连接", "Unshare" : "取消共享", + "Could not load info for file \"{file}\"" : "无法载入\"{file}\"文件信息", "Details" : "详细信息", "Select" : "选择", "Pending" : "等待", @@ -38,13 +45,15 @@ OC.L10N.register( "Could not create file \"{file}\" because it already exists" : "不能创建文件 \"{file}\" ,因为它已经存在", "Could not create folder \"{dir}\" because it already exists" : "不能创建文件夹 \"{dir}\" ,因为它已经存在", "Error deleting file \"{fileName}\"." : "删除文件 \"{fileName}\" 时出错。", + "No search results in other folders for '{tag}{filter}{endtag}'" : " 在其他文件夹未找到包含 '{tag}{filter}{endtag}'标签的结果", "Name" : "名称", "Size" : "大小", "Modified" : "修改日期", "_%n folder_::_%n folders_" : ["%n 文件夹"], "_%n file_::_%n files_" : ["%n个文件"], "{dirs} and {files}" : "{dirs} 和 {files}", - "You don’t have permission to upload or create files here" : "您没有权限来上传湖州哦和创建文件", + "_including %n hidden_::_including %n hidden_" : ["包括 %n 隐藏的"], + "You don’t have permission to upload or create files here" : "您没有权限在此上传或创建文件", "_Uploading %n file_::_Uploading %n files_" : ["上传 %n 个文件"], "New" : "新建", "\"{name}\" is an invalid file name." : "“{name}”是一个无效的文件名。", @@ -54,16 +63,46 @@ OC.L10N.register( "Storage of {owner} is almost full ({usedSpacePercent}%)" : "{owner} 的存储空间即将用完 ({usedSpacePercent}%)", "Your storage is almost full ({usedSpacePercent}%)" : "您的存储空间即将用完 ({usedSpacePercent}%)", "_matches '{filter}'_::_match '{filter}'_" : ["匹配“{filter}”"], + "View in folder" : "在文件夹里查看", "Path" : "路径", "_%n byte_::_%n bytes_" : ["%n 字节"], "Favorited" : "已收藏", "Favorite" : "收藏", + "Copy local link" : "复制本地链接", "Folder" : "文件夹", "New folder" : "增加文件夹", "Upload" : "上传", "An error occurred while trying to update the tags" : "更新标签时出错", + "Added to favorites" : "添加到收藏", + "Removed from favorites" : "取消收藏", + "You added {file} to your favorites" : "您添加了 {file} 文件到您的收藏夹", + "You removed {file} from your favorites" : "您从您的收藏夹删除了 {file} 文件", + "File changes" : "文件发生变化", + "Created by {user}" : "由 {user} 创建", + "Changed by {user}" : "由 {user} 更改", + "Deleted by {user}" : "由 {user} 删除", + "Restored by {user}" : "由 {user} 恢复", + "Renamed by {user}" : "由 {user} 重命名", + "Moved by {user}" : "由 {user} 移动", + "You created {file}" : "您创建了 {file}", + "{user} created {file}" : "{user} 创建了 {file}", + "{file} was created in a public folder" : "{file} 被创建在公共文件夹", + "You changed {file}" : "您更改了 {file}", + "{user} changed {file}" : "{user} 更改了 {file}", + "You deleted {file}" : "您删除了{file}", + "{user} deleted {file}" : "{user} 删除了 {file}", + "You restored {file}" : "您恢复了{file}", + "{user} restored {file}" : "{user} 恢复了 {file}", + "You renamed {oldfile} to {newfile}" : "您将 {oldfile} 改名为 {newfile}", + "{user} renamed {oldfile} to {newfile}" : "{user} 将 {oldfile} 改名为 {newfile}", + "You moved {oldfile} to {newfile}" : "您移动 {oldfile} 到 {newfile}", + "{user} moved {oldfile} to {newfile}" : "{user} 移动 {oldfile} 到 {newfile}", + "A file has been added to or removed from your <strong>favorites</strong>" : "一个文件从您的 <strong>收藏夹</strong> 添加或者删除", + "A file or folder has been <strong>changed</strong> or <strong>renamed</strong>" : "一个文件或者文件夹被 <strong>更改</strong> 或者 <strong>重命名</strong>", "A new file or folder has been <strong>created</strong>" : "一个新的文件或文件夹已被<strong>创建</strong>", + "A new file or folder has been <strong>deleted</strong>" : "一个新文件或者文件夹被 <strong>删除</strong>", "Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>" : "针对<strong>已收藏文件</strong>的新建和修改发送有限的通知 <em>(仅流)</em>", + "A new file or folder has been <strong>restored</strong>" : "一个新文件或者文件夹被<strong>恢复</strong>", "Upload (max. %s)" : "上传 (最大 %s)", "File handling" : "文件处理", "Maximum upload size" : "最大上传大小", @@ -83,6 +122,11 @@ OC.L10N.register( "The files you are trying to upload exceed the maximum size for file uploads on this server." : "您正尝试上传的文件超过了此服务器可以上传的最大容量限制", "No favorites" : "无收藏", "Files and folders you mark as favorite will show up here" : "收藏的文件和文件夹会在这里显示", + "Shared with you" : "与您分享", + "Shared with others" : "与他人分享", + "Shared by link" : "通过链接分享", + "Tags" : "标签", + "Deleted files" : "已删除的文件", "Text file" : "文本文件", "New text file.txt" : "创建文本文件 .txt", "Storage not available" : "存储空间不可用", diff --git a/apps/files/l10n/zh_CN.json b/apps/files/l10n/zh_CN.json index 070e92d2528..68e15813cdb 100644 --- a/apps/files/l10n/zh_CN.json +++ b/apps/files/l10n/zh_CN.json @@ -1,8 +1,10 @@ { "translations": { + "Storage is temporarily not available" : "存储器当前无法访问", "Storage invalid" : "存储空间无效", "Unknown error" : "未知错误", "Files" : "文件", "All files" : "全部文件", + "Recent" : "最近", "File could not be found" : "文件未找到", "Home" : "家庭", "Close" : "关闭", @@ -11,15 +13,20 @@ "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} 可用。", + "Target folder \"{dir}\" does not exist any more" : "目标目录\"{dir}\" 不存在", + "Not enough free space" : "没有足够空间", "Uploading..." : "上传中...", "..." : "...", "{loadedSize} of {totalSize} ({bitrate})" : "{loadedSize} / {totalSize} ({bitrate})", "Actions" : "动作", "Download" : "下载", "Rename" : "重命名", + "Move" : "移动", + "Target folder" : "目标目录", "Delete" : "删除", "Disconnect storage" : "断开储存连接", "Unshare" : "取消共享", + "Could not load info for file \"{file}\"" : "无法载入\"{file}\"文件信息", "Details" : "详细信息", "Select" : "选择", "Pending" : "等待", @@ -36,13 +43,15 @@ "Could not create file \"{file}\" because it already exists" : "不能创建文件 \"{file}\" ,因为它已经存在", "Could not create folder \"{dir}\" because it already exists" : "不能创建文件夹 \"{dir}\" ,因为它已经存在", "Error deleting file \"{fileName}\"." : "删除文件 \"{fileName}\" 时出错。", + "No search results in other folders for '{tag}{filter}{endtag}'" : " 在其他文件夹未找到包含 '{tag}{filter}{endtag}'标签的结果", "Name" : "名称", "Size" : "大小", "Modified" : "修改日期", "_%n folder_::_%n folders_" : ["%n 文件夹"], "_%n file_::_%n files_" : ["%n个文件"], "{dirs} and {files}" : "{dirs} 和 {files}", - "You don’t have permission to upload or create files here" : "您没有权限来上传湖州哦和创建文件", + "_including %n hidden_::_including %n hidden_" : ["包括 %n 隐藏的"], + "You don’t have permission to upload or create files here" : "您没有权限在此上传或创建文件", "_Uploading %n file_::_Uploading %n files_" : ["上传 %n 个文件"], "New" : "新建", "\"{name}\" is an invalid file name." : "“{name}”是一个无效的文件名。", @@ -52,16 +61,46 @@ "Storage of {owner} is almost full ({usedSpacePercent}%)" : "{owner} 的存储空间即将用完 ({usedSpacePercent}%)", "Your storage is almost full ({usedSpacePercent}%)" : "您的存储空间即将用完 ({usedSpacePercent}%)", "_matches '{filter}'_::_match '{filter}'_" : ["匹配“{filter}”"], + "View in folder" : "在文件夹里查看", "Path" : "路径", "_%n byte_::_%n bytes_" : ["%n 字节"], "Favorited" : "已收藏", "Favorite" : "收藏", + "Copy local link" : "复制本地链接", "Folder" : "文件夹", "New folder" : "增加文件夹", "Upload" : "上传", "An error occurred while trying to update the tags" : "更新标签时出错", + "Added to favorites" : "添加到收藏", + "Removed from favorites" : "取消收藏", + "You added {file} to your favorites" : "您添加了 {file} 文件到您的收藏夹", + "You removed {file} from your favorites" : "您从您的收藏夹删除了 {file} 文件", + "File changes" : "文件发生变化", + "Created by {user}" : "由 {user} 创建", + "Changed by {user}" : "由 {user} 更改", + "Deleted by {user}" : "由 {user} 删除", + "Restored by {user}" : "由 {user} 恢复", + "Renamed by {user}" : "由 {user} 重命名", + "Moved by {user}" : "由 {user} 移动", + "You created {file}" : "您创建了 {file}", + "{user} created {file}" : "{user} 创建了 {file}", + "{file} was created in a public folder" : "{file} 被创建在公共文件夹", + "You changed {file}" : "您更改了 {file}", + "{user} changed {file}" : "{user} 更改了 {file}", + "You deleted {file}" : "您删除了{file}", + "{user} deleted {file}" : "{user} 删除了 {file}", + "You restored {file}" : "您恢复了{file}", + "{user} restored {file}" : "{user} 恢复了 {file}", + "You renamed {oldfile} to {newfile}" : "您将 {oldfile} 改名为 {newfile}", + "{user} renamed {oldfile} to {newfile}" : "{user} 将 {oldfile} 改名为 {newfile}", + "You moved {oldfile} to {newfile}" : "您移动 {oldfile} 到 {newfile}", + "{user} moved {oldfile} to {newfile}" : "{user} 移动 {oldfile} 到 {newfile}", + "A file has been added to or removed from your <strong>favorites</strong>" : "一个文件从您的 <strong>收藏夹</strong> 添加或者删除", + "A file or folder has been <strong>changed</strong> or <strong>renamed</strong>" : "一个文件或者文件夹被 <strong>更改</strong> 或者 <strong>重命名</strong>", "A new file or folder has been <strong>created</strong>" : "一个新的文件或文件夹已被<strong>创建</strong>", + "A new file or folder has been <strong>deleted</strong>" : "一个新文件或者文件夹被 <strong>删除</strong>", "Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>" : "针对<strong>已收藏文件</strong>的新建和修改发送有限的通知 <em>(仅流)</em>", + "A new file or folder has been <strong>restored</strong>" : "一个新文件或者文件夹被<strong>恢复</strong>", "Upload (max. %s)" : "上传 (最大 %s)", "File handling" : "文件处理", "Maximum upload size" : "最大上传大小", @@ -81,6 +120,11 @@ "The files you are trying to upload exceed the maximum size for file uploads on this server." : "您正尝试上传的文件超过了此服务器可以上传的最大容量限制", "No favorites" : "无收藏", "Files and folders you mark as favorite will show up here" : "收藏的文件和文件夹会在这里显示", + "Shared with you" : "与您分享", + "Shared with others" : "与他人分享", + "Shared by link" : "通过链接分享", + "Tags" : "标签", + "Deleted files" : "已删除的文件", "Text file" : "文本文件", "New text file.txt" : "创建文本文件 .txt", "Storage not available" : "存储空间不可用", diff --git a/apps/files_external/3rdparty/autoload.php b/apps/files_external/3rdparty/autoload.php index 78e3de4ca0c..5875a3b996a 100644 --- a/apps/files_external/3rdparty/autoload.php +++ b/apps/files_external/3rdparty/autoload.php @@ -2,6 +2,6 @@ // autoload.php @generated by Composer -require_once __DIR__ . '/composer' . '/autoload_real.php'; +require_once __DIR__ . '/composer/autoload_real.php'; return ComposerAutoloaderInit98fe9b281934250b3a93f69a5ce843b3::getLoader(); diff --git a/apps/files_external/3rdparty/composer.json b/apps/files_external/3rdparty/composer.json index 158478741ec..84f1e192018 100644 --- a/apps/files_external/3rdparty/composer.json +++ b/apps/files_external/3rdparty/composer.json @@ -8,7 +8,7 @@ "classmap-authoritative": true }, "require": { - "icewind/smb": "1.1.0", + "icewind/smb": "2.0.0", "icewind/streams": "0.4.1" } } diff --git a/apps/files_external/3rdparty/composer.lock b/apps/files_external/3rdparty/composer.lock index 8f324299ff3..305cc8dc4ba 100644 --- a/apps/files_external/3rdparty/composer.lock +++ b/apps/files_external/3rdparty/composer.lock @@ -4,30 +4,29 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "8ed3150b0b3e916ad66558242b4cf2a0", - "content-hash": "70722dcee13b3ac1c1951479b7431c97", + "hash": "a8b4080027a432533f6d0597ad527fe7", + "content-hash": "8165981792437b03b39c11d28b061eda", "packages": [ { "name": "icewind/smb", - "version": "v1.1.0", + "version": "v2.0.0", "source": { "type": "git", "url": "https://github.com/icewind1991/SMB.git", - "reference": "822f924967c68228555cea84ea44765f8e85c601" + "reference": "95a5ecbaf92617f9800ad7d6070ef31d8ff28c3a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/icewind1991/SMB/zipball/822f924967c68228555cea84ea44765f8e85c601", - "reference": "822f924967c68228555cea84ea44765f8e85c601", + "url": "https://api.github.com/repos/icewind1991/SMB/zipball/95a5ecbaf92617f9800ad7d6070ef31d8ff28c3a", + "reference": "95a5ecbaf92617f9800ad7d6070ef31d8ff28c3a", "shasum": "" }, "require": { "icewind/streams": ">=0.2.0", - "php": ">=5.3" + "php": ">=5.4" }, "require-dev": { - "phpunit/phpunit": "^4.8", - "satooshi/php-coveralls": "v1.0.0" + "phpunit/phpunit": "^4.8" }, "type": "library", "autoload": { @@ -47,7 +46,7 @@ } ], "description": "php wrapper for smbclient and libsmbclient-php", - "time": "2016-04-26 13:26:39" + "time": "2016-12-13 13:56:55" }, { "name": "icewind/streams", diff --git a/apps/files_external/3rdparty/composer/ClassLoader.php b/apps/files_external/3rdparty/composer/ClassLoader.php index ff6ecfb822f..ac67d302a18 100644 --- a/apps/files_external/3rdparty/composer/ClassLoader.php +++ b/apps/files_external/3rdparty/composer/ClassLoader.php @@ -53,8 +53,8 @@ class ClassLoader private $useIncludePath = false; private $classMap = array(); - private $classMapAuthoritative = false; + private $missingClasses = array(); public function getPrefixes() { @@ -322,20 +322,20 @@ class ClassLoader if (isset($this->classMap[$class])) { return $this->classMap[$class]; } - if ($this->classMapAuthoritative) { + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { return false; } $file = $this->findFileWithExtension($class, '.php'); // Search for Hack files if we are running on HHVM - if ($file === null && defined('HHVM_VERSION')) { + if (false === $file && defined('HHVM_VERSION')) { $file = $this->findFileWithExtension($class, '.hh'); } - if ($file === null) { + if (false === $file) { // Remember that this class does not exist. - return $this->classMap[$class] = false; + $this->missingClasses[$class] = true; } return $file; @@ -399,6 +399,8 @@ class ClassLoader if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { return $file; } + + return false; } } diff --git a/apps/files_external/3rdparty/composer/autoload_classmap.php b/apps/files_external/3rdparty/composer/autoload_classmap.php index 0f9a43c6cd7..ba5d06f7011 100644 --- a/apps/files_external/3rdparty/composer/autoload_classmap.php +++ b/apps/files_external/3rdparty/composer/autoload_classmap.php @@ -7,6 +7,7 @@ $baseDir = $vendorDir; return array( 'Icewind\\SMB\\AbstractShare' => $vendorDir . '/icewind/smb/src/AbstractShare.php', + 'Icewind\\SMB\\Change' => $vendorDir . '/icewind/smb/src/Change.php', 'Icewind\\SMB\\Connection' => $vendorDir . '/icewind/smb/src/Connection.php', 'Icewind\\SMB\\ErrorCodes' => $vendorDir . '/icewind/smb/src/ErrorCodes.php', 'Icewind\\SMB\\Exception\\AccessDeniedException' => $vendorDir . '/icewind/smb/src/Exception/AccessDeniedException.php', @@ -15,6 +16,7 @@ return array( 'Icewind\\SMB\\Exception\\ConnectException' => $vendorDir . '/icewind/smb/src/Exception/ConnectException.php', 'Icewind\\SMB\\Exception\\ConnectionException' => $vendorDir . '/icewind/smb/src/Exception/ConnectionException.php', 'Icewind\\SMB\\Exception\\ConnectionRefusedException' => $vendorDir . '/icewind/smb/src/Exception/ConnectionRefusedException.php', + 'Icewind\\SMB\\Exception\\DependencyException' => $vendorDir . '/icewind/smb/src/Exception/DependencyException.php', 'Icewind\\SMB\\Exception\\Exception' => $vendorDir . '/icewind/smb/src/Exception/Exception.php', 'Icewind\\SMB\\Exception\\FileInUseException' => $vendorDir . '/icewind/smb/src/Exception/FileInUseException.php', 'Icewind\\SMB\\Exception\\ForbiddenException' => $vendorDir . '/icewind/smb/src/Exception/ForbiddenException.php', @@ -31,17 +33,27 @@ return array( 'Icewind\\SMB\\Exception\\TimedOutException' => $vendorDir . '/icewind/smb/src/Exception/TimedOutException.php', 'Icewind\\SMB\\FileInfo' => $vendorDir . '/icewind/smb/src/FileInfo.php', 'Icewind\\SMB\\IFileInfo' => $vendorDir . '/icewind/smb/src/IFileInfo.php', + 'Icewind\\SMB\\INotifyHandler' => $vendorDir . '/icewind/smb/src/INotifyHandler.php', 'Icewind\\SMB\\IShare' => $vendorDir . '/icewind/smb/src/IShare.php', 'Icewind\\SMB\\NativeFileInfo' => $vendorDir . '/icewind/smb/src/NativeFileInfo.php', 'Icewind\\SMB\\NativeServer' => $vendorDir . '/icewind/smb/src/NativeServer.php', 'Icewind\\SMB\\NativeShare' => $vendorDir . '/icewind/smb/src/NativeShare.php', 'Icewind\\SMB\\NativeState' => $vendorDir . '/icewind/smb/src/NativeState.php', 'Icewind\\SMB\\NativeStream' => $vendorDir . '/icewind/smb/src/NativeStream.php', + 'Icewind\\SMB\\NotifyHandler' => $vendorDir . '/icewind/smb/src/NotifyHandler.php', 'Icewind\\SMB\\Parser' => $vendorDir . '/icewind/smb/src/Parser.php', 'Icewind\\SMB\\RawConnection' => $vendorDir . '/icewind/smb/src/RawConnection.php', 'Icewind\\SMB\\Server' => $vendorDir . '/icewind/smb/src/Server.php', 'Icewind\\SMB\\Share' => $vendorDir . '/icewind/smb/src/Share.php', 'Icewind\\SMB\\System' => $vendorDir . '/icewind/smb/src/System.php', + 'Icewind\\SMB\\Test\\AbstractShare' => $vendorDir . '/icewind/smb/tests/AbstractShare.php', + 'Icewind\\SMB\\Test\\NativeShare' => $vendorDir . '/icewind/smb/tests/NativeShare.php', + 'Icewind\\SMB\\Test\\NativeStream' => $vendorDir . '/icewind/smb/tests/NativeStream.php', + 'Icewind\\SMB\\Test\\NotifyHandlerTest' => $vendorDir . '/icewind/smb/tests/NotifyHandlerTest.php', + 'Icewind\\SMB\\Test\\Parser' => $vendorDir . '/icewind/smb/tests/Parser.php', + 'Icewind\\SMB\\Test\\Server' => $vendorDir . '/icewind/smb/tests/Server.php', + 'Icewind\\SMB\\Test\\Share' => $vendorDir . '/icewind/smb/tests/Share.php', + 'Icewind\\SMB\\Test\\TestCase' => $vendorDir . '/icewind/smb/tests/TestCase.php', 'Icewind\\SMB\\TimeZoneProvider' => $vendorDir . '/icewind/smb/src/TimeZoneProvider.php', 'Icewind\\Streams\\CallbackWrapper' => $vendorDir . '/icewind/streams/src/CallbackWrapper.php', 'Icewind\\Streams\\Directory' => $vendorDir . '/icewind/streams/src/Directory.php', diff --git a/apps/files_external/3rdparty/composer/autoload_real.php b/apps/files_external/3rdparty/composer/autoload_real.php index 6f27fffb9da..4ac68127fea 100644 --- a/apps/files_external/3rdparty/composer/autoload_real.php +++ b/apps/files_external/3rdparty/composer/autoload_real.php @@ -23,9 +23,16 @@ class ComposerAutoloaderInit98fe9b281934250b3a93f69a5ce843b3 self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInit98fe9b281934250b3a93f69a5ce843b3', 'loadClassLoader')); - $classMap = require __DIR__ . '/autoload_classmap.php'; - if ($classMap) { - $loader->addClassMap($classMap); + $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); + if ($useStaticLoader) { + require_once __DIR__ . '/autoload_static.php'; + + call_user_func(\Composer\Autoload\ComposerStaticInit98fe9b281934250b3a93f69a5ce843b3::getInitializer($loader)); + } else { + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } } $loader->setClassMapAuthoritative(true); diff --git a/apps/files_external/3rdparty/composer/autoload_static.php b/apps/files_external/3rdparty/composer/autoload_static.php new file mode 100644 index 00000000000..10c09bfc409 --- /dev/null +++ b/apps/files_external/3rdparty/composer/autoload_static.php @@ -0,0 +1,112 @@ +<?php + +// autoload_static.php @generated by Composer + +namespace Composer\Autoload; + +class ComposerStaticInit98fe9b281934250b3a93f69a5ce843b3 +{ + public static $prefixLengthsPsr4 = array ( + 'I' => + array ( + 'Icewind\\Streams\\Tests\\' => 22, + 'Icewind\\Streams\\' => 16, + 'Icewind\\SMB\\Test\\' => 17, + 'Icewind\\SMB\\' => 12, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Icewind\\Streams\\Tests\\' => + array ( + 0 => __DIR__ . '/..' . '/icewind/streams/tests', + ), + 'Icewind\\Streams\\' => + array ( + 0 => __DIR__ . '/..' . '/icewind/streams/src', + ), + 'Icewind\\SMB\\Test\\' => + array ( + 0 => __DIR__ . '/..' . '/icewind/smb/tests', + ), + 'Icewind\\SMB\\' => + array ( + 0 => __DIR__ . '/..' . '/icewind/smb/src', + ), + ); + + public static $classMap = array ( + 'Icewind\\SMB\\AbstractShare' => __DIR__ . '/..' . '/icewind/smb/src/AbstractShare.php', + 'Icewind\\SMB\\Change' => __DIR__ . '/..' . '/icewind/smb/src/Change.php', + 'Icewind\\SMB\\Connection' => __DIR__ . '/..' . '/icewind/smb/src/Connection.php', + 'Icewind\\SMB\\ErrorCodes' => __DIR__ . '/..' . '/icewind/smb/src/ErrorCodes.php', + 'Icewind\\SMB\\Exception\\AccessDeniedException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/AccessDeniedException.php', + 'Icewind\\SMB\\Exception\\AlreadyExistsException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/AlreadyExistsException.php', + 'Icewind\\SMB\\Exception\\AuthenticationException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/AuthenticationException.php', + 'Icewind\\SMB\\Exception\\ConnectException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/ConnectException.php', + 'Icewind\\SMB\\Exception\\ConnectionException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/ConnectionException.php', + 'Icewind\\SMB\\Exception\\ConnectionRefusedException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/ConnectionRefusedException.php', + 'Icewind\\SMB\\Exception\\DependencyException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/DependencyException.php', + 'Icewind\\SMB\\Exception\\Exception' => __DIR__ . '/..' . '/icewind/smb/src/Exception/Exception.php', + 'Icewind\\SMB\\Exception\\FileInUseException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/FileInUseException.php', + 'Icewind\\SMB\\Exception\\ForbiddenException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/ForbiddenException.php', + 'Icewind\\SMB\\Exception\\HostDownException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/HostDownException.php', + 'Icewind\\SMB\\Exception\\InvalidHostException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/InvalidHostException.php', + 'Icewind\\SMB\\Exception\\InvalidPathException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/InvalidPathException.php', + 'Icewind\\SMB\\Exception\\InvalidRequestException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/InvalidRequestException.php', + 'Icewind\\SMB\\Exception\\InvalidResourceException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/InvalidResourceException.php', + 'Icewind\\SMB\\Exception\\InvalidTypeException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/InvalidTypeException.php', + 'Icewind\\SMB\\Exception\\NoLoginServerException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/NoLoginServerException.php', + 'Icewind\\SMB\\Exception\\NoRouteToHostException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/NoRouteToHostException.php', + 'Icewind\\SMB\\Exception\\NotEmptyException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/NotEmptyException.php', + 'Icewind\\SMB\\Exception\\NotFoundException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/NotFoundException.php', + 'Icewind\\SMB\\Exception\\TimedOutException' => __DIR__ . '/..' . '/icewind/smb/src/Exception/TimedOutException.php', + 'Icewind\\SMB\\FileInfo' => __DIR__ . '/..' . '/icewind/smb/src/FileInfo.php', + 'Icewind\\SMB\\IFileInfo' => __DIR__ . '/..' . '/icewind/smb/src/IFileInfo.php', + 'Icewind\\SMB\\INotifyHandler' => __DIR__ . '/..' . '/icewind/smb/src/INotifyHandler.php', + 'Icewind\\SMB\\IShare' => __DIR__ . '/..' . '/icewind/smb/src/IShare.php', + 'Icewind\\SMB\\NativeFileInfo' => __DIR__ . '/..' . '/icewind/smb/src/NativeFileInfo.php', + 'Icewind\\SMB\\NativeServer' => __DIR__ . '/..' . '/icewind/smb/src/NativeServer.php', + 'Icewind\\SMB\\NativeShare' => __DIR__ . '/..' . '/icewind/smb/src/NativeShare.php', + 'Icewind\\SMB\\NativeState' => __DIR__ . '/..' . '/icewind/smb/src/NativeState.php', + 'Icewind\\SMB\\NativeStream' => __DIR__ . '/..' . '/icewind/smb/src/NativeStream.php', + 'Icewind\\SMB\\NotifyHandler' => __DIR__ . '/..' . '/icewind/smb/src/NotifyHandler.php', + 'Icewind\\SMB\\Parser' => __DIR__ . '/..' . '/icewind/smb/src/Parser.php', + 'Icewind\\SMB\\RawConnection' => __DIR__ . '/..' . '/icewind/smb/src/RawConnection.php', + 'Icewind\\SMB\\Server' => __DIR__ . '/..' . '/icewind/smb/src/Server.php', + 'Icewind\\SMB\\Share' => __DIR__ . '/..' . '/icewind/smb/src/Share.php', + 'Icewind\\SMB\\System' => __DIR__ . '/..' . '/icewind/smb/src/System.php', + 'Icewind\\SMB\\Test\\AbstractShare' => __DIR__ . '/..' . '/icewind/smb/tests/AbstractShare.php', + 'Icewind\\SMB\\Test\\NativeShare' => __DIR__ . '/..' . '/icewind/smb/tests/NativeShare.php', + 'Icewind\\SMB\\Test\\NativeStream' => __DIR__ . '/..' . '/icewind/smb/tests/NativeStream.php', + 'Icewind\\SMB\\Test\\NotifyHandlerTest' => __DIR__ . '/..' . '/icewind/smb/tests/NotifyHandlerTest.php', + 'Icewind\\SMB\\Test\\Parser' => __DIR__ . '/..' . '/icewind/smb/tests/Parser.php', + 'Icewind\\SMB\\Test\\Server' => __DIR__ . '/..' . '/icewind/smb/tests/Server.php', + 'Icewind\\SMB\\Test\\Share' => __DIR__ . '/..' . '/icewind/smb/tests/Share.php', + 'Icewind\\SMB\\Test\\TestCase' => __DIR__ . '/..' . '/icewind/smb/tests/TestCase.php', + 'Icewind\\SMB\\TimeZoneProvider' => __DIR__ . '/..' . '/icewind/smb/src/TimeZoneProvider.php', + 'Icewind\\Streams\\CallbackWrapper' => __DIR__ . '/..' . '/icewind/streams/src/CallbackWrapper.php', + 'Icewind\\Streams\\Directory' => __DIR__ . '/..' . '/icewind/streams/src/Directory.php', + 'Icewind\\Streams\\DirectoryFilter' => __DIR__ . '/..' . '/icewind/streams/src/DirectoryFilter.php', + 'Icewind\\Streams\\DirectoryWrapper' => __DIR__ . '/..' . '/icewind/streams/src/DirectoryWrapper.php', + 'Icewind\\Streams\\File' => __DIR__ . '/..' . '/icewind/streams/src/File.php', + 'Icewind\\Streams\\IteratorDirectory' => __DIR__ . '/..' . '/icewind/streams/src/IteratorDirectory.php', + 'Icewind\\Streams\\NullWrapper' => __DIR__ . '/..' . '/icewind/streams/src/NullWrapper.php', + 'Icewind\\Streams\\Path' => __DIR__ . '/..' . '/icewind/streams/src/Path.php', + 'Icewind\\Streams\\RetryWrapper' => __DIR__ . '/..' . '/icewind/streams/src/RetryWrapper.php', + 'Icewind\\Streams\\SeekableWrapper' => __DIR__ . '/..' . '/icewind/streams/src/SeekableWrapper.php', + 'Icewind\\Streams\\Url' => __DIR__ . '/..' . '/icewind/streams/src/Url.php', + 'Icewind\\Streams\\UrlCallback' => __DIR__ . '/..' . '/icewind/streams/src/UrlCallBack.php', + 'Icewind\\Streams\\Wrapper' => __DIR__ . '/..' . '/icewind/streams/src/Wrapper.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit98fe9b281934250b3a93f69a5ce843b3::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit98fe9b281934250b3a93f69a5ce843b3::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit98fe9b281934250b3a93f69a5ce843b3::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/apps/files_external/3rdparty/composer/installed.json b/apps/files_external/3rdparty/composer/installed.json index 127d2bd3a19..bc71c6d0458 100644 --- a/apps/files_external/3rdparty/composer/installed.json +++ b/apps/files_external/3rdparty/composer/installed.json @@ -1,34 +1,33 @@ [ { - "name": "icewind/smb", - "version": "v1.1.0", - "version_normalized": "1.1.0.0", + "name": "icewind/streams", + "version": "0.4.1", + "version_normalized": "0.4.1.0", "source": { "type": "git", - "url": "https://github.com/icewind1991/SMB.git", - "reference": "822f924967c68228555cea84ea44765f8e85c601" + "url": "https://github.com/icewind1991/Streams.git", + "reference": "d3620e8dc410c86c2ba55579803679c4e0b289ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/icewind1991/SMB/zipball/822f924967c68228555cea84ea44765f8e85c601", - "reference": "822f924967c68228555cea84ea44765f8e85c601", + "url": "https://api.github.com/repos/icewind1991/Streams/zipball/d3620e8dc410c86c2ba55579803679c4e0b289ac", + "reference": "d3620e8dc410c86c2ba55579803679c4e0b289ac", "shasum": "" }, "require": { - "icewind/streams": ">=0.2.0", "php": ">=5.3" }, "require-dev": { "phpunit/phpunit": "^4.8", "satooshi/php-coveralls": "v1.0.0" }, - "time": "2016-04-26 13:26:39", + "time": "2016-06-02 14:37:52", "type": "library", - "installation-source": "source", + "installation-source": "dist", "autoload": { "psr-4": { - "Icewind\\SMB\\": "src/", - "Icewind\\SMB\\Test\\": "tests/" + "Icewind\\Streams\\Tests\\": "tests/", + "Icewind\\Streams\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -41,37 +40,37 @@ "email": "icewind@owncloud.com" } ], - "description": "php wrapper for smbclient and libsmbclient-php" + "description": "A set of generic stream wrappers" }, { - "name": "icewind/streams", - "version": "0.4.1", - "version_normalized": "0.4.1.0", + "name": "icewind/smb", + "version": "v2.0.0", + "version_normalized": "2.0.0.0", "source": { "type": "git", - "url": "https://github.com/icewind1991/Streams.git", - "reference": "d3620e8dc410c86c2ba55579803679c4e0b289ac" + "url": "https://github.com/icewind1991/SMB.git", + "reference": "95a5ecbaf92617f9800ad7d6070ef31d8ff28c3a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/icewind1991/Streams/zipball/d3620e8dc410c86c2ba55579803679c4e0b289ac", - "reference": "d3620e8dc410c86c2ba55579803679c4e0b289ac", + "url": "https://api.github.com/repos/icewind1991/SMB/zipball/95a5ecbaf92617f9800ad7d6070ef31d8ff28c3a", + "reference": "95a5ecbaf92617f9800ad7d6070ef31d8ff28c3a", "shasum": "" }, "require": { - "php": ">=5.3" + "icewind/streams": ">=0.2.0", + "php": ">=5.4" }, "require-dev": { - "phpunit/phpunit": "^4.8", - "satooshi/php-coveralls": "v1.0.0" + "phpunit/phpunit": "^4.8" }, - "time": "2016-06-02 14:37:52", + "time": "2016-12-13 13:56:55", "type": "library", - "installation-source": "dist", + "installation-source": "source", "autoload": { "psr-4": { - "Icewind\\Streams\\Tests\\": "tests/", - "Icewind\\Streams\\": "src/" + "Icewind\\SMB\\": "src/", + "Icewind\\SMB\\Test\\": "tests/" } }, "notification-url": "https://packagist.org/downloads/", @@ -84,6 +83,6 @@ "email": "icewind@owncloud.com" } ], - "description": "A set of generic stream wrappers" + "description": "php wrapper for smbclient and libsmbclient-php" } ] diff --git a/apps/files_external/3rdparty/icewind/smb/README.md b/apps/files_external/3rdparty/icewind/smb/README.md index 32f3c650f87..c8f31193c83 100644 --- a/apps/files_external/3rdparty/icewind/smb/README.md +++ b/apps/files_external/3rdparty/icewind/smb/README.md @@ -1,7 +1,7 @@ SMB === -[![Coverage Status](https://img.shields.io/coveralls/icewind1991/SMB.svg)](https://coveralls.io/r/icewind1991/SMB?branch=master) +[![Code Coverage](https://scrutinizer-ci.com/g/icewind1991/SMB/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/icewind1991/SMB/?branch=master) [![Build Status](https://travis-ci.org/icewind1991/SMB.svg?branch=master)](https://travis-ci.org/icewind1991/SMB) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/icewind1991/SMB/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/icewind1991/SMB/?branch=master) @@ -134,3 +134,19 @@ if (Server::NativeAvailable()) { $share = $server->getShare('test'); $share->put($fileToUpload, 'example.txt'); ``` + +### Using notify + +```php +<?php +use Icewind\SMB\Server; + +require('vendor/autoload.php'); + +$server = new Server('localhost', 'test', 'test'); +$share = $server->getShare('test'); + +$share->notify('')->listen(function (\Icewind\SMB\Change $change) { + echo $change->getCode() . ': ' . $change->getPath() . "\n"; +}); +``` diff --git a/apps/files_external/3rdparty/icewind/smb/composer.json b/apps/files_external/3rdparty/icewind/smb/composer.json index 4ac8b27e725..8374f564bf1 100644 --- a/apps/files_external/3rdparty/icewind/smb/composer.json +++ b/apps/files_external/3rdparty/icewind/smb/composer.json @@ -9,11 +9,10 @@ } ], "require" : { - "php": ">=5.3", + "php": ">=5.4", "icewind/streams": ">=0.2.0" }, "require-dev": { - "satooshi/php-coveralls" : "v1.0.0", "phpunit/phpunit": "^4.8" }, "autoload" : { diff --git a/apps/files_external/3rdparty/icewind/smb/src/Connection.php b/apps/files_external/3rdparty/icewind/smb/src/Connection.php index d24cdc1f6d0..c1ebb861711 100644 --- a/apps/files_external/3rdparty/icewind/smb/src/Connection.php +++ b/apps/files_external/3rdparty/icewind/smb/src/Connection.php @@ -17,6 +17,14 @@ class Connection extends RawConnection { const DELIMITER = 'smb:'; const DELIMITER_LENGTH = 4; + /** @var Parser */ + private $parser; + + public function __construct($command, Parser $parser, $env = array()) { + parent::__construct($command, $env); + $this->parser = $parser; + } + /** * send input to smbclient * @@ -30,7 +38,7 @@ class Connection extends RawConnection { * get all unprocessed output from smbclient until the next prompt * * @param callable $callback (optional) callback to call for every line read - * @return string + * @return string[] * @throws AuthenticationException * @throws ConnectException * @throws ConnectionException @@ -42,7 +50,7 @@ class Connection extends RawConnection { throw new ConnectionException('Connection not valid'); } $promptLine = $this->readLine(); //first line is prompt - $this->checkConnectionError($promptLine); + $this->parser->checkConnectionError($promptLine); $output = array(); $line = $this->readLine(); @@ -54,6 +62,7 @@ class Connection extends RawConnection { $result = $callback($line); if ($result === false) { // allow the callback to close the connection for infinite running commands $this->close(true); + break; } } else { $output[] .= $line; @@ -90,33 +99,6 @@ class Connection extends RawConnection { } } - /** - * check if the first line holds a connection failure - * - * @param $line - * @throws AuthenticationException - * @throws InvalidHostException - * @throws NoLoginServerException - */ - private function checkConnectionError($line) { - $line = rtrim($line, ')'); - if (substr($line, -23) === ErrorCodes::LogonFailure) { - throw new AuthenticationException('Invalid login'); - } - if (substr($line, -26) === ErrorCodes::BadHostName) { - throw new InvalidHostException('Invalid hostname'); - } - if (substr($line, -22) === ErrorCodes::Unsuccessful) { - throw new InvalidHostException('Connection unsuccessful'); - } - if (substr($line, -28) === ErrorCodes::ConnectionRefused) { - throw new InvalidHostException('Connection refused'); - } - if (substr($line, -26) === ErrorCodes::NoLogonServers) { - throw new NoLoginServerException('No login server'); - } - } - public function close($terminate = true) { if (is_resource($this->getInputStream())) { $this->write('close' . PHP_EOL); diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/Exception.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/Exception.php index 3307ad57a24..7ac528198a9 100644 --- a/apps/files_external/3rdparty/icewind/smb/src/Exception/Exception.php +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/Exception.php @@ -7,4 +7,32 @@ namespace Icewind\SMB\Exception; -class Exception extends \Exception {} +class Exception extends \Exception { + static public function unknown($path, $error) { + $message = 'Unknown error (' . $error . ')'; + if ($path) { + $message .= ' for ' . $path; + } + + return new Exception($message, $error); + } + + /** + * @param array $exceptionMap + * @param mixed $error + * @param string $path + * @return Exception + */ + static public function fromMap(array $exceptionMap, $error, $path) { + if (isset($exceptionMap[$error])) { + $exceptionClass = $exceptionMap[$error]; + if (is_numeric($error)) { + return new $exceptionClass($path, $error); + } else { + return new $exceptionClass($path); + } + } else { + return Exception::unknown($path, $error); + } + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/IShare.php b/apps/files_external/3rdparty/icewind/smb/src/IShare.php index 40423151332..8f6d5058b81 100644 --- a/apps/files_external/3rdparty/icewind/smb/src/IShare.php +++ b/apps/files_external/3rdparty/icewind/smb/src/IShare.php @@ -8,17 +8,6 @@ namespace Icewind\SMB; interface IShare { - // https://msdn.microsoft.com/en-us/library/dn392331.aspx - const NOTIFY_ADDED = 1; - const NOTIFY_REMOVED = 2; - const NOTIFY_MODIFIED = 3; - const NOTIFY_RENAMED_OLD = 4; - const NOTIFY_RENAMED_NEW = 5; - const NOTIFY_ADDED_STREAM = 6; - const NOTIFY_REMOVED_STREAM = 7; - const NOTIFY_MODIFIED_STREAM = 8; - const NOTIFY_REMOVED_BY_DELETE = 9; - /** * Get the name of the share * @@ -145,8 +134,7 @@ interface IShare { /** * @param string $path - * @param callable $callback callable which will be called for each received change - * @return mixed + * @return INotifyHandler */ - public function notify($path, callable $callback); + public function notify($path); } diff --git a/apps/files_external/3rdparty/icewind/smb/src/NativeFileInfo.php b/apps/files_external/3rdparty/icewind/smb/src/NativeFileInfo.php index 6ef5cf0c5b9..6cb070196a6 100644 --- a/apps/files_external/3rdparty/icewind/smb/src/NativeFileInfo.php +++ b/apps/files_external/3rdparty/icewind/smb/src/NativeFileInfo.php @@ -26,7 +26,7 @@ class NativeFileInfo implements IFileInfo { protected $share; /** - * @var array | null + * @var array|null */ protected $statCache; @@ -66,7 +66,7 @@ class NativeFileInfo implements IFileInfo { * @return array */ protected function stat() { - if (!$this->statCache) { + if (is_null($this->statCache)) { $this->statCache = $this->share->getStat($this->getPath()); } return $this->statCache; diff --git a/apps/files_external/3rdparty/icewind/smb/src/NativeShare.php b/apps/files_external/3rdparty/icewind/smb/src/NativeShare.php index 51e16d1841f..7efa34df370 100644 --- a/apps/files_external/3rdparty/icewind/smb/src/NativeShare.php +++ b/apps/files_external/3rdparty/icewind/smb/src/NativeShare.php @@ -60,6 +60,7 @@ class NativeShare extends AbstractShare { } private function buildUrl($path) { + $this->connect(); $this->verifyPath($path); $url = sprintf('smb://%s/%s', $this->server->getHost(), $this->name); if ($path) { @@ -80,7 +81,6 @@ class NativeShare extends AbstractShare { * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function dir($path) { - $this->connect(); $files = array(); $dh = $this->state->opendir($this->buildUrl($path)); @@ -97,14 +97,13 @@ class NativeShare extends AbstractShare { /** * @param string $path - * @return \Icewind\SMB\IFileInfo[] + * @return \Icewind\SMB\IFileInfo */ public function stat($path) { return new NativeFileInfo($this, $path, basename($path), $this->getStat($path)); } public function getStat($path) { - $this->connect(); return $this->state->stat($this->buildUrl($path)); } @@ -118,7 +117,6 @@ class NativeShare extends AbstractShare { * @throws \Icewind\SMB\Exception\AlreadyExistsException */ public function mkdir($path) { - $this->connect(); return $this->state->mkdir($this->buildUrl($path)); } @@ -132,7 +130,6 @@ class NativeShare extends AbstractShare { * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function rmdir($path) { - $this->connect(); return $this->state->rmdir($this->buildUrl($path)); } @@ -146,7 +143,6 @@ class NativeShare extends AbstractShare { * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function del($path) { - $this->connect(); return $this->state->unlink($this->buildUrl($path)); } @@ -161,7 +157,6 @@ class NativeShare extends AbstractShare { * @throws \Icewind\SMB\Exception\AlreadyExistsException */ public function rename($from, $to) { - $this->connect(); return $this->state->rename($this->buildUrl($from), $this->buildUrl($to)); } @@ -176,7 +171,6 @@ class NativeShare extends AbstractShare { * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function put($source, $target) { - $this->connect(); $sourceHandle = fopen($source, 'rb'); $targetHandle = $this->state->create($this->buildUrl($target)); @@ -214,7 +208,6 @@ class NativeShare extends AbstractShare { throw new InvalidResourceException('Failed opening local file "' . $target . '" for writing: ' . $reason); } - $this->connect(); $sourceHandle = $this->state->open($this->buildUrl($source), 'r'); if (!$sourceHandle) { fclose($targetHandle); @@ -238,7 +231,6 @@ class NativeShare extends AbstractShare { * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function read($source) { - $this->connect(); $url = $this->buildUrl($source); $handle = $this->state->open($url, 'r'); return NativeStream::wrap($this->state, $handle, 'r', $url); @@ -254,7 +246,6 @@ class NativeShare extends AbstractShare { * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function write($source) { - $this->connect(); $url = $this->buildUrl($source); $handle = $this->state->create($url); return NativeStream::wrap($this->state, $handle, 'w', $url); @@ -268,10 +259,7 @@ class NativeShare extends AbstractShare { * @return string the attribute value */ public function getAttribute($path, $attribute) { - $this->connect(); - - $result = $this->state->getxattr($this->buildUrl($path), $attribute); - return $result; + return $this->state->getxattr($this->buildUrl($path), $attribute); } /** @@ -283,7 +271,6 @@ class NativeShare extends AbstractShare { * @return string the attribute value */ public function setAttribute($path, $attribute, $value) { - $this->connect(); if ($attribute === 'system.dos_attr.mode' and is_int($value)) { $value = '0x' . dechex($value); @@ -303,14 +290,13 @@ class NativeShare extends AbstractShare { /** * @param string $path - * @param callable $callback callable which will be called for each received change - * @return mixed + * @return INotifyHandler */ - public function notify($path, callable $callback) { + public function notify($path) { // php-smbclient does support notify (https://github.com/eduardok/libsmbclient-php/issues/29) // so we use the smbclient based backend for this $share = new Share($this->server, $this->getName()); - $share->notify($path, $callback); + return $share->notify($path); } public function __destruct() { diff --git a/apps/files_external/3rdparty/icewind/smb/src/NativeState.php b/apps/files_external/3rdparty/icewind/smb/src/NativeState.php index e3e344d9e01..7ddce831853 100644 --- a/apps/files_external/3rdparty/icewind/smb/src/NativeState.php +++ b/apps/files_external/3rdparty/icewind/smb/src/NativeState.php @@ -7,16 +7,7 @@ namespace Icewind\SMB; -use Icewind\SMB\Exception\AlreadyExistsException; -use Icewind\SMB\Exception\ConnectionRefusedException; use Icewind\SMB\Exception\Exception; -use Icewind\SMB\Exception\ForbiddenException; -use Icewind\SMB\Exception\HostDownException; -use Icewind\SMB\Exception\InvalidTypeException; -use Icewind\SMB\Exception\NoRouteToHostException; -use Icewind\SMB\Exception\NotEmptyException; -use Icewind\SMB\Exception\NotFoundException; -use Icewind\SMB\Exception\TimedOutException; /** * Low level wrapper for libsmbclient-php for error handling @@ -31,44 +22,39 @@ class NativeState { protected $connected = false; + // todo replace with static once <5.6 support is dropped + // see error.h + private static $exceptionMap = [ + 1 => '\Icewind\SMB\Exception\ForbiddenException', + 2 => '\Icewind\SMB\Exception\NotFoundException', + 13 => '\Icewind\SMB\Exception\ForbiddenException', + 17 => '\Icewind\SMB\Exception\AlreadyExistsException', + 20 => '\Icewind\SMB\Exception\InvalidTypeException', + 21 => '\Icewind\SMB\Exception\InvalidTypeException', + 39 => '\Icewind\SMB\Exception\NotEmptyException', + 110 => '\Icewind\SMB\Exception\TimedOutException', + 111 => '\Icewind\SMB\Exception\ConnectionRefusedException', + 112 => '\Icewind\SMB\Exception\HostDownException', + 113 => '\Icewind\SMB\Exception\NoRouteToHostException' + ]; + protected function handleError($path) { $error = smbclient_state_errno($this->state); - switch ($error) { - // see error.h - case 0; - return; - case 1: - case 13: - throw new ForbiddenException($path, $error); - case 2: - throw new NotFoundException($path, $error); - case 17: - throw new AlreadyExistsException($path, $error); - case 20: - throw new InvalidTypeException($path, $error); - case 21: - throw new InvalidTypeException($path, $error); - case 39: - throw new NotEmptyException($path, $error); - case 110: - throw new TimedOutException($path, $error); - case 111: - throw new ConnectionRefusedException($path, $error); - case 112: - throw new HostDownException($path, $error); - case 113: - throw new NoRouteToHostException($path, $error); - default: - $message = 'Unknown error (' . $error . ')'; - if ($path) { - $message .= ' for ' . $path; - } - throw new Exception($message, $error); + if ($error === 0) { + return; } + throw Exception::fromMap(self::$exceptionMap, $error, $path); } - protected function testResult($result, $path) { + protected function testResult($result, $uri) { if ($result === false or $result === null) { + // smb://host/share/path + if (is_string($uri)) { + list(, , , , $path) = explode('/', $uri, 5); + $path = '/' . $path; + } else { + $path = null; + } $this->handleError($path); } } @@ -246,7 +232,7 @@ class NativeState { * @param resource $file * @param int $offset * @param int $whence SEEK_SET | SEEK_CUR | SEEK_END - * @return int | bool new file offset as measured from the start of the file on success, false on failure. + * @return int|bool new file offset as measured from the start of the file on success, false on failure. */ public function lseek($file, $offset, $whence = SEEK_SET) { $result = @smbclient_lseek($this->state, $file, $offset, $whence); diff --git a/apps/files_external/3rdparty/icewind/smb/src/Parser.php b/apps/files_external/3rdparty/icewind/smb/src/Parser.php index fc1d7f283a8..5cc5acbdf56 100644 --- a/apps/files_external/3rdparty/icewind/smb/src/Parser.php +++ b/apps/files_external/3rdparty/icewind/smb/src/Parser.php @@ -9,19 +9,38 @@ namespace Icewind\SMB; use Icewind\SMB\Exception\AccessDeniedException; use Icewind\SMB\Exception\AlreadyExistsException; +use Icewind\SMB\Exception\AuthenticationException; use Icewind\SMB\Exception\Exception; use Icewind\SMB\Exception\FileInUseException; +use Icewind\SMB\Exception\InvalidHostException; use Icewind\SMB\Exception\InvalidResourceException; use Icewind\SMB\Exception\InvalidTypeException; +use Icewind\SMB\Exception\NoLoginServerException; use Icewind\SMB\Exception\NotEmptyException; use Icewind\SMB\Exception\NotFoundException; class Parser { + const MSG_NOT_FOUND = 'Error opening local file '; + /** * @var \Icewind\SMB\TimeZoneProvider */ protected $timeZoneProvider; + // todo replace with static once <5.6 support is dropped + // see error.h + private static $exceptionMap = [ + ErrorCodes::PathNotFound => '\Icewind\SMB\Exception\NotFoundException', + ErrorCodes::ObjectNotFound => '\Icewind\SMB\Exception\NotFoundException', + ErrorCodes::NoSuchFile => '\Icewind\SMB\Exception\NotFoundException', + ErrorCodes::NameCollision => '\Icewind\SMB\Exception\AlreadyExistsException', + ErrorCodes::AccessDenied => '\Icewind\SMB\Exception\AccessDeniedException', + ErrorCodes::DirectoryNotEmpty => '\Icewind\SMB\Exception\NotEmptyException', + ErrorCodes::FileIsADirectory => '\Icewind\SMB\Exception\InvalidTypeException', + ErrorCodes::NotADirectory => '\Icewind\SMB\Exception\InvalidTypeException', + ErrorCodes::SharingViolation => '\Icewind\SMB\Exception\FileInUseException' + ]; + /** * @param \Icewind\SMB\TimeZoneProvider $timeZoneProvider */ @@ -29,50 +48,54 @@ class Parser { $this->timeZoneProvider = $timeZoneProvider; } - public function checkForError($output, $path) { - if (count($output) === 0) { - return true; - } else { - if (strpos($output[0], 'does not exist')) { - throw new NotFoundException($path); - } - $parts = explode(' ', $output[0]); - $error = false; - foreach ($parts as $part) { - if (substr($part, 0, 9) === 'NT_STATUS') { - $error = $part; - } + private function getErrorCode($line) { + $parts = explode(' ', $line); + foreach ($parts as $part) { + if (substr($part, 0, 9) === 'NT_STATUS') { + return $part; } + } + return false; + } - $notFoundMsg = 'Error opening local file '; - if (substr($output[0], 0, strlen($notFoundMsg)) === $notFoundMsg) { - $localPath = substr($output[0], strlen($notFoundMsg)); - throw new InvalidResourceException('Failed opening local file "' . $localPath . '" for writing'); - } + public function checkForError($output, $path) { + if (strpos($output[0], 'does not exist')) { + throw new NotFoundException($path); + } + $error = $this->getErrorCode($output[0]); - switch ($error) { - case ErrorCodes::PathNotFound: - case ErrorCodes::ObjectNotFound: - case ErrorCodes::NoSuchFile: - throw new NotFoundException($path); - case ErrorCodes::NameCollision: - throw new AlreadyExistsException($path); - case ErrorCodes::AccessDenied: - throw new AccessDeniedException($path); - case ErrorCodes::DirectoryNotEmpty: - throw new NotEmptyException($path); - case ErrorCodes::FileIsADirectory: - case ErrorCodes::NotADirectory: - throw new InvalidTypeException($path); - case ErrorCodes::SharingViolation: - throw new FileInUseException($path); - default: - $message = 'Unknown error (' . $error . ')'; - if ($path) { - $message .= ' for ' . $path; - } - throw new Exception($message); - } + if (substr($output[0], 0, strlen(self::MSG_NOT_FOUND)) === self::MSG_NOT_FOUND) { + $localPath = substr($output[0], strlen(self::MSG_NOT_FOUND)); + throw new InvalidResourceException('Failed opening local file "' . $localPath . '" for writing'); + } + + throw Exception::fromMap(self::$exceptionMap, $error, $path); + } + + /** + * check if the first line holds a connection failure + * + * @param $line + * @throws AuthenticationException + * @throws InvalidHostException + * @throws NoLoginServerException + */ + public function checkConnectionError($line) { + $line = rtrim($line, ')'); + if (substr($line, -23) === ErrorCodes::LogonFailure) { + throw new AuthenticationException('Invalid login'); + } + if (substr($line, -26) === ErrorCodes::BadHostName) { + throw new InvalidHostException('Invalid hostname'); + } + if (substr($line, -22) === ErrorCodes::Unsuccessful) { + throw new InvalidHostException('Connection unsuccessful'); + } + if (substr($line, -28) === ErrorCodes::ConnectionRefused) { + throw new InvalidHostException('Connection refused'); + } + if (substr($line, -26) === ErrorCodes::NoLogonServers) { + throw new NoLoginServerException('No login server'); } } @@ -95,9 +118,7 @@ class Parser { } public function parseStat($output) { - $mtime = 0; - $mode = 0; - $size = 0; + $data = []; foreach ($output as $line) { // A line = explode statement may not fill all array elements // properly. May happen when accessing non Windows Fileservers @@ -105,20 +126,13 @@ class Parser { $name = isset($words[0]) ? $words[0] : ''; $value = isset($words[1]) ? $words[1] : ''; $value = trim($value); - if ($name === 'write_time') { - $mtime = strtotime($value); - } else if ($name === 'attributes') { - $mode = hexdec(substr($value, 1, -1)); - } else if ($name === 'stream') { - list(, $size,) = explode(' ', $value); - $size = intval($size); - } + $data[$name] = $value; } - return array( - 'mtime' => $mtime, - 'mode' => $mode, - 'size' => $size - ); + return [ + 'mtime' => strtotime($data['write_time']), + 'mode' => hexdec(substr($data['attributes'], strpos($data['attributes'], '('), -1)), + 'size' => isset($data['stream']) ? intval(explode(' ', $data['stream'])[1]) : 0 + ]; } public function parseDir($output, $basePath) { @@ -139,4 +153,17 @@ class Parser { } return $content; } + + public function parseListShares($output) { + $shareNames = array(); + foreach ($output as $line) { + if (strpos($line, '|')) { + list($type, $name, $description) = explode('|', $line); + if (strtolower($type) === 'disk') { + $shareNames[$name] = $description; + } + } + } + return $shareNames; + } } diff --git a/apps/files_external/3rdparty/icewind/smb/src/RawConnection.php b/apps/files_external/3rdparty/icewind/smb/src/RawConnection.php index 88ab046ef38..e9349716430 100644 --- a/apps/files_external/3rdparty/icewind/smb/src/RawConnection.php +++ b/apps/files_external/3rdparty/icewind/smb/src/RawConnection.php @@ -88,7 +88,7 @@ class RawConnection { /** * read a line of output * - * @return string + * @return string|false */ public function readLine() { return stream_get_line($this->getOutputStream(), 4086, "\n"); diff --git a/apps/files_external/3rdparty/icewind/smb/src/Server.php b/apps/files_external/3rdparty/icewind/smb/src/Server.php index 25f17201397..12692eb4c6e 100644 --- a/apps/files_external/3rdparty/icewind/smb/src/Server.php +++ b/apps/files_external/3rdparty/icewind/smb/src/Server.php @@ -135,32 +135,11 @@ class Server { $connection = new RawConnection($command); $connection->writeAuthentication($this->getUser(), $this->getPassword()); $output = $connection->readAll(); + $parser = new Parser($this->timezoneProvider); - $line = $output[0]; + $parser->checkConnectionError($output[0]); - $line = rtrim($line, ')'); - if (substr($line, -23) === ErrorCodes::LogonFailure) { - throw new AuthenticationException(); - } - if (substr($line, -26) === ErrorCodes::BadHostName) { - throw new InvalidHostException(); - } - if (substr($line, -22) === ErrorCodes::Unsuccessful) { - throw new InvalidHostException(); - } - if (substr($line, -28) === ErrorCodes::ConnectionRefused) { - throw new InvalidHostException(); - } - - $shareNames = array(); - foreach ($output as $line) { - if (strpos($line, '|')) { - list($type, $name, $description) = explode('|', $line); - if (strtolower($type) === 'disk') { - $shareNames[$name] = $description; - } - } - } + $shareNames = $parser->parseListShares($output); $shares = array(); foreach ($shareNames as $name => $description) { @@ -174,7 +153,7 @@ class Server { * @return \Icewind\SMB\IShare */ public function getShare($name) { - return new Share($this, $name); + return new Share($this, $name, $this->system); } /** diff --git a/apps/files_external/3rdparty/icewind/smb/src/Share.php b/apps/files_external/3rdparty/icewind/smb/src/Share.php index 21f8fe5b139..ba8bbbbb8ca 100644 --- a/apps/files_external/3rdparty/icewind/smb/src/Share.php +++ b/apps/files_external/3rdparty/icewind/smb/src/Share.php @@ -8,6 +8,7 @@ namespace Icewind\SMB; use Icewind\SMB\Exception\ConnectionException; +use Icewind\SMB\Exception\DependencyException; use Icewind\SMB\Exception\FileInUseException; use Icewind\SMB\Exception\InvalidTypeException; use Icewind\SMB\Exception\NotFoundException; @@ -42,24 +43,30 @@ class Share extends AbstractShare { /** * @param Server $server * @param string $name + * @param System $system */ - public function __construct($server, $name) { + public function __construct($server, $name, System $system = null) { parent::__construct(); $this->server = $server; $this->name = $name; - $this->system = new System(); + $this->system = (!is_null($system)) ? $system : new System(); $this->parser = new Parser(new TimeZoneProvider($this->server->getHost(), $this->system)); } protected function getConnection() { $workgroupArgument = ($this->server->getWorkgroup()) ? ' -W ' . escapeshellarg($this->server->getWorkgroup()) : ''; - $command = sprintf('stdbuf -o0 %s %s --authentication-file=%s %s', + $smbClientPath = $this->system->getSmbclientPath(); + if (!$smbClientPath) { + throw new DependencyException('Can\'t find smbclient binary in path'); + } + $command = sprintf('%s%s %s --authentication-file=%s %s', + $this->system->hasStdBuf() ? 'stdbuf -o0 ' : '', $this->system->getSmbclientPath(), $workgroupArgument, System::getFD(3), escapeshellarg('//' . $this->server->getHost() . '/' . $this->name) ); - $connection = new Connection($command); + $connection = new Connection($command, $this->parser); $connection->writeAuthentication($this->server->getUser(), $this->server->getPassword()); if (!$connection->isValid()) { throw new ConnectionException(); @@ -97,8 +104,8 @@ class Share extends AbstractShare { } protected function simpleCommand($command, $path) { - $path = $this->escapePath($path); - $cmd = $command . ' ' . $path; + $escapedPath = $this->escapePath($path); + $cmd = $command . ' ' . $escapedPath; $output = $this->execute($cmd); return $this->parseOutput($output, $path); } @@ -125,7 +132,7 @@ class Share extends AbstractShare { /** * @param string $path - * @return \Icewind\SMB\IFileInfo[] + * @return \Icewind\SMB\IFileInfo */ public function stat($path) { $escapedPath = $this->escapePath($path); @@ -216,8 +223,7 @@ class Share extends AbstractShare { public function rename($from, $to) { $path1 = $this->escapePath($from); $path2 = $this->escapePath($to); - $cmd = 'rename ' . $path1 . ' ' . $path2; - $output = $this->execute($cmd); + $output = $this->execute('rename ' . $path1 . ' ' . $path2); return $this->parseOutput($output, $to); } @@ -268,15 +274,8 @@ class Share extends AbstractShare { $source = $this->escapePath($source); // since returned stream is closed by the caller we need to create a new instance // since we can't re-use the same file descriptor over multiple calls - $workgroupArgument = ($this->server->getWorkgroup()) ? ' -W ' . escapeshellarg($this->server->getWorkgroup()) : ''; - $command = sprintf('%s %s --authentication-file=%s %s', - $this->system->getSmbclientPath(), - $workgroupArgument, - System::getFD(3), - escapeshellarg('//' . $this->server->getHost() . '/' . $this->name) - ); - $connection = new Connection($command); - $connection->writeAuthentication($this->server->getUser(), $this->server->getPassword()); + $connection = $this->getConnection(); + $connection->write('get ' . $source . ' ' . System::getFD(5)); $connection->write('exit'); $fh = $connection->getFileOutputStream(); @@ -297,17 +296,9 @@ class Share extends AbstractShare { $target = $this->escapePath($target); // since returned stream is closed by the caller we need to create a new instance // since we can't re-use the same file descriptor over multiple calls - $workgroupArgument = ($this->server->getWorkgroup()) ? ' -W ' . escapeshellarg($this->server->getWorkgroup()) : ''; - $command = sprintf('%s %s --authentication-file=%s %s', - $this->system->getSmbclientPath(), - $workgroupArgument, - System::getFD(3), - escapeshellarg('//' . $this->server->getHost() . '/' . $this->name) - ); - $connection = new Connection($command); - $connection->writeAuthentication($this->server->getUser(), $this->server->getPassword()); - $fh = $connection->getFileInputStream(); + $connection = $this->getConnection(); + $fh = $connection->getFileInputStream(); $connection->write('put ' . System::getFD(4) . ' ' . $target); $connection->write('exit'); @@ -343,30 +334,30 @@ class Share extends AbstractShare { $output = $this->execute($cmd); $this->parseOutput($output, $path); - // then set the modes we want - $cmd = 'setmode ' . $path . ' ' . $modeString; - $output = $this->execute($cmd); - return $this->parseOutput($output, $path); + if ($mode !== FileInfo::MODE_NORMAL) { + // then set the modes we want + $cmd = 'setmode ' . $path . ' ' . $modeString; + $output = $this->execute($cmd); + return $this->parseOutput($output, $path); + } else { + return true; + } } /** * @param string $path - * @param callable $callback callable which will be called for each received change - * @return mixed + * @return INotifyHandler + * @throws ConnectionException + * @throws DependencyException */ - public function notify($path, callable $callback) { + public function notify($path) { + if (!$this->system->hasStdBuf()) { //stdbuf is required to disable smbclient's output buffering + throw new DependencyException('stdbuf is required for usage of the notify command'); + } $connection = $this->getConnection(); // use a fresh connection since the notify command blocks the process $command = 'notify ' . $this->escapePath($path); $connection->write($command . PHP_EOL); - $connection->read(function ($line) use ($callback, $path) { - $code = (int)substr($line, 0, 4); - $subPath = substr($line, 5); - if ($path === '') { - return $callback($code, $subPath); - } else { - return $callback($code, $path . '/' . $subPath); - } - }); + return new NotifyHandler($connection, $path); } /** @@ -395,7 +386,12 @@ class Share extends AbstractShare { * @return bool */ protected function parseOutput($lines, $path = '') { - return $this->parser->checkForError($lines, $path); + if (count($lines) === 0) { + return true; + } else { + $this->parser->checkForError($lines, $path); + return false; + } } /** diff --git a/apps/files_external/3rdparty/icewind/smb/src/System.php b/apps/files_external/3rdparty/icewind/smb/src/System.php index 192a0b3877d..7c519988aa5 100644 --- a/apps/files_external/3rdparty/icewind/smb/src/System.php +++ b/apps/files_external/3rdparty/icewind/smb/src/System.php @@ -14,6 +14,8 @@ class System { private $net; + private $stdbuf; + public static function getFD($num) { $folders = array( '/proc/self/fd', @@ -40,4 +42,14 @@ class System { } return $this->net; } + + public function hasStdBuf() { + if (!$this->stdbuf) { + $result = null; + $output = array(); + exec('which stdbuf 2>&1', $output, $result); + $this->stdbuf = $result === 0; + } + return $this->stdbuf; + } } diff --git a/apps/files_external/3rdparty/icewind/smb/src/TimeZoneProvider.php b/apps/files_external/3rdparty/icewind/smb/src/TimeZoneProvider.php index 86d7859a6f7..fcdf7e3e879 100644 --- a/apps/files_external/3rdparty/icewind/smb/src/TimeZoneProvider.php +++ b/apps/files_external/3rdparty/icewind/smb/src/TimeZoneProvider.php @@ -27,7 +27,7 @@ class TimeZoneProvider { * @param string $host * @param System $system */ - function __construct($host, System $system) { + public function __construct($host, System $system) { $this->host = $host; $this->system = $system; } diff --git a/apps/files_external/l10n/sv.js b/apps/files_external/l10n/sv.js index d2a83012c74..0f0ed79c9a6 100644 --- a/apps/files_external/l10n/sv.js +++ b/apps/files_external/l10n/sv.js @@ -2,6 +2,7 @@ OC.L10N.register( "files_external", { "Fetching request tokens failed. Verify that your app key and secret are correct." : "Fel vid hämtning av åtkomst-token. Verifiera att din app-nyckel och hemlighet stämmer.", + "Fetching access tokens failed. Verify that your app key and secret are correct." : "Kontroll av behörigheter misslyckades. Verifiera att ditt app-lösenord och hemlighet är korrekt.", "Please provide a valid app key and secret." : "Vänligen ange en giltig applikationsnyckel och hemlig fras.", "Step 1 failed. Exception: %s" : "Steg 1 flaerade. Undantag: %s", "Step 2 failed. Exception: %s" : "Steg 2 falerade. Undantag: %s", @@ -11,10 +12,13 @@ OC.L10N.register( "Personal" : "Privat", "System" : "System", "Grant access" : "Bevilja åtkomst", + "Error configuring OAuth1" : "Misslyckades konfigurera OAuth1", + "Error configuring OAuth2" : "Misslyckades konfigurera OAuth2", "Generate keys" : "Generera nycklar", "Error generating key pair" : "Fel vid generering av nyckelpar", "All users. Type to select user or group." : "Alla användare. Skriv för att välja användare eller grupp.", "(group)" : "(grupp)", + "Compatibility with Mac NFD encoding (slow)" : "Kompatibilitet med Mac NFD kodning (slö)", "Admin defined" : "Admin definerad", "Saved" : "Sparad", "Saving..." : "Sparar...", @@ -26,6 +30,8 @@ OC.L10N.register( "There was an error with message: " : "Det fanns ett fel med meddelande:", "External mount error" : "Fel vid extern montering", "external-storage" : "extern-lagring", + "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Kunde inte hitta listan med Windows nätverksdiskar: tomt svar från servern", + "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Några av de konfigurerade externa monteringspunkter är inte anslutna. Klicka på den röda rad(er) för mer information.", "Please enter the credentials for the {mount} mount" : "Vänligen ange uppgifterna för {mount} montering", "Username" : "Användarnamn", "Password" : "Lösenord", @@ -33,7 +39,14 @@ OC.L10N.register( "Credentials saving failed" : "Misslyckades med att spara uppgifterna", "Credentials required" : "Uppgifter krävs", "Storage with id \"%i\" not found" : "Lagring med id \"%i\" kan ej hittas", + "Invalid backend or authentication mechanism class" : "Ogiltig backend eller autentiseringsmekanism-klass", "Invalid mount point" : "Ogiltig monteringspunkt", + "Objectstore forbidden" : "Objekt förbjudet", + "Invalid storage backend \"%s\"" : "Ogiltig lagrings backend \"%s\"", + "Not permitted to use backend \"%s\"" : "Inte tillåtet att använda backend \"%s\"", + "Not permitted to use authentication mechanism \"%s\"" : "Inte tillåtet att använda autentiseringsmekanism \"%s\"", + "Unsatisfied backend parameters" : "Otillfredsställda backend parametrar", + "Unsatisfied authentication mechanism parameters" : "Otillfredsställda autentiseringsmekanism parametrar", "Insufficient data: %s" : "Otillräcklig data: %s", "%s" : "%s", "Storage with id \"%i\" is not user editable" : "Lagring med id \"%i\" är inte redigerbar av användare", @@ -49,13 +62,16 @@ OC.L10N.register( "Client secret" : "Klienthemlighet", "OpenStack" : "OpenStack", "Tenant name" : "Namn på arrendator", + "Identity endpoint URL" : "Identitet endpoint URL", + "Rackspace" : "Rackutrymme", "API key" : "API-nyckel", "Global credentials" : "Globala uppgifter", "Log-in credentials, save in database" : "Inloggningsuppgifter sparade i databasen", "Username and password" : "Användarnamn och lösenord", "Log-in credentials, save in session" : "Inloggninguppgifter, spara i sessionen", - "RSA public key" : "RSA publik nyckel", - "Public key" : "Publik nyckel", + "User entered, store in database" : "Användare lades till, lagras i databasen", + "RSA public key" : "RSA offentlig nyckel", + "Public key" : "Offentlig nyckel", "Amazon S3" : "Amazon S3", "Bucket" : "Bucket", "Hostname" : "Värdnamn", @@ -65,7 +81,7 @@ OC.L10N.register( "Enable Path Style" : "Aktivera Path Style", "WebDAV" : "WebDAV", "URL" : "URL", - "Remote subfolder" : "Fjärrmapp", + "Remote subfolder" : "Extern mapp", "Secure https://" : "Säker https://", "Dropbox" : "Dropbox", "FTP" : "FTP", @@ -87,6 +103,9 @@ OC.L10N.register( "Service name" : "Namn på tjänst", "Request timeout (seconds)" : "Sekunder för anslutningsförsök", "External storages" : "Extern Lagring", + "The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." : "cURL-stöd i PHP är inte aktiverat eller har inte installerats. Montering av %s är inte möjlig. Be din systemadministratör om installation.", + "The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." : "FTP-stöd i PHP är inte aktiverat eller har inte installerats. Montering av %s är inte möjlig. Be din systemadministratör om installation.", + "\"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." : "\"%s\" är inte installerad. Montering av %s är inte möjlig. Be din systemadministratör om installation.", "No external storage configured" : "Ingen extern lagring konfigurerad", "You can add external storages in the personal settings" : "Du kan lägga till externa lagringar i de personliga inställningarna", "Name" : "Namn", diff --git a/apps/files_external/l10n/sv.json b/apps/files_external/l10n/sv.json index 910458460c8..f9693a98585 100644 --- a/apps/files_external/l10n/sv.json +++ b/apps/files_external/l10n/sv.json @@ -1,5 +1,6 @@ { "translations": { "Fetching request tokens failed. Verify that your app key and secret are correct." : "Fel vid hämtning av åtkomst-token. Verifiera att din app-nyckel och hemlighet stämmer.", + "Fetching access tokens failed. Verify that your app key and secret are correct." : "Kontroll av behörigheter misslyckades. Verifiera att ditt app-lösenord och hemlighet är korrekt.", "Please provide a valid app key and secret." : "Vänligen ange en giltig applikationsnyckel och hemlig fras.", "Step 1 failed. Exception: %s" : "Steg 1 flaerade. Undantag: %s", "Step 2 failed. Exception: %s" : "Steg 2 falerade. Undantag: %s", @@ -9,10 +10,13 @@ "Personal" : "Privat", "System" : "System", "Grant access" : "Bevilja åtkomst", + "Error configuring OAuth1" : "Misslyckades konfigurera OAuth1", + "Error configuring OAuth2" : "Misslyckades konfigurera OAuth2", "Generate keys" : "Generera nycklar", "Error generating key pair" : "Fel vid generering av nyckelpar", "All users. Type to select user or group." : "Alla användare. Skriv för att välja användare eller grupp.", "(group)" : "(grupp)", + "Compatibility with Mac NFD encoding (slow)" : "Kompatibilitet med Mac NFD kodning (slö)", "Admin defined" : "Admin definerad", "Saved" : "Sparad", "Saving..." : "Sparar...", @@ -24,6 +28,8 @@ "There was an error with message: " : "Det fanns ett fel med meddelande:", "External mount error" : "Fel vid extern montering", "external-storage" : "extern-lagring", + "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Kunde inte hitta listan med Windows nätverksdiskar: tomt svar från servern", + "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Några av de konfigurerade externa monteringspunkter är inte anslutna. Klicka på den röda rad(er) för mer information.", "Please enter the credentials for the {mount} mount" : "Vänligen ange uppgifterna för {mount} montering", "Username" : "Användarnamn", "Password" : "Lösenord", @@ -31,7 +37,14 @@ "Credentials saving failed" : "Misslyckades med att spara uppgifterna", "Credentials required" : "Uppgifter krävs", "Storage with id \"%i\" not found" : "Lagring med id \"%i\" kan ej hittas", + "Invalid backend or authentication mechanism class" : "Ogiltig backend eller autentiseringsmekanism-klass", "Invalid mount point" : "Ogiltig monteringspunkt", + "Objectstore forbidden" : "Objekt förbjudet", + "Invalid storage backend \"%s\"" : "Ogiltig lagrings backend \"%s\"", + "Not permitted to use backend \"%s\"" : "Inte tillåtet att använda backend \"%s\"", + "Not permitted to use authentication mechanism \"%s\"" : "Inte tillåtet att använda autentiseringsmekanism \"%s\"", + "Unsatisfied backend parameters" : "Otillfredsställda backend parametrar", + "Unsatisfied authentication mechanism parameters" : "Otillfredsställda autentiseringsmekanism parametrar", "Insufficient data: %s" : "Otillräcklig data: %s", "%s" : "%s", "Storage with id \"%i\" is not user editable" : "Lagring med id \"%i\" är inte redigerbar av användare", @@ -47,13 +60,16 @@ "Client secret" : "Klienthemlighet", "OpenStack" : "OpenStack", "Tenant name" : "Namn på arrendator", + "Identity endpoint URL" : "Identitet endpoint URL", + "Rackspace" : "Rackutrymme", "API key" : "API-nyckel", "Global credentials" : "Globala uppgifter", "Log-in credentials, save in database" : "Inloggningsuppgifter sparade i databasen", "Username and password" : "Användarnamn och lösenord", "Log-in credentials, save in session" : "Inloggninguppgifter, spara i sessionen", - "RSA public key" : "RSA publik nyckel", - "Public key" : "Publik nyckel", + "User entered, store in database" : "Användare lades till, lagras i databasen", + "RSA public key" : "RSA offentlig nyckel", + "Public key" : "Offentlig nyckel", "Amazon S3" : "Amazon S3", "Bucket" : "Bucket", "Hostname" : "Värdnamn", @@ -63,7 +79,7 @@ "Enable Path Style" : "Aktivera Path Style", "WebDAV" : "WebDAV", "URL" : "URL", - "Remote subfolder" : "Fjärrmapp", + "Remote subfolder" : "Extern mapp", "Secure https://" : "Säker https://", "Dropbox" : "Dropbox", "FTP" : "FTP", @@ -85,6 +101,9 @@ "Service name" : "Namn på tjänst", "Request timeout (seconds)" : "Sekunder för anslutningsförsök", "External storages" : "Extern Lagring", + "The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." : "cURL-stöd i PHP är inte aktiverat eller har inte installerats. Montering av %s är inte möjlig. Be din systemadministratör om installation.", + "The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." : "FTP-stöd i PHP är inte aktiverat eller har inte installerats. Montering av %s är inte möjlig. Be din systemadministratör om installation.", + "\"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." : "\"%s\" är inte installerad. Montering av %s är inte möjlig. Be din systemadministratör om installation.", "No external storage configured" : "Ingen extern lagring konfigurerad", "You can add external storages in the personal settings" : "Du kan lägga till externa lagringar i de personliga inställningarna", "Name" : "Namn", diff --git a/apps/files_external/lib/Lib/Storage/AmazonS3.php b/apps/files_external/lib/Lib/Storage/AmazonS3.php index e6e26e3547a..9dab25f7197 100644 --- a/apps/files_external/lib/Lib/Storage/AmazonS3.php +++ b/apps/files_external/lib/Lib/Storage/AmazonS3.php @@ -42,6 +42,7 @@ require_once 'aws-autoloader.php'; use Aws\S3\S3Client; use Aws\S3\Exception\S3Exception; +use Icewind\Streams\CallbackWrapper; use Icewind\Streams\IteratorDirectory; use OC\Files\ObjectStore\S3ConnectionTrait; @@ -366,14 +367,15 @@ class AmazonS3 extends \OC\Files\Storage\Common { $ext = ''; } $tmpFile = \OCP\Files::tmpFile($ext); - \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); if ($this->file_exists($path)) { $source = $this->fopen($path, 'r'); file_put_contents($tmpFile, $source); } - self::$tmpFiles[$tmpFile] = $path; - return fopen('close://' . $tmpFile, $mode); + $handle = fopen($tmpFile, $mode); + return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) { + $this->writeBack($tmpFile, $path); + }); } return false; } @@ -514,15 +516,11 @@ class AmazonS3 extends \OC\Files\Storage\Common { return $this->id; } - public function writeBack($tmpFile) { - if (!isset(self::$tmpFiles[$tmpFile])) { - return false; - } - + public function writeBack($tmpFile, $path) { try { $this->getConnection()->putObject(array( 'Bucket' => $this->bucket, - 'Key' => $this->cleanKey(self::$tmpFiles[$tmpFile]), + 'Key' => $this->cleanKey($path), 'SourceFile' => $tmpFile, 'ContentType' => \OC::$server->getMimeTypeDetector()->detect($tmpFile), 'ContentLength' => filesize($tmpFile) diff --git a/apps/files_external/lib/Lib/Storage/Dropbox.php b/apps/files_external/lib/Lib/Storage/Dropbox.php index 45bc6cd0e98..d2ba1cca751 100644 --- a/apps/files_external/lib/Lib/Storage/Dropbox.php +++ b/apps/files_external/lib/Lib/Storage/Dropbox.php @@ -31,6 +31,7 @@ namespace OCA\Files_External\Lib\Storage; use GuzzleHttp\Exception\RequestException; +use Icewind\Streams\CallbackWrapper; use Icewind\Streams\IteratorDirectory; use Icewind\Streams\RetryWrapper; use OCP\Files\StorageNotAvailableException; @@ -45,8 +46,6 @@ class Dropbox extends \OC\Files\Storage\Common { private $metaData = array(); private $oauth; - private static $tempFiles = array(); - public function __construct($params) { if (isset($params['configured']) && $params['configured'] == 'true' && isset($params['app_key']) @@ -305,27 +304,26 @@ class Dropbox extends \OC\Files\Storage\Common { $ext = ''; } $tmpFile = \OCP\Files::tmpFile($ext); - \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); if ($this->file_exists($path)) { $source = $this->fopen($path, 'r'); file_put_contents($tmpFile, $source); } - self::$tempFiles[$tmpFile] = $path; - return fopen('close://'.$tmpFile, $mode); + $handle = fopen($tmpFile, $mode); + return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) { + $this->writeBack($tmpFile, $path); + }); } return false; } - public function writeBack($tmpFile) { - if (isset(self::$tempFiles[$tmpFile])) { - $handle = fopen($tmpFile, 'r'); - try { - $this->dropbox->putFile(self::$tempFiles[$tmpFile], $handle); - unlink($tmpFile); - $this->deleteMetaData(self::$tempFiles[$tmpFile]); - } catch (\Exception $exception) { - \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); - } + public function writeBack($tmpFile, $path) { + $handle = fopen($tmpFile, 'r'); + try { + $this->dropbox->putFile($path, $handle); + unlink($tmpFile); + $this->deleteMetaData($path); + } catch (\Exception $exception) { + \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); } } diff --git a/apps/files_external/lib/Lib/Storage/FTP.php b/apps/files_external/lib/Lib/Storage/FTP.php index 6f34416d111..22fe2090f30 100644 --- a/apps/files_external/lib/Lib/Storage/FTP.php +++ b/apps/files_external/lib/Lib/Storage/FTP.php @@ -33,6 +33,7 @@ namespace OCA\Files_External\Lib\Storage; +use Icewind\Streams\CallbackWrapper; use Icewind\Streams\RetryWrapper; class FTP extends StreamWrapper{ @@ -127,21 +128,20 @@ class FTP extends StreamWrapper{ $ext=''; } $tmpFile=\OCP\Files::tmpFile($ext); - \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); if ($this->file_exists($path)) { $this->getFile($path, $tmpFile); } - self::$tempFiles[$tmpFile]=$path; - return fopen('close://'.$tmpFile, $mode); + $handle = fopen($tmpFile, $mode); + return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) { + $this->writeBack($tmpFile, $path); + }); } return false; } - public function writeBack($tmpFile) { - if (isset(self::$tempFiles[$tmpFile])) { - $this->uploadFile($tmpFile, self::$tempFiles[$tmpFile]); - unlink($tmpFile); - } + public function writeBack($tmpFile, $path) { + $this->uploadFile($tmpFile, $path); + unlink($tmpFile); } /** diff --git a/apps/files_external/lib/Lib/Storage/Google.php b/apps/files_external/lib/Lib/Storage/Google.php index a3133cb4743..b22b0c29263 100644 --- a/apps/files_external/lib/Lib/Storage/Google.php +++ b/apps/files_external/lib/Lib/Storage/Google.php @@ -36,6 +36,7 @@ namespace OCA\Files_External\Lib\Storage; use GuzzleHttp\Exception\RequestException; +use Icewind\Streams\CallbackWrapper; use Icewind\Streams\IteratorDirectory; use Icewind\Streams\RetryWrapper; @@ -50,8 +51,6 @@ class Google extends \OC\Files\Storage\Common { private $service; private $driveFiles; - private static $tempFiles = array(); - // Google Doc mimetypes const FOLDER = 'application/vnd.google-apps.folder'; const DOCUMENT = 'application/vnd.google-apps.document'; @@ -495,94 +494,91 @@ class Google extends \OC\Files\Storage\Common { case 'c': case 'c+': $tmpFile = \OCP\Files::tmpFile($ext); - \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); if ($this->file_exists($path)) { $source = $this->fopen($path, 'rb'); file_put_contents($tmpFile, $source); } - self::$tempFiles[$tmpFile] = $path; - return fopen('close://'.$tmpFile, $mode); + $handle = fopen($tmpFile, $mode); + return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) { + $this->writeBack($tmpFile, $path); + }); } } - public function writeBack($tmpFile) { - if (isset(self::$tempFiles[$tmpFile])) { - $path = self::$tempFiles[$tmpFile]; - $parentFolder = $this->getDriveFile(dirname($path)); - if ($parentFolder) { - $mimetype = \OC::$server->getMimeTypeDetector()->detect($tmpFile); - $params = array( - 'mimeType' => $mimetype, - 'uploadType' => 'media' - ); - $result = false; + public function writeBack($tmpFile, $path) { + $parentFolder = $this->getDriveFile(dirname($path)); + if ($parentFolder) { + $mimetype = \OC::$server->getMimeTypeDetector()->detect($tmpFile); + $params = array( + 'mimeType' => $mimetype, + 'uploadType' => 'media' + ); + $result = false; + + $chunkSizeBytes = 10 * 1024 * 1024; + + $useChunking = false; + $size = filesize($tmpFile); + if ($size > $chunkSizeBytes) { + $useChunking = true; + } else { + $params['data'] = file_get_contents($tmpFile); + } - $chunkSizeBytes = 10 * 1024 * 1024; + if ($this->file_exists($path)) { + $file = $this->getDriveFile($path); + $this->client->setDefer($useChunking); + $request = $this->service->files->update($file->getId(), $file, $params); + } else { + $file = new \Google_Service_Drive_DriveFile(); + $file->setTitle(basename($path)); + $file->setMimeType($mimetype); + $parent = new \Google_Service_Drive_ParentReference(); + $parent->setId($parentFolder->getId()); + $file->setParents(array($parent)); + $this->client->setDefer($useChunking); + $request = $this->service->files->insert($file, $params); + } - $useChunking = false; - $size = filesize($tmpFile); - if ($size > $chunkSizeBytes) { - $useChunking = true; - } else { - $params['data'] = file_get_contents($tmpFile); + if ($useChunking) { + // Create a media file upload to represent our upload process. + $media = new \Google_Http_MediaFileUpload( + $this->client, + $request, + 'text/plain', + null, + true, + $chunkSizeBytes + ); + $media->setFileSize($size); + + // Upload the various chunks. $status will be false until the process is + // complete. + $status = false; + $handle = fopen($tmpFile, 'rb'); + while (!$status && !feof($handle)) { + $chunk = fread($handle, $chunkSizeBytes); + $status = $media->nextChunk($chunk); } - if ($this->file_exists($path)) { - $file = $this->getDriveFile($path); - $this->client->setDefer($useChunking); - $request = $this->service->files->update($file->getId(), $file, $params); - } else { - $file = new \Google_Service_Drive_DriveFile(); - $file->setTitle(basename($path)); - $file->setMimeType($mimetype); - $parent = new \Google_Service_Drive_ParentReference(); - $parent->setId($parentFolder->getId()); - $file->setParents(array($parent)); - $this->client->setDefer($useChunking); - $request = $this->service->files->insert($file, $params); + // The final value of $status will be the data from the API for the object + // that has been uploaded. + $result = false; + if ($status !== false) { + $result = $status; } - if ($useChunking) { - // Create a media file upload to represent our upload process. - $media = new \Google_Http_MediaFileUpload( - $this->client, - $request, - 'text/plain', - null, - true, - $chunkSizeBytes - ); - $media->setFileSize($size); - - // Upload the various chunks. $status will be false until the process is - // complete. - $status = false; - $handle = fopen($tmpFile, 'rb'); - while (!$status && !feof($handle)) { - $chunk = fread($handle, $chunkSizeBytes); - $status = $media->nextChunk($chunk); - } - - // The final value of $status will be the data from the API for the object - // that has been uploaded. - $result = false; - if ($status !== false) { - $result = $status; - } - - fclose($handle); - } else { - $result = $request; - } + fclose($handle); + } else { + $result = $request; + } - // Reset to the client to execute requests immediately in the future. - $this->client->setDefer(false); + // Reset to the client to execute requests immediately in the future. + $this->client->setDefer(false); - if ($result) { - $this->setDriveFile($path, $result); - } + if ($result) { + $this->setDriveFile($path, $result); } - unlink($tmpFile); } } diff --git a/apps/files_external/lib/Lib/Storage/SMB.php b/apps/files_external/lib/Lib/Storage/SMB.php index 4773afcb75f..7ffc078df6f 100644 --- a/apps/files_external/lib/Lib/Storage/SMB.php +++ b/apps/files_external/lib/Lib/Storage/SMB.php @@ -31,10 +31,12 @@ namespace OCA\Files_External\Lib\Storage; +use Icewind\SMB\Change; use Icewind\SMB\Exception\ConnectException; use Icewind\SMB\Exception\Exception; use Icewind\SMB\Exception\ForbiddenException; use Icewind\SMB\Exception\NotFoundException; +use Icewind\SMB\INotifyHandler; use Icewind\SMB\IFileInfo; use Icewind\SMB\IShare; use Icewind\SMB\NativeServer; @@ -486,16 +488,16 @@ class SMB extends Common implements INotifyStorage { public function listen($path, callable $callback) { $fullPath = $this->buildPath($path); $oldRenamePath = null; - $this->share->notify($fullPath, function ($smbType, $fullPath) use (&$oldRenamePath, $callback) { - $path = $this->relativePath($fullPath); + $this->share->notify($fullPath)->listen(function (Change $change) use (&$oldRenamePath, $callback) { + $path = $this->relativePath($change->getPath()); if (is_null($path)) { return true; } - if ($smbType === IShare::NOTIFY_RENAMED_OLD) { + if ($change->getCode() === INotifyHandler::NOTIFY_RENAMED_OLD) { $oldRenamePath = $path; return true; } - $type = $this->mapNotifyType($smbType); + $type = $this->mapNotifyType($change->getCode()); if (is_null($type)) { return true; } @@ -513,16 +515,16 @@ class SMB extends Common implements INotifyStorage { private function mapNotifyType($smbType) { switch ($smbType) { - case IShare::NOTIFY_ADDED: + case INotifyHandler::NOTIFY_ADDED: return INotifyStorage::NOTIFY_ADDED; - case IShare::NOTIFY_REMOVED: + case INotifyHandler::NOTIFY_REMOVED: return INotifyStorage::NOTIFY_REMOVED; - case IShare::NOTIFY_MODIFIED: - case IShare::NOTIFY_ADDED_STREAM: - case IShare::NOTIFY_MODIFIED_STREAM: - case IShare::NOTIFY_REMOVED_STREAM: + case INotifyHandler::NOTIFY_MODIFIED: + case INotifyHandler::NOTIFY_ADDED_STREAM: + case INotifyHandler::NOTIFY_MODIFIED_STREAM: + case INotifyHandler::NOTIFY_REMOVED_STREAM: return INotifyStorage::NOTIFY_MODIFIED; - case IShare::NOTIFY_RENAMED_NEW: + case INotifyHandler::NOTIFY_RENAMED_NEW: return INotifyStorage::NOTIFY_RENAMED; default: return null; diff --git a/apps/files_external/lib/Lib/Storage/Swift.php b/apps/files_external/lib/Lib/Storage/Swift.php index ba0b4898e2e..5fec278ef3d 100644 --- a/apps/files_external/lib/Lib/Storage/Swift.php +++ b/apps/files_external/lib/Lib/Storage/Swift.php @@ -37,6 +37,7 @@ namespace OCA\Files_External\Lib\Storage; use Guzzle\Http\Url; use Guzzle\Http\Exception\ClientErrorResponseException; +use Icewind\Streams\CallbackWrapper; use Icewind\Streams\IteratorDirectory; use OpenCloud; use OpenCloud\Common\Exceptions; @@ -410,7 +411,6 @@ class Swift extends \OC\Files\Storage\Common { $ext = ''; } $tmpFile = \OCP\Files::tmpFile($ext); - \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); // Fetch existing file if required if ($mode[0] !== 'w' && $this->file_exists($path)) { if ($mode[0] === 'x') { @@ -424,9 +424,10 @@ class Swift extends \OC\Files\Storage\Common { fseek($tmpFile, 0, SEEK_END); } } - self::$tmpFiles[$tmpFile] = $path; - - return fopen('close://' . $tmpFile, $mode); + $handle = fopen($tmpFile, $mode); + return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) { + $this->writeBack($tmpFile, $path); + }); } } @@ -615,12 +616,9 @@ class Swift extends \OC\Files\Storage\Common { return $this->container; } - public function writeBack($tmpFile) { - if (!isset(self::$tmpFiles[$tmpFile])) { - return false; - } + public function writeBack($tmpFile, $path) { $fileData = fopen($tmpFile, 'r'); - $this->getContainer()->uploadObject(self::$tmpFiles[$tmpFile], $fileData); + $this->getContainer()->uploadObject($path, $fileData); // invalidate target object to force repopulation on fetch $this->objectCache->remove(self::$tmpFiles[$tmpFile]); unlink($tmpFile); diff --git a/apps/files_sharing/l10n/de_DE.js b/apps/files_sharing/l10n/de_DE.js index 5dd7d26a530..bbfdae37c58 100644 --- a/apps/files_sharing/l10n/de_DE.js +++ b/apps/files_sharing/l10n/de_DE.js @@ -81,10 +81,10 @@ OC.L10N.register( "%1$s removed share" : "%1$s hat die Freigabe entfernt", "{actor} removed share" : "{actor} hat die Freigabe entfernt", "You shared %1$s with %2$s" : "Sie haben %1$s mit %2$s geteilt", - "You shared {file} with {user}" : "Du hast {file} mit {user} geteilt", - "You removed %2$s from %1$s" : "Sie haben die %2$s von %1$s entfernt", - "You removed {user} from {file}" : "Sie haben die {user} von {file} entfernt", - "%3$s shared %1$s with %2$s" : "%3$s geteilt %1$s mit %2$s", + "You shared {file} with {user}" : "Sie haben {file} mit {user} geteilt", + "You removed %2$s from %1$s" : "Sie haben %2$s von %1$s entfernt", + "You removed {user} from {file}" : "Sie haben {user} von {file} entfernt", + "%3$s shared %1$s with %2$s" : "%3$s hat %1$s mit %2$s geteilt", "{actor} removed {user} from {file}" : "{actor} hat {user} von {file} entfernt", "%3$s removed %2$s from %1$s" : "%3$s hat %2$s von %1$s entfernt", "%2$s shared %1$s with you" : "%2$s hat %1$s mit Ihnen geteilt", @@ -112,7 +112,7 @@ OC.L10N.register( "Unknown share type" : "Unbekannter Freigabetyp", "Not a directory" : "Kein Verzeichnis", "Could not lock path" : "Pfad konnte nicht gesperrt werden", - "Wrong or no update parameter given" : "Falscher oder kein Updateparameter wurde übergeben", + "Wrong or no update parameter given" : "Es wurde ein falscher oder kein Updateparameter übergeben", "Can't change permissions for public share links" : "Berechtigungen für öffentlich freigegebene Links konnten nicht geändert werden", "Cannot increase permissions" : "Berechtigungen können nicht erhöht werden", "%s is publicly shared" : "%s ist öffentlich geteilt", @@ -144,8 +144,8 @@ OC.L10N.register( "%1$s accepted remote share %2$s" : "%1$s hat die Remotefreigabe von %2$s akzeptiert", "%1$s declined remote share %2$s" : "%1$s hat die Remotefreigabe von %2$s abgelehnt", "%1$s unshared %2$s from you" : "%1$s hat die Freigabe von %2$s für Sie entfernt", - "Public shared folder %1$s was downloaded" : "Der öffentliche geteilte Ordner %1$s wurde heruntergeladen", - "Public shared file %1$s was downloaded" : "Die öffentliche geteilte Datei %1$s wurde heruntergeladen", + "Public shared folder %1$s was downloaded" : "Der öffentlich geteilte Ordner %1$s wurde heruntergeladen", + "Public shared file %1$s was downloaded" : "Die öffentlich geteilte Datei %1$s wurde heruntergeladen", "%2$s shared %1$s with %3$s" : "%2$s hat %1$s mit %3$s geteilt", "You removed the share of %2$s for %1$s" : "Sie haben die Freigabe von %2$s für %1$s entfernt", "%2$s removed the share of %3$s for %1$s" : "%2$s hat die Freigabe von %3$s für %1$s entfernt", @@ -166,10 +166,10 @@ OC.L10N.register( "Shared with group %2$s" : "Geteilt mit der Gruppe %2$s", "Shared with group %3$s by %2$s" : "Geteilt mit der Gruppe %3$s von %2$s", "Removed share of group %2$s" : "Freigabe für Gruppe %2$s entfernt", - "%2$s removed share of group %3$s" : "%2$s hat die Freigabe für Gruppe %3$s entfernt", + "%2$s removed share of group %3$s" : "%2$s hat die Freigabe für die Gruppe %3$s entfernt", "Shared via link by %2$s" : "Geteilt durch einen Link von %2$s", "Shared via public link" : "Durch einen öffentlichen Link geteilt", - "%2$s removed public link" : "%2$s hat die Freigabe als Link entfernt", + "%2$s removed public link" : "%2$s hat die Freigabe als öffentlichen Link entfernt", "Public link expired" : "öffentlicher Link ist abgelaufen", "Public link of %2$s expired" : "Öffentlicher Link von %2$s ist abgelaufen", "Shared by %2$s" : "Geteilt von %2$s", diff --git a/apps/files_sharing/l10n/de_DE.json b/apps/files_sharing/l10n/de_DE.json index 0bd88eea608..e62bb0d816f 100644 --- a/apps/files_sharing/l10n/de_DE.json +++ b/apps/files_sharing/l10n/de_DE.json @@ -79,10 +79,10 @@ "%1$s removed share" : "%1$s hat die Freigabe entfernt", "{actor} removed share" : "{actor} hat die Freigabe entfernt", "You shared %1$s with %2$s" : "Sie haben %1$s mit %2$s geteilt", - "You shared {file} with {user}" : "Du hast {file} mit {user} geteilt", - "You removed %2$s from %1$s" : "Sie haben die %2$s von %1$s entfernt", - "You removed {user} from {file}" : "Sie haben die {user} von {file} entfernt", - "%3$s shared %1$s with %2$s" : "%3$s geteilt %1$s mit %2$s", + "You shared {file} with {user}" : "Sie haben {file} mit {user} geteilt", + "You removed %2$s from %1$s" : "Sie haben %2$s von %1$s entfernt", + "You removed {user} from {file}" : "Sie haben {user} von {file} entfernt", + "%3$s shared %1$s with %2$s" : "%3$s hat %1$s mit %2$s geteilt", "{actor} removed {user} from {file}" : "{actor} hat {user} von {file} entfernt", "%3$s removed %2$s from %1$s" : "%3$s hat %2$s von %1$s entfernt", "%2$s shared %1$s with you" : "%2$s hat %1$s mit Ihnen geteilt", @@ -110,7 +110,7 @@ "Unknown share type" : "Unbekannter Freigabetyp", "Not a directory" : "Kein Verzeichnis", "Could not lock path" : "Pfad konnte nicht gesperrt werden", - "Wrong or no update parameter given" : "Falscher oder kein Updateparameter wurde übergeben", + "Wrong or no update parameter given" : "Es wurde ein falscher oder kein Updateparameter übergeben", "Can't change permissions for public share links" : "Berechtigungen für öffentlich freigegebene Links konnten nicht geändert werden", "Cannot increase permissions" : "Berechtigungen können nicht erhöht werden", "%s is publicly shared" : "%s ist öffentlich geteilt", @@ -142,8 +142,8 @@ "%1$s accepted remote share %2$s" : "%1$s hat die Remotefreigabe von %2$s akzeptiert", "%1$s declined remote share %2$s" : "%1$s hat die Remotefreigabe von %2$s abgelehnt", "%1$s unshared %2$s from you" : "%1$s hat die Freigabe von %2$s für Sie entfernt", - "Public shared folder %1$s was downloaded" : "Der öffentliche geteilte Ordner %1$s wurde heruntergeladen", - "Public shared file %1$s was downloaded" : "Die öffentliche geteilte Datei %1$s wurde heruntergeladen", + "Public shared folder %1$s was downloaded" : "Der öffentlich geteilte Ordner %1$s wurde heruntergeladen", + "Public shared file %1$s was downloaded" : "Die öffentlich geteilte Datei %1$s wurde heruntergeladen", "%2$s shared %1$s with %3$s" : "%2$s hat %1$s mit %3$s geteilt", "You removed the share of %2$s for %1$s" : "Sie haben die Freigabe von %2$s für %1$s entfernt", "%2$s removed the share of %3$s for %1$s" : "%2$s hat die Freigabe von %3$s für %1$s entfernt", @@ -164,10 +164,10 @@ "Shared with group %2$s" : "Geteilt mit der Gruppe %2$s", "Shared with group %3$s by %2$s" : "Geteilt mit der Gruppe %3$s von %2$s", "Removed share of group %2$s" : "Freigabe für Gruppe %2$s entfernt", - "%2$s removed share of group %3$s" : "%2$s hat die Freigabe für Gruppe %3$s entfernt", + "%2$s removed share of group %3$s" : "%2$s hat die Freigabe für die Gruppe %3$s entfernt", "Shared via link by %2$s" : "Geteilt durch einen Link von %2$s", "Shared via public link" : "Durch einen öffentlichen Link geteilt", - "%2$s removed public link" : "%2$s hat die Freigabe als Link entfernt", + "%2$s removed public link" : "%2$s hat die Freigabe als öffentlichen Link entfernt", "Public link expired" : "öffentlicher Link ist abgelaufen", "Public link of %2$s expired" : "Öffentlicher Link von %2$s ist abgelaufen", "Shared by %2$s" : "Geteilt von %2$s", diff --git a/apps/files_sharing/l10n/sv.js b/apps/files_sharing/l10n/sv.js index 79b85b90711..fb6d04c52c2 100644 --- a/apps/files_sharing/l10n/sv.js +++ b/apps/files_sharing/l10n/sv.js @@ -1,57 +1,164 @@ OC.L10N.register( "files_sharing", { - "Server to server sharing is not enabled on this server" : "Server-till-server-delning är inte aktiverat på denna server", - "The mountpoint name contains invalid characters." : "Monteringspunktens namn innehåller ogiltiga tecken.", - "Not allowed to create a federated share with the same user server" : "Ej tillåten att skapa en federerad delning med samma användarserver.", - "Invalid or untrusted SSL certificate" : "Ogiltigt eller ej betrott SSL-certifikat", - "Could not authenticate to remote share, password might be wrong" : "Kunde ej autensiera fjärrdelningen, lösenordet kan vara fel", - "Storage not valid" : "Lagring ej giltig", - "Couldn't add remote share" : "Kunde inte lägga till fjärrutdelning", "Shared with you" : "Delat med dig", "Shared with others" : "Delat med andra", - "Shared by link" : "Delad som länk", + "Shared by link" : "Delat som länk", "Nothing shared with you yet" : "Inget delat med dig ännu", - "Files and folders others share with you will show up here" : "Filer och mappar andra delar med dig kommer visas här", + "Files and folders others share with you will show up here" : "Filer och mappar som andra delar med dig kommer visas här", "Nothing shared yet" : "Inget delat ännu", - "Files and folders you share will show up here" : "Filer och mappar du delar kommer visas här", + "Files and folders you share will show up here" : "Filer och mappar som du delar kommer visas här", "No shared links" : "Inga delade länkar", - "Files and folders you share by link will show up here" : "Filer och mappar du delar som länkar kommer visas här", - "Do you want to add the remote share {name} from {owner}@{remote}?" : "Vill du lägga till fjärrdelning {name} från {owner}@{remote}?", - "Remote share" : "Fjärrdelning", - "Remote share password" : "Lösenord för fjärrdelning", - "Cancel" : "Avbryt", - "Add remote share" : "Lägg till fjärrdelning", + "Files and folders you share by link will show up here" : "Filer och mappar som du delar som länkar kommer visas här", "You can upload into this folder" : "Du kan ladda upp i denna map", + "No compatible server found at {remote}" : "Ingen kompatibel server hittad på {remote}", + "Invalid server URL" : "Ogiltig server URL", + "Failed to add the public link to your Nextcloud" : "Misslyckades skapa den offentliga delningslänken till ditt moln", + "Share" : "Dela", + "No expiration date set" : "Inget utgångsdatum satt", "Shared by" : "Delad av", - "Sharing" : "Dela", - "A file or folder has been <strong>shared</strong>" : "En fil eller mapp har <strong>delats</strong>", + "Sharing" : "Delning", + "File shares" : "Fildelningar", + "Downloaded via public link" : "Nedladdad via offentlig länk", + "Downloaded by %1$s" : "Nedladdad av %1$s", + "Downloaded by {email}" : "Nedladdad av {email}", + "%1$s downloaded via public link" : "%1$s nedladdad via offentlig länk", + "{file} downloaded via public link" : "{file} nedladdad via offentlig länk", + "%1$s downloaded %2$s" : "%1$s laddade ned %2$s", + "{email} downloaded {file}" : "{email} laddade ned {file}", + "Shared with group %1$s" : "Delad med grupp %1$s", + "Shared with group {group}" : "Delad med grupp {group}", + "Removed share for group %1$s" : "Tog bort delning för grupp %1$s", + "Removed share for group {group}" : "Tog bort delning för grupp {group}", + "%2$s shared with group %1$s" : "%2$s delad med grupp %1$s", + "{actor} shared with group {group}" : "{actor} delade med gruppen {group}", + "%2$s removed share for group %1$s" : "%2$s tog bort delningen för gruppen %1$s", + "{actor} removed share for group {group}" : "{actor} tog bort delningen för gruppen {group}", + "You shared %1$s with group %2$s" : "Du delade %1$s med grupp %2$s", + "You shared {file} with group {group}" : "Du delade {file} med gruppen {group}", + "You removed group %2$s from %1$s" : "Du tog bort gruppen %2$s från %1$s", + "You removed group {group} from {file}" : "Du tog bort gruppen {group} från {file}", + "%3$s shared %1$s with group %2$s" : "%3$s delade %1$s med gruppen %2$s", + "{actor} shared {file} with group {group}" : "{actor} delade {file} med gruppen {group}", + "%3$s removed group %2$s from %1$s" : "%3$s tog bort gruppen %2$s från %1$s", + "{actor} removed group {group} from {file}" : "{actor} tog bort gruppen {group} från {file}", + "Shared as public link" : "Delades offentligt", + "Removed public link" : "Tog bort offentlig länk", + "%1$s shared as public link" : "%1$s delade offentligt", + "{actor} shared as public link" : "{actor} delade offentligt", + "%1$s removed public link" : "%1$s tog bort offentlig länk", + "{actor} removed public link" : "{actor} tog bort offentlig länk", + "You shared %1$s as public link" : "Du delade %1$s offentligt", + "You shared {file} as public link" : "Du delade {file} offentligt", + "You removed public link for %1$s" : "Du tog bort den offentliga länken för %1$s", + "You removed public link for {file}" : "Du tog bort den offentliga länken för {file}", + "%2$s shared %1$s as public link" : "%2$s delade %1$s offentligt", + "{actor} shared {file} as public link" : "{actor} delade {file} offentligt", + "%2$s removed public link for %1$s" : "%2$s tog bort den offentliga länken för %1$s", + "{actor} removed public link for {file}" : "{actor} tog bort den offentliga länken för {file}", + "%1$s accepted the remote share" : "%1$s accepterade extern delning", + "{user} accepted the remote share" : "{user} accepterade extern delning", + "%1$s declined the remote share" : "%1$s nekade extern delning", + "{user} declined the remote share" : "{user} nekade extern delning", + "You received a new remote share %1$s from %2$s" : "Du har fått en ny extern delning %1$s från %2$s", + "You received a new remote share {file} from {user}" : "Du har fått en ny extern delning {file} från {user}", + "%2$s accepted the remote share of %1$s" : "%2$s accepterade extern delning av %1$s", + "{user} accepted the remote share of {file}" : "{user} accepterade extern delning av {file}", + "%2$s declined the remote share of %1$s" : "%2$s nekade extern delning av %1$s", + "{user} declined the remote share of {file}" : "{user} nekade extern delning av {file}", + "%2$s unshared %1$s from you" : "%2$s slutade dela %1$s med dig", + "{user} unshared {file} from you" : "{user} slutade dela {file} med dig", + "Shared with %1$s" : "Delade med %1$s", + "Shared with {user}" : "Delade med {user}", + "Removed share for %1$s" : "Tog bort delningen för %1$s", + "Removed share for {user}" : "Tog bort delningen för {user}", + "%2$s shared with %1$s" : "%2$s delade med %1$s", + "{actor} shared with {user}" : "{actor} delade med {user}", + "%2$s removed share for %1$s" : "%2$s tog bort delningen för %1$s", + "{actor} removed share for {user}" : "{actor} tog bort delningen för {user}", + "Shared by %1$s" : "Delad av %1$s", + "Shared by {actor}" : "Delad av {actor}", + "%1$s removed share" : "%1$s tog bort delning", + "{actor} removed share" : "{actor} tog bort delning", + "You shared %1$s with %2$s" : "Du delade %1$s med %2$s", + "You shared {file} with {user}" : "Du delade {file} med {user}", + "You removed %2$s from %1$s" : "Du tog bort %2$s från %1$s", + "You removed {user} from {file}" : "Du tog bort {user} från {file}", + "%3$s shared %1$s with %2$s" : "%3$s delade %1$s med %2$s", + "{actor} removed {user} from {file}" : "{actor} tog bort {user} från {file}", + "%3$s removed %2$s from %1$s" : "%3$s tog bort %2$s från %1$s", + "%2$s shared %1$s with you" : "%2$s delade %1$s med dig", + "{actor} shared {file} with you" : "{actor} delade {file} med dig", + "%2$s removed you from %1$s" : "%2$s tog bort dig från %1$s", + "{actor} removed you from {file}" : "{actor} tog bort dig från {file}", + "A file or folder shared by mail or by public link was <strong>downloaded</strong>" : "En fil eller mapp som delats via mejl eller offentlig länk har blivit <strong>nedladdad</strong>", "A file or folder was shared from <strong>another server</strong>" : "En fil eller mapp delades från <strong>en annan server</strong>", - "A public shared file or folder was <strong>downloaded</strong>" : "En publikt delad fil eller mapp blev <strong>nerladdad</strong>", - "You received a new remote share %2$s from %1$s" : "Du har mottagit en fjärrdelning %2$s från %1$s", - "You received a new remote share from %s" : "Du mottog en ny fjärrdelning från %s", - "%1$s accepted remote share %2$s" : "%1$s accepterade fjärrdelning %2$s", - "%1$s declined remote share %2$s" : "%1$s nekade fjärrdelning %2$s", + "A file or folder has been <strong>shared</strong>" : "En fil eller mapp har <strong>delats</strong>", + "Wrong share ID, share doesn't exist" : "Fel delnings-ID, delningen finns inte", + "could not delete share" : "kunde inte ta bort delningen", + "Could not delete share" : "Kunde inte ta bort delningen", + "Please specify a file or folder path" : "Ange sökväg till filen eller mappen", + "Wrong path, file/folder doesn't exist" : "Fel sökväg, fil/mapp finns inte", + "Could not create share" : "Kunde inte skapa delning", + "invalid permissions" : "ogiltiga behörigheter", + "Please specify a valid user" : "Ange en giltig användare", + "Group sharing is disabled by the administrator" : "Gruppdelning är avstängt", + "Please specify a valid group" : "Ange en giltig grupp", + "Public link sharing is disabled by the administrator" : "Offentlig delningslänk är avstängt", + "Public upload disabled by the administrator" : "Offentlig uppladdning är avstängt", + "Public upload is only possible for publicly shared folders" : "Offentlig uppladdning fungerar endast i offentligt delade mappar", + "Invalid date, date format must be YYYY-MM-DD" : "Ogiltigt datum, måste anges: ÅÅÅÅ-MM-DD", + "Sharing %s failed because the back end does not allow shares from type %s" : "Delningen av %s misslyckades. Ej tillåtet med delning av %s", + "Unknown share type" : "Ogiltig delningstyp", + "Not a directory" : "Inte en mapp", + "Could not lock path" : "Kunde inte låsa sökvägen", + "Wrong or no update parameter given" : "Fel eller ingen uppdateringsparameter angiven", + "Can't change permissions for public share links" : "Det går inte att ändra behörigheterna för offentliga länkar", + "Cannot increase permissions" : "Kan inte utöka behörigheter", + "%s is publicly shared" : "%s är offentligt delad", + "Share API is disabled" : "Delning av API är inaktiverad", + "This share is password-protected" : "Den här delningen är lösenordsskyddad", + "The password is wrong. Try again." : "Lösenordet är fel. Försök igen.", + "Password" : "Lösenord", + "No entries found in this folder" : "Inga filer hittades i denna mapp", + "Name" : "Namn", + "Share time" : "Delningstid", + "Expiration date" : "Utgångsdatum", + "Sorry, this link doesn’t seem to work anymore." : "Tyvärr, denna länk verkar inte fungera längre.", + "Reasons might be:" : "Orsaker kan vara:", + "the item was removed" : "objektet togs bort", + "the link expired" : "giltighet för länken har gått ut", + "sharing is disabled" : "delning är inaktiverat", + "For more info, please ask the person who sent this link." : "För mer information, kontakta den person som skickade den här länken.", + "Add to your Nextcloud" : "Lägg till i molnet", + "Download" : "Ladda ned", + "Download %s" : "Ladda ned %s", + "Direct link" : "Direktlänk", + "Upload files to %s" : "Ladda upp filer till %s", + "Select or drop files" : "Välj eller dra filer hit", + "Uploading files…" : "Laddar upp filer...", + "Uploaded files:" : "Uppladdade filer:", + "A public shared file or folder was <strong>downloaded</strong>" : "En offentligt delad fil eller mapp blev <strong>nedladdad</strong>", + "You received a new remote share %2$s from %1$s" : "Du har mottagit en ny extern delning %2$s från %1$s", + "You received a new remote share from %s" : "Du har mottagit en ny extern delning från %s", + "%1$s accepted remote share %2$s" : "%1$s accepterade extern delning %2$s", + "%1$s declined remote share %2$s" : "%1$s nekade extern delning av %2$s", "%1$s unshared %2$s from you" : "%1$s tog bort delningen %2$s från dig", - "Public shared folder %1$s was downloaded" : "Publikt delad mapp %1$s blev nerladdad", - "Public shared file %1$s was downloaded" : "Publikt delad fil %1$s blev nerladdad", - "You shared %1$s with %2$s" : "Du delade %1$s med %2$s", + "Public shared folder %1$s was downloaded" : "Offentligt delad mapp %1$s blev nedladdad", + "Public shared file %1$s was downloaded" : "Offentligt delad fil %1$s blev nedladdad", "%2$s shared %1$s with %3$s" : "%2$s delade %1$s med %3$s", "You removed the share of %2$s for %1$s" : "Du tog bort delning av %2$s för %1$s", "%2$s removed the share of %3$s for %1$s" : "%2$s tog bort delningen av %3$s för %1$s", - "You shared %1$s with group %2$s" : "Du delade %1$s med grupp %2$s", "%2$s shared %1$s with group %3$s" : "%2$s delade %1$s med gruppen %3$s", "You removed the share of group %2$s for %1$s" : "Du tog bort delningen av gruppen %2$s för %1$s", "%2$s removed the share of group %3$s for %1$s" : "%2$s tog bort delningen av grupp %3$s för %1$s", "%2$s shared %1$s via link" : "%2$s delade %1$s via länk", "You shared %1$s via link" : "Du delade %1$s via länk", - "You removed the public link for %1$s" : "Du tog bort den publika länken för %1$s", - "%2$s removed the public link for %1$s" : "%2$s tog bort den publika länken för %1$s", - "Your public link for %1$s expired" : "Din publika länk för %1$s har löpt ut", - "The public link of %2$s for %1$s expired" : "Den publika länken för %2$s av %1$s har löpt ut", - "%2$s shared %1$s with you" : "%2$s delade %1$s med dig", + "You removed the public link for %1$s" : "Du tog bort den offentliga länken för %1$s", + "%2$s removed the public link for %1$s" : "%2$s tog bort den offentliga länken för %1$s", + "Your public link for %1$s expired" : "Din offentliga länk för %1$s har löpt ut", + "The public link of %2$s for %1$s expired" : "Den offentliga länken för %2$s av %1$s har löpt ut", "%2$s removed the share for %1$s" : "%2$s tog bort delningen av %1$s", - "Downloaded via public link" : "Nerladdad via publik länk", "Shared with %2$s" : "Delad med %2$s", "Shared with %3$s by %2$s" : "Delad med %3$s av %2$s", "Removed share for %2$s" : "Tog bort delningen för %2$s", @@ -61,28 +168,11 @@ OC.L10N.register( "Removed share of group %2$s" : "Tog bort delning av grupp %2$s", "%2$s removed share of group %3$s" : "%2$s tog bort delningen av grupp %3$s", "Shared via link by %2$s" : "Delad via länk av %2$s", - "Shared via public link" : "Delad via publik länk", - "Removed public link" : "Tog bort publik länk", - "%2$s removed public link" : "%2$s tog bort publik länk", - "Public link expired" : "Tidsgräns för publik länk har löpt ut", - "Public link of %2$s expired" : "Tidsgräns för publik länk för %2$s har löpt ut", + "Shared via public link" : "Delad via offentlig länk", + "%2$s removed public link" : "%2$s tog bort offentlig länk", + "Public link expired" : "Tidsgräns för offentlig länk har löpt ut", + "Public link of %2$s expired" : "Tidsgräns för offentlig länk för %2$s har löpt ut", "Shared by %2$s" : "Delad av %2$s", - "Shares" : "Delningar", - "This share is password-protected" : "Den här delningen är lösenordsskyddad", - "The password is wrong. Try again." : "Lösenordet är fel. Försök igen.", - "Password" : "Lösenord", - "No entries found in this folder" : "nga Filer hittades i denna mapp", - "Name" : "Namn", - "Share time" : "Delningstid", - "Sorry, this link doesn’t seem to work anymore." : "Tyvärr, denna länk verkar inte fungera längre.", - "Reasons might be:" : "Orsaker kan vara:", - "the item was removed" : "objektet togs bort", - "the link expired" : "giltighet för länken har gått ut", - "sharing is disabled" : "delning är inaktiverat", - "For more info, please ask the person who sent this link." : "För mer information, kontakta den person som skickade den här länken.", - "Add to your ownCloud" : "Lägg till i din ownCloud", - "Download" : "Ladda ner", - "Download %s" : "Ladda ner %s", - "Direct link" : "Direkt länk" + "Shares" : "Delningar" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_sharing/l10n/sv.json b/apps/files_sharing/l10n/sv.json index 870d7c1c3a4..84a1e4a83bf 100644 --- a/apps/files_sharing/l10n/sv.json +++ b/apps/files_sharing/l10n/sv.json @@ -1,55 +1,162 @@ { "translations": { - "Server to server sharing is not enabled on this server" : "Server-till-server-delning är inte aktiverat på denna server", - "The mountpoint name contains invalid characters." : "Monteringspunktens namn innehåller ogiltiga tecken.", - "Not allowed to create a federated share with the same user server" : "Ej tillåten att skapa en federerad delning med samma användarserver.", - "Invalid or untrusted SSL certificate" : "Ogiltigt eller ej betrott SSL-certifikat", - "Could not authenticate to remote share, password might be wrong" : "Kunde ej autensiera fjärrdelningen, lösenordet kan vara fel", - "Storage not valid" : "Lagring ej giltig", - "Couldn't add remote share" : "Kunde inte lägga till fjärrutdelning", "Shared with you" : "Delat med dig", "Shared with others" : "Delat med andra", - "Shared by link" : "Delad som länk", + "Shared by link" : "Delat som länk", "Nothing shared with you yet" : "Inget delat med dig ännu", - "Files and folders others share with you will show up here" : "Filer och mappar andra delar med dig kommer visas här", + "Files and folders others share with you will show up here" : "Filer och mappar som andra delar med dig kommer visas här", "Nothing shared yet" : "Inget delat ännu", - "Files and folders you share will show up here" : "Filer och mappar du delar kommer visas här", + "Files and folders you share will show up here" : "Filer och mappar som du delar kommer visas här", "No shared links" : "Inga delade länkar", - "Files and folders you share by link will show up here" : "Filer och mappar du delar som länkar kommer visas här", - "Do you want to add the remote share {name} from {owner}@{remote}?" : "Vill du lägga till fjärrdelning {name} från {owner}@{remote}?", - "Remote share" : "Fjärrdelning", - "Remote share password" : "Lösenord för fjärrdelning", - "Cancel" : "Avbryt", - "Add remote share" : "Lägg till fjärrdelning", + "Files and folders you share by link will show up here" : "Filer och mappar som du delar som länkar kommer visas här", "You can upload into this folder" : "Du kan ladda upp i denna map", + "No compatible server found at {remote}" : "Ingen kompatibel server hittad på {remote}", + "Invalid server URL" : "Ogiltig server URL", + "Failed to add the public link to your Nextcloud" : "Misslyckades skapa den offentliga delningslänken till ditt moln", + "Share" : "Dela", + "No expiration date set" : "Inget utgångsdatum satt", "Shared by" : "Delad av", - "Sharing" : "Dela", - "A file or folder has been <strong>shared</strong>" : "En fil eller mapp har <strong>delats</strong>", + "Sharing" : "Delning", + "File shares" : "Fildelningar", + "Downloaded via public link" : "Nedladdad via offentlig länk", + "Downloaded by %1$s" : "Nedladdad av %1$s", + "Downloaded by {email}" : "Nedladdad av {email}", + "%1$s downloaded via public link" : "%1$s nedladdad via offentlig länk", + "{file} downloaded via public link" : "{file} nedladdad via offentlig länk", + "%1$s downloaded %2$s" : "%1$s laddade ned %2$s", + "{email} downloaded {file}" : "{email} laddade ned {file}", + "Shared with group %1$s" : "Delad med grupp %1$s", + "Shared with group {group}" : "Delad med grupp {group}", + "Removed share for group %1$s" : "Tog bort delning för grupp %1$s", + "Removed share for group {group}" : "Tog bort delning för grupp {group}", + "%2$s shared with group %1$s" : "%2$s delad med grupp %1$s", + "{actor} shared with group {group}" : "{actor} delade med gruppen {group}", + "%2$s removed share for group %1$s" : "%2$s tog bort delningen för gruppen %1$s", + "{actor} removed share for group {group}" : "{actor} tog bort delningen för gruppen {group}", + "You shared %1$s with group %2$s" : "Du delade %1$s med grupp %2$s", + "You shared {file} with group {group}" : "Du delade {file} med gruppen {group}", + "You removed group %2$s from %1$s" : "Du tog bort gruppen %2$s från %1$s", + "You removed group {group} from {file}" : "Du tog bort gruppen {group} från {file}", + "%3$s shared %1$s with group %2$s" : "%3$s delade %1$s med gruppen %2$s", + "{actor} shared {file} with group {group}" : "{actor} delade {file} med gruppen {group}", + "%3$s removed group %2$s from %1$s" : "%3$s tog bort gruppen %2$s från %1$s", + "{actor} removed group {group} from {file}" : "{actor} tog bort gruppen {group} från {file}", + "Shared as public link" : "Delades offentligt", + "Removed public link" : "Tog bort offentlig länk", + "%1$s shared as public link" : "%1$s delade offentligt", + "{actor} shared as public link" : "{actor} delade offentligt", + "%1$s removed public link" : "%1$s tog bort offentlig länk", + "{actor} removed public link" : "{actor} tog bort offentlig länk", + "You shared %1$s as public link" : "Du delade %1$s offentligt", + "You shared {file} as public link" : "Du delade {file} offentligt", + "You removed public link for %1$s" : "Du tog bort den offentliga länken för %1$s", + "You removed public link for {file}" : "Du tog bort den offentliga länken för {file}", + "%2$s shared %1$s as public link" : "%2$s delade %1$s offentligt", + "{actor} shared {file} as public link" : "{actor} delade {file} offentligt", + "%2$s removed public link for %1$s" : "%2$s tog bort den offentliga länken för %1$s", + "{actor} removed public link for {file}" : "{actor} tog bort den offentliga länken för {file}", + "%1$s accepted the remote share" : "%1$s accepterade extern delning", + "{user} accepted the remote share" : "{user} accepterade extern delning", + "%1$s declined the remote share" : "%1$s nekade extern delning", + "{user} declined the remote share" : "{user} nekade extern delning", + "You received a new remote share %1$s from %2$s" : "Du har fått en ny extern delning %1$s från %2$s", + "You received a new remote share {file} from {user}" : "Du har fått en ny extern delning {file} från {user}", + "%2$s accepted the remote share of %1$s" : "%2$s accepterade extern delning av %1$s", + "{user} accepted the remote share of {file}" : "{user} accepterade extern delning av {file}", + "%2$s declined the remote share of %1$s" : "%2$s nekade extern delning av %1$s", + "{user} declined the remote share of {file}" : "{user} nekade extern delning av {file}", + "%2$s unshared %1$s from you" : "%2$s slutade dela %1$s med dig", + "{user} unshared {file} from you" : "{user} slutade dela {file} med dig", + "Shared with %1$s" : "Delade med %1$s", + "Shared with {user}" : "Delade med {user}", + "Removed share for %1$s" : "Tog bort delningen för %1$s", + "Removed share for {user}" : "Tog bort delningen för {user}", + "%2$s shared with %1$s" : "%2$s delade med %1$s", + "{actor} shared with {user}" : "{actor} delade med {user}", + "%2$s removed share for %1$s" : "%2$s tog bort delningen för %1$s", + "{actor} removed share for {user}" : "{actor} tog bort delningen för {user}", + "Shared by %1$s" : "Delad av %1$s", + "Shared by {actor}" : "Delad av {actor}", + "%1$s removed share" : "%1$s tog bort delning", + "{actor} removed share" : "{actor} tog bort delning", + "You shared %1$s with %2$s" : "Du delade %1$s med %2$s", + "You shared {file} with {user}" : "Du delade {file} med {user}", + "You removed %2$s from %1$s" : "Du tog bort %2$s från %1$s", + "You removed {user} from {file}" : "Du tog bort {user} från {file}", + "%3$s shared %1$s with %2$s" : "%3$s delade %1$s med %2$s", + "{actor} removed {user} from {file}" : "{actor} tog bort {user} från {file}", + "%3$s removed %2$s from %1$s" : "%3$s tog bort %2$s från %1$s", + "%2$s shared %1$s with you" : "%2$s delade %1$s med dig", + "{actor} shared {file} with you" : "{actor} delade {file} med dig", + "%2$s removed you from %1$s" : "%2$s tog bort dig från %1$s", + "{actor} removed you from {file}" : "{actor} tog bort dig från {file}", + "A file or folder shared by mail or by public link was <strong>downloaded</strong>" : "En fil eller mapp som delats via mejl eller offentlig länk har blivit <strong>nedladdad</strong>", "A file or folder was shared from <strong>another server</strong>" : "En fil eller mapp delades från <strong>en annan server</strong>", - "A public shared file or folder was <strong>downloaded</strong>" : "En publikt delad fil eller mapp blev <strong>nerladdad</strong>", - "You received a new remote share %2$s from %1$s" : "Du har mottagit en fjärrdelning %2$s från %1$s", - "You received a new remote share from %s" : "Du mottog en ny fjärrdelning från %s", - "%1$s accepted remote share %2$s" : "%1$s accepterade fjärrdelning %2$s", - "%1$s declined remote share %2$s" : "%1$s nekade fjärrdelning %2$s", + "A file or folder has been <strong>shared</strong>" : "En fil eller mapp har <strong>delats</strong>", + "Wrong share ID, share doesn't exist" : "Fel delnings-ID, delningen finns inte", + "could not delete share" : "kunde inte ta bort delningen", + "Could not delete share" : "Kunde inte ta bort delningen", + "Please specify a file or folder path" : "Ange sökväg till filen eller mappen", + "Wrong path, file/folder doesn't exist" : "Fel sökväg, fil/mapp finns inte", + "Could not create share" : "Kunde inte skapa delning", + "invalid permissions" : "ogiltiga behörigheter", + "Please specify a valid user" : "Ange en giltig användare", + "Group sharing is disabled by the administrator" : "Gruppdelning är avstängt", + "Please specify a valid group" : "Ange en giltig grupp", + "Public link sharing is disabled by the administrator" : "Offentlig delningslänk är avstängt", + "Public upload disabled by the administrator" : "Offentlig uppladdning är avstängt", + "Public upload is only possible for publicly shared folders" : "Offentlig uppladdning fungerar endast i offentligt delade mappar", + "Invalid date, date format must be YYYY-MM-DD" : "Ogiltigt datum, måste anges: ÅÅÅÅ-MM-DD", + "Sharing %s failed because the back end does not allow shares from type %s" : "Delningen av %s misslyckades. Ej tillåtet med delning av %s", + "Unknown share type" : "Ogiltig delningstyp", + "Not a directory" : "Inte en mapp", + "Could not lock path" : "Kunde inte låsa sökvägen", + "Wrong or no update parameter given" : "Fel eller ingen uppdateringsparameter angiven", + "Can't change permissions for public share links" : "Det går inte att ändra behörigheterna för offentliga länkar", + "Cannot increase permissions" : "Kan inte utöka behörigheter", + "%s is publicly shared" : "%s är offentligt delad", + "Share API is disabled" : "Delning av API är inaktiverad", + "This share is password-protected" : "Den här delningen är lösenordsskyddad", + "The password is wrong. Try again." : "Lösenordet är fel. Försök igen.", + "Password" : "Lösenord", + "No entries found in this folder" : "Inga filer hittades i denna mapp", + "Name" : "Namn", + "Share time" : "Delningstid", + "Expiration date" : "Utgångsdatum", + "Sorry, this link doesn’t seem to work anymore." : "Tyvärr, denna länk verkar inte fungera längre.", + "Reasons might be:" : "Orsaker kan vara:", + "the item was removed" : "objektet togs bort", + "the link expired" : "giltighet för länken har gått ut", + "sharing is disabled" : "delning är inaktiverat", + "For more info, please ask the person who sent this link." : "För mer information, kontakta den person som skickade den här länken.", + "Add to your Nextcloud" : "Lägg till i molnet", + "Download" : "Ladda ned", + "Download %s" : "Ladda ned %s", + "Direct link" : "Direktlänk", + "Upload files to %s" : "Ladda upp filer till %s", + "Select or drop files" : "Välj eller dra filer hit", + "Uploading files…" : "Laddar upp filer...", + "Uploaded files:" : "Uppladdade filer:", + "A public shared file or folder was <strong>downloaded</strong>" : "En offentligt delad fil eller mapp blev <strong>nedladdad</strong>", + "You received a new remote share %2$s from %1$s" : "Du har mottagit en ny extern delning %2$s från %1$s", + "You received a new remote share from %s" : "Du har mottagit en ny extern delning från %s", + "%1$s accepted remote share %2$s" : "%1$s accepterade extern delning %2$s", + "%1$s declined remote share %2$s" : "%1$s nekade extern delning av %2$s", "%1$s unshared %2$s from you" : "%1$s tog bort delningen %2$s från dig", - "Public shared folder %1$s was downloaded" : "Publikt delad mapp %1$s blev nerladdad", - "Public shared file %1$s was downloaded" : "Publikt delad fil %1$s blev nerladdad", - "You shared %1$s with %2$s" : "Du delade %1$s med %2$s", + "Public shared folder %1$s was downloaded" : "Offentligt delad mapp %1$s blev nedladdad", + "Public shared file %1$s was downloaded" : "Offentligt delad fil %1$s blev nedladdad", "%2$s shared %1$s with %3$s" : "%2$s delade %1$s med %3$s", "You removed the share of %2$s for %1$s" : "Du tog bort delning av %2$s för %1$s", "%2$s removed the share of %3$s for %1$s" : "%2$s tog bort delningen av %3$s för %1$s", - "You shared %1$s with group %2$s" : "Du delade %1$s med grupp %2$s", "%2$s shared %1$s with group %3$s" : "%2$s delade %1$s med gruppen %3$s", "You removed the share of group %2$s for %1$s" : "Du tog bort delningen av gruppen %2$s för %1$s", "%2$s removed the share of group %3$s for %1$s" : "%2$s tog bort delningen av grupp %3$s för %1$s", "%2$s shared %1$s via link" : "%2$s delade %1$s via länk", "You shared %1$s via link" : "Du delade %1$s via länk", - "You removed the public link for %1$s" : "Du tog bort den publika länken för %1$s", - "%2$s removed the public link for %1$s" : "%2$s tog bort den publika länken för %1$s", - "Your public link for %1$s expired" : "Din publika länk för %1$s har löpt ut", - "The public link of %2$s for %1$s expired" : "Den publika länken för %2$s av %1$s har löpt ut", - "%2$s shared %1$s with you" : "%2$s delade %1$s med dig", + "You removed the public link for %1$s" : "Du tog bort den offentliga länken för %1$s", + "%2$s removed the public link for %1$s" : "%2$s tog bort den offentliga länken för %1$s", + "Your public link for %1$s expired" : "Din offentliga länk för %1$s har löpt ut", + "The public link of %2$s for %1$s expired" : "Den offentliga länken för %2$s av %1$s har löpt ut", "%2$s removed the share for %1$s" : "%2$s tog bort delningen av %1$s", - "Downloaded via public link" : "Nerladdad via publik länk", "Shared with %2$s" : "Delad med %2$s", "Shared with %3$s by %2$s" : "Delad med %3$s av %2$s", "Removed share for %2$s" : "Tog bort delningen för %2$s", @@ -59,28 +166,11 @@ "Removed share of group %2$s" : "Tog bort delning av grupp %2$s", "%2$s removed share of group %3$s" : "%2$s tog bort delningen av grupp %3$s", "Shared via link by %2$s" : "Delad via länk av %2$s", - "Shared via public link" : "Delad via publik länk", - "Removed public link" : "Tog bort publik länk", - "%2$s removed public link" : "%2$s tog bort publik länk", - "Public link expired" : "Tidsgräns för publik länk har löpt ut", - "Public link of %2$s expired" : "Tidsgräns för publik länk för %2$s har löpt ut", + "Shared via public link" : "Delad via offentlig länk", + "%2$s removed public link" : "%2$s tog bort offentlig länk", + "Public link expired" : "Tidsgräns för offentlig länk har löpt ut", + "Public link of %2$s expired" : "Tidsgräns för offentlig länk för %2$s har löpt ut", "Shared by %2$s" : "Delad av %2$s", - "Shares" : "Delningar", - "This share is password-protected" : "Den här delningen är lösenordsskyddad", - "The password is wrong. Try again." : "Lösenordet är fel. Försök igen.", - "Password" : "Lösenord", - "No entries found in this folder" : "nga Filer hittades i denna mapp", - "Name" : "Namn", - "Share time" : "Delningstid", - "Sorry, this link doesn’t seem to work anymore." : "Tyvärr, denna länk verkar inte fungera längre.", - "Reasons might be:" : "Orsaker kan vara:", - "the item was removed" : "objektet togs bort", - "the link expired" : "giltighet för länken har gått ut", - "sharing is disabled" : "delning är inaktiverat", - "For more info, please ask the person who sent this link." : "För mer information, kontakta den person som skickade den här länken.", - "Add to your ownCloud" : "Lägg till i din ownCloud", - "Download" : "Ladda ner", - "Download %s" : "Ladda ner %s", - "Direct link" : "Direkt länk" + "Shares" : "Delningar" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_sharing/lib/AppInfo/Application.php b/apps/files_sharing/lib/AppInfo/Application.php index 922db7dac75..403d30ae2e6 100644 --- a/apps/files_sharing/lib/AppInfo/Application.php +++ b/apps/files_sharing/lib/AppInfo/Application.php @@ -111,7 +111,9 @@ class Application extends App { $c->query('AppName'), $server->getConfig(), $server->getAppManager(), - $c['ControllerMethodReflector'] + $c['ControllerMethodReflector'], + $server->getShareManager(), + $server->getRequest() ); }); diff --git a/apps/files_sharing/lib/Middleware/SharingCheckMiddleware.php b/apps/files_sharing/lib/Middleware/SharingCheckMiddleware.php index 7e9109bf2d1..5712b96b97d 100644 --- a/apps/files_sharing/lib/Middleware/SharingCheckMiddleware.php +++ b/apps/files_sharing/lib/Middleware/SharingCheckMiddleware.php @@ -25,6 +25,8 @@ namespace OCA\Files_Sharing\Middleware; +use OCA\Files_Sharing\Controller\ExternalSharesController; +use OCA\Files_Sharing\Controller\ShareController; use OCP\App\IAppManager; use OCP\AppFramework\Http\NotFoundResponse; use OCP\AppFramework\Middleware; @@ -33,6 +35,8 @@ use OCP\IConfig; use OCP\AppFramework\Utility\IControllerMethodReflector; use OCA\Files_Sharing\Exceptions\S2SException; use OCP\AppFramework\Http\JSONResponse; +use OCP\IRequest; +use OCP\Share\IManager; /** * Checks whether the "sharing check" is enabled @@ -49,21 +53,32 @@ class SharingCheckMiddleware extends Middleware { protected $appManager; /** @var IControllerMethodReflector */ protected $reflector; + /** @var IManager */ + protected $shareManager; + /** @var IRequest */ + protected $request; /*** * @param string $appName * @param IConfig $config * @param IAppManager $appManager + * @param IControllerMethodReflector $reflector + * @param IManager $shareManager + * @param IRequest $request */ public function __construct($appName, IConfig $config, IAppManager $appManager, - IControllerMethodReflector $reflector + IControllerMethodReflector $reflector, + IManager $shareManager, + IRequest $request ) { $this->appName = $appName; $this->config = $config; $this->appManager = $appManager; $this->reflector = $reflector; + $this->shareManager = $shareManager; + $this->request = $request; } /** @@ -72,18 +87,23 @@ class SharingCheckMiddleware extends Middleware { * @param \OCP\AppFramework\Controller $controller * @param string $methodName * @throws NotFoundException + * @throws S2SException */ public function beforeController($controller, $methodName) { if(!$this->isSharingEnabled()) { throw new NotFoundException('Sharing is disabled.'); } - if ($controller instanceof \OCA\Files_Sharing\Controller\ExternalSharesController && + if ($controller instanceof ExternalSharesController && !$this->externalSharesChecks()) { throw new S2SException('Federated sharing not allowed'); - } else if ($controller instanceof \OCA\Files_Sharing\Controller\ShareController && - !$this->isLinkSharingEnabled()) { - throw new NotFoundException('Link sharing is disabled'); + } else if ($controller instanceof ShareController) { + $token = $this->request->getParam('token'); + $share = $this->shareManager->getShareByToken($token); + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK + && !$this->isLinkSharingEnabled()) { + throw new NotFoundException('Link sharing is disabled'); + } } } diff --git a/apps/files_sharing/tests/Middleware/SharingCheckMiddlewareTest.php b/apps/files_sharing/tests/Middleware/SharingCheckMiddlewareTest.php index c2965d04b6b..8d7d42722b9 100644 --- a/apps/files_sharing/tests/Middleware/SharingCheckMiddlewareTest.php +++ b/apps/files_sharing/tests/Middleware/SharingCheckMiddlewareTest.php @@ -34,6 +34,9 @@ use OCP\AppFramework\Utility\IControllerMethodReflector; use OCA\Files_Sharing\Exceptions\S2SException; use OCP\AppFramework\Http\JSONResponse; use OCP\IConfig; +use OCP\IRequest; +use OCP\Share\IManager; +use OCP\Share\IShare; /** * @package OCA\Files_Sharing\Middleware\SharingCheckMiddleware @@ -50,6 +53,10 @@ class SharingCheckMiddlewareTest extends \Test\TestCase { private $controllerMock; /** @var IControllerMethodReflector|\PHPUnit_Framework_MockObject_MockObject */ private $reflector; + /** @var IManager | \PHPUnit_Framework_MockObject_MockObject */ + private $shareManager; + /** @var IRequest | \PHPUnit_Framework_MockObject_MockObject */ + private $request; protected function setUp() { parent::setUp(); @@ -58,12 +65,16 @@ class SharingCheckMiddlewareTest extends \Test\TestCase { $this->appManager = $this->createMock(IAppManager::class); $this->controllerMock = $this->createMock(Controller::class); $this->reflector = $this->createMock(IControllerMethodReflector::class); + $this->shareManager = $this->createMock(IManager::class); + $this->request = $this->createMock(IRequest::class); $this->sharingCheckMiddleware = new SharingCheckMiddleware( 'files_sharing', $this->config, $this->appManager, - $this->reflector); + $this->reflector, + $this->shareManager, + $this->request); } public function testIsSharingEnabledWithAppEnabled() { @@ -215,6 +226,9 @@ class SharingCheckMiddlewareTest extends \Test\TestCase { } public function testBeforeControllerWithShareControllerWithSharingEnabled() { + + $share = $this->createMock(IShare::class); + $this->appManager ->expects($this->once()) ->method('isEnabledForUser') @@ -233,6 +247,13 @@ class SharingCheckMiddlewareTest extends \Test\TestCase { ->with('core', 'shareapi_allow_links', 'yes') ->will($this->returnValue('yes')); + $this->request->expects($this->once())->method('getParam')->with('token') + ->willReturn('token'); + $this->shareManager->expects($this->once())->method('getShareByToken') + ->with('token')->willReturn($share); + + $share->expects($this->once())->method('getShareType')->willReturn(\OCP\Share::SHARE_TYPE_LINK); + $controller = $this->createMock(ShareController::class); $this->sharingCheckMiddleware->beforeController($controller, 'myMethod'); @@ -243,6 +264,9 @@ class SharingCheckMiddlewareTest extends \Test\TestCase { * @expectedExceptionMessage Link sharing is disabled */ public function testBeforeControllerWithShareControllerWithSharingEnabledAPIDisabled() { + + $share = $this->createMock(IShare::class); + $this->appManager ->expects($this->once()) ->method('isEnabledForUser') @@ -251,6 +275,14 @@ class SharingCheckMiddlewareTest extends \Test\TestCase { $controller = $this->createMock(ShareController::class); + $this->request->expects($this->once())->method('getParam')->with('token') + ->willReturn('token'); + $this->shareManager->expects($this->once())->method('getShareByToken') + ->with('token')->willReturn($share); + + $share->expects($this->once())->method('getShareType')->willReturn(\OCP\Share::SHARE_TYPE_LINK); + + $this->sharingCheckMiddleware->beforeController($controller, 'myMethod'); } diff --git a/apps/files_trashbin/l10n/ca.js b/apps/files_trashbin/l10n/ca.js index d5f19e466c1..149611cd8d2 100644 --- a/apps/files_trashbin/l10n/ca.js +++ b/apps/files_trashbin/l10n/ca.js @@ -8,9 +8,11 @@ OC.L10N.register( "Delete" : "Esborra", "Delete permanently" : "Esborra permanentment", "Error" : "Error", + "This operation is forbidden" : "Aquesta operació està prohibida", + "This directory is unavailable, please check the logs or contact the administrator" : "Aquesta carpeta no està disponible. Comproveu els registres o contacteu amb l'administrador.", "restored" : "restaurat", - "No deleted files" : "No hi ha cap arxiu eliminat", - "You will be able to recover deleted files from here" : "Des d'aquí es podran recuperar arxius eliminats", + "No deleted files" : "No hi ha cap fitxer eliminat", + "You will be able to recover deleted files from here" : "Des d'aquí es podran recuperar fitxers eliminats", "No entries found in this folder" : "No hi ha entrades en aquesta carpeta", "Select all" : "Seleccionar tot", "Name" : "Nom", diff --git a/apps/files_trashbin/l10n/ca.json b/apps/files_trashbin/l10n/ca.json index 520ef5f2f74..2bd36c79241 100644 --- a/apps/files_trashbin/l10n/ca.json +++ b/apps/files_trashbin/l10n/ca.json @@ -6,9 +6,11 @@ "Delete" : "Esborra", "Delete permanently" : "Esborra permanentment", "Error" : "Error", + "This operation is forbidden" : "Aquesta operació està prohibida", + "This directory is unavailable, please check the logs or contact the administrator" : "Aquesta carpeta no està disponible. Comproveu els registres o contacteu amb l'administrador.", "restored" : "restaurat", - "No deleted files" : "No hi ha cap arxiu eliminat", - "You will be able to recover deleted files from here" : "Des d'aquí es podran recuperar arxius eliminats", + "No deleted files" : "No hi ha cap fitxer eliminat", + "You will be able to recover deleted files from here" : "Des d'aquí es podran recuperar fitxers eliminats", "No entries found in this folder" : "No hi ha entrades en aquesta carpeta", "Select all" : "Seleccionar tot", "Name" : "Nom", diff --git a/apps/files_trashbin/l10n/eo.js b/apps/files_trashbin/l10n/eo.js index 9fd798d8df9..7a4dd0a5e65 100644 --- a/apps/files_trashbin/l10n/eo.js +++ b/apps/files_trashbin/l10n/eo.js @@ -12,6 +12,7 @@ OC.L10N.register( "This directory is unavailable, please check the logs or contact the administrator" : "Ĉi tiu dosierujo maldisponeblas, bonvolu kontroli la protokolojn aŭ kontakti la administranton", "restored" : "restaŭrita", "No deleted files" : "Neniu dosiero foriĝis", + "You will be able to recover deleted files from here" : "Vi eblos restaŭrigi forigitaj dosieroj el tie", "No entries found in this folder" : "Neniu enigo troviĝis en ĉi tiu dosierujo", "Select all" : "Elekti ĉion", "Name" : "Nomo", diff --git a/apps/files_trashbin/l10n/eo.json b/apps/files_trashbin/l10n/eo.json index 92577367e08..2b491222622 100644 --- a/apps/files_trashbin/l10n/eo.json +++ b/apps/files_trashbin/l10n/eo.json @@ -10,6 +10,7 @@ "This directory is unavailable, please check the logs or contact the administrator" : "Ĉi tiu dosierujo maldisponeblas, bonvolu kontroli la protokolojn aŭ kontakti la administranton", "restored" : "restaŭrita", "No deleted files" : "Neniu dosiero foriĝis", + "You will be able to recover deleted files from here" : "Vi eblos restaŭrigi forigitaj dosieroj el tie", "No entries found in this folder" : "Neniu enigo troviĝis en ĉi tiu dosierujo", "Select all" : "Elekti ĉion", "Name" : "Nomo", diff --git a/apps/files_trashbin/l10n/sv.js b/apps/files_trashbin/l10n/sv.js index a022dde5dcb..34e2b2a2f04 100644 --- a/apps/files_trashbin/l10n/sv.js +++ b/apps/files_trashbin/l10n/sv.js @@ -11,7 +11,7 @@ OC.L10N.register( "This operation is forbidden" : "Denna åtgärd är förbjuden", "This directory is unavailable, please check the logs or contact the administrator" : "Denna katalog är inte tillgänglig, kontrollera loggarna eller kontakta administratören", "restored" : "återställd", - "No deleted files" : "Inga borttagna filer", + "No deleted files" : "Inga raderade filer", "You will be able to recover deleted files from here" : "Du kommer kunna återskapa raderade filer härifrån", "No entries found in this folder" : "Inga filer hittades i denna mapp", "Select all" : "Välj allt", diff --git a/apps/files_trashbin/l10n/sv.json b/apps/files_trashbin/l10n/sv.json index 31898a41f5a..1abc0bfba34 100644 --- a/apps/files_trashbin/l10n/sv.json +++ b/apps/files_trashbin/l10n/sv.json @@ -9,7 +9,7 @@ "This operation is forbidden" : "Denna åtgärd är förbjuden", "This directory is unavailable, please check the logs or contact the administrator" : "Denna katalog är inte tillgänglig, kontrollera loggarna eller kontakta administratören", "restored" : "återställd", - "No deleted files" : "Inga borttagna filer", + "No deleted files" : "Inga raderade filer", "You will be able to recover deleted files from here" : "Du kommer kunna återskapa raderade filer härifrån", "No entries found in this folder" : "Inga filer hittades i denna mapp", "Select all" : "Välj allt", diff --git a/apps/files_versions/l10n/eo.js b/apps/files_versions/l10n/eo.js index 831e984a885..3ef246ace2a 100644 --- a/apps/files_versions/l10n/eo.js +++ b/apps/files_versions/l10n/eo.js @@ -5,6 +5,7 @@ OC.L10N.register( "Versions" : "Versioj", "Failed to revert {file} to revision {timestamp}." : "Malsukcesis returnigo de {file} al la revizio {timestamp}.", "Restore" : "Restaŭri", + "No versions available" : "Neniu versio disponebla", "More versions..." : "Pli da versioj...", "No other versions available" : "Ne disponeblas aliaj versioj" }, diff --git a/apps/files_versions/l10n/eo.json b/apps/files_versions/l10n/eo.json index 751a6e02bf5..f91aafe4d84 100644 --- a/apps/files_versions/l10n/eo.json +++ b/apps/files_versions/l10n/eo.json @@ -3,6 +3,7 @@ "Versions" : "Versioj", "Failed to revert {file} to revision {timestamp}." : "Malsukcesis returnigo de {file} al la revizio {timestamp}.", "Restore" : "Restaŭri", + "No versions available" : "Neniu versio disponebla", "More versions..." : "Pli da versioj...", "No other versions available" : "Ne disponeblas aliaj versioj" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/files_versions/l10n/sv.js b/apps/files_versions/l10n/sv.js index 7bccb3c43eb..e33aae97a2f 100644 --- a/apps/files_versions/l10n/sv.js +++ b/apps/files_versions/l10n/sv.js @@ -3,7 +3,7 @@ OC.L10N.register( { "Could not revert: %s" : "Kunde inte återställa: %s", "Versions" : "Versioner", - "Failed to revert {file} to revision {timestamp}." : "Kunde inte återställa {file} till revision {timestamp}.", + "Failed to revert {file} to revision {timestamp}." : "Kunde inte återställa {file} till {timestamp}.", "_%n byte_::_%n bytes_" : ["%n byte","%n bytes"], "Restore" : "Återskapa", "No versions available" : "Inga versioner tillgängliga", diff --git a/apps/files_versions/l10n/sv.json b/apps/files_versions/l10n/sv.json index 9cfba181e1b..5e74937879d 100644 --- a/apps/files_versions/l10n/sv.json +++ b/apps/files_versions/l10n/sv.json @@ -1,7 +1,7 @@ { "translations": { "Could not revert: %s" : "Kunde inte återställa: %s", "Versions" : "Versioner", - "Failed to revert {file} to revision {timestamp}." : "Kunde inte återställa {file} till revision {timestamp}.", + "Failed to revert {file} to revision {timestamp}." : "Kunde inte återställa {file} till {timestamp}.", "_%n byte_::_%n bytes_" : ["%n byte","%n bytes"], "Restore" : "Återskapa", "No versions available" : "Inga versioner tillgängliga", diff --git a/apps/systemtags/l10n/sv.js b/apps/systemtags/l10n/sv.js index 3da131bbbd3..a91610dd452 100644 --- a/apps/systemtags/l10n/sv.js +++ b/apps/systemtags/l10n/sv.js @@ -9,7 +9,7 @@ OC.L10N.register( "Select tags to filter by" : "Välj taggar att filtrera med", "No tags found" : "Inga taggar hittades", "Please select tags to filter by" : "Vänligen välj taggar att filtrera med", - "No files found for the selected tags" : "Inga filer hittade för de valda taggarna", + "No files found for the selected tags" : "Inga filer hittades för de valda taggarna", "Added system tag %1$s" : "La till systemtagg %1$s", "Added system tag {systemtag}" : "La till systemtagg {systemtag}", "%1$s added system tag %2$s" : "%1$s la till systemtagg %2$s", @@ -41,23 +41,23 @@ OC.L10N.register( "%s (restricted)" : "%s (begränsad)", "%s (invisible)" : "%s (osynlig)", "<strong>System tags</strong> for a file have been modified" : "<strong>Systemtaggar</strong> för en fil har blivit ändrade", - "Collaborative tags" : "Samverkade taggar", + "Collaborative tags" : "Samverkande taggar", "Name" : "Namn", "Delete" : "Radera", - "Public" : "Publikt", + "Public" : "Offentlig", "Restricted" : "Begränsad", "Invisible" : "Osynlig", "Reset" : "Återställ", - "No files in here" : "Inga filer kunde hittas", - "No entries found in this folder" : "Inga filer hittades i denna mapp", + "No files in here" : "Inga filer här inne", + "No entries found in this folder" : "Ingenting hittades i denna mapp", "Size" : "Storlek", - "Modified" : "Ändrad", + "Modified" : "Modifierad", "You assigned system tag %3$s" : "Du tilldelade systemtagg %3$s", "%1$s assigned system tag %3$s" : "%1$s blev tilldelad systemtagg %3$s", "You unassigned system tag %3$s" : "Du tog bort tilldelning av systemtag %3$s", "%1$s unassigned system tag %3$s" : "%1$s tog bort tilldelad systemtagg %3$s", "You created system tag %2$s" : "Du skapade systemtagg %2$s", - "You deleted system tag %2$s" : "Du tog bort systemtagg %2$s", + "You deleted system tag %2$s" : "Du raderade systemtagg %2$s", "You updated system tag %3$s to %2$s" : "Du uppdaterade systemtagg %3$s till %2$s", "You assigned system tag %3$s to %2$s" : "Du tilldelade systemtagg %3$s till %2$s", "%1$s assigned system tag %3$s to %2$s" : "%1$s tilldelade systemtagg %3$s till %2$s", diff --git a/apps/systemtags/l10n/sv.json b/apps/systemtags/l10n/sv.json index bcf27fa8730..df64bff3ffe 100644 --- a/apps/systemtags/l10n/sv.json +++ b/apps/systemtags/l10n/sv.json @@ -7,7 +7,7 @@ "Select tags to filter by" : "Välj taggar att filtrera med", "No tags found" : "Inga taggar hittades", "Please select tags to filter by" : "Vänligen välj taggar att filtrera med", - "No files found for the selected tags" : "Inga filer hittade för de valda taggarna", + "No files found for the selected tags" : "Inga filer hittades för de valda taggarna", "Added system tag %1$s" : "La till systemtagg %1$s", "Added system tag {systemtag}" : "La till systemtagg {systemtag}", "%1$s added system tag %2$s" : "%1$s la till systemtagg %2$s", @@ -39,23 +39,23 @@ "%s (restricted)" : "%s (begränsad)", "%s (invisible)" : "%s (osynlig)", "<strong>System tags</strong> for a file have been modified" : "<strong>Systemtaggar</strong> för en fil har blivit ändrade", - "Collaborative tags" : "Samverkade taggar", + "Collaborative tags" : "Samverkande taggar", "Name" : "Namn", "Delete" : "Radera", - "Public" : "Publikt", + "Public" : "Offentlig", "Restricted" : "Begränsad", "Invisible" : "Osynlig", "Reset" : "Återställ", - "No files in here" : "Inga filer kunde hittas", - "No entries found in this folder" : "Inga filer hittades i denna mapp", + "No files in here" : "Inga filer här inne", + "No entries found in this folder" : "Ingenting hittades i denna mapp", "Size" : "Storlek", - "Modified" : "Ändrad", + "Modified" : "Modifierad", "You assigned system tag %3$s" : "Du tilldelade systemtagg %3$s", "%1$s assigned system tag %3$s" : "%1$s blev tilldelad systemtagg %3$s", "You unassigned system tag %3$s" : "Du tog bort tilldelning av systemtag %3$s", "%1$s unassigned system tag %3$s" : "%1$s tog bort tilldelad systemtagg %3$s", "You created system tag %2$s" : "Du skapade systemtagg %2$s", - "You deleted system tag %2$s" : "Du tog bort systemtagg %2$s", + "You deleted system tag %2$s" : "Du raderade systemtagg %2$s", "You updated system tag %3$s to %2$s" : "Du uppdaterade systemtagg %3$s till %2$s", "You assigned system tag %3$s to %2$s" : "Du tilldelade systemtagg %3$s till %2$s", "%1$s assigned system tag %3$s to %2$s" : "%1$s tilldelade systemtagg %3$s till %2$s", diff --git a/apps/twofactor_backupcodes/l10n/sv.js b/apps/twofactor_backupcodes/l10n/sv.js index 623282586d6..2c4f3a40ac8 100644 --- a/apps/twofactor_backupcodes/l10n/sv.js +++ b/apps/twofactor_backupcodes/l10n/sv.js @@ -1,7 +1,7 @@ OC.L10N.register( "twofactor_backupcodes", { - "Generate backup codes" : "Skapa återställningskoder", + "Generate backup codes" : "Generera återställningsnycklar", "Backup codes have been generated. {{used}} of {{total}} codes have been used." : "Återställningsnycklarna har skapats. {{used}} av {{total}} nycklar har använts.", "These are your backup codes. Please save and/or print them as you will not be able to read the codes again later" : "Detta är dina återställningsnycklar. Vänligen spara och/eller skriv ut dem eftersom du inte kommer kunna se dessa nycklar igen.", "Save backup codes" : "Spara återställningsnycklar", @@ -12,10 +12,10 @@ OC.L10N.register( "Nextcloud backup codes" : "Nextcloud återställningsnycklar", "Two-factor authentication" : "Tvåfaktorsautentisering", "You successfully logged in using two-factor authentication (%1$s)" : "Du lyckades logga in genom att använda tvåfaktorsautentisering (%1$s)", - "A login attempt using two-factor authentication failed (%1$s)" : "Du kunde EJ logga in med tvåfaktorsautentisering (%1$s)", + "A login attempt using two-factor authentication failed (%1$s)" : "Ett inloggningsförsök med tvåfaktorsautentisering misslyckades (%1$s)", "You created two-factor backup codes for your account" : "Du skapade återställningsnycklar till tvåfaktorsautentiseringen på ditt användarkonto.", "Backup code" : "Återställningsnyckel", "Use backup code" : "Använd återställningsnyckel", - "Second-factor backup codes" : "Återställningsnycklar till tvåfaktorsautentisering" + "Second-factor backup codes" : "Tvåfaktorsautentisering" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/twofactor_backupcodes/l10n/sv.json b/apps/twofactor_backupcodes/l10n/sv.json index e75e2859653..096f04f0f8f 100644 --- a/apps/twofactor_backupcodes/l10n/sv.json +++ b/apps/twofactor_backupcodes/l10n/sv.json @@ -1,5 +1,5 @@ { "translations": { - "Generate backup codes" : "Skapa återställningskoder", + "Generate backup codes" : "Generera återställningsnycklar", "Backup codes have been generated. {{used}} of {{total}} codes have been used." : "Återställningsnycklarna har skapats. {{used}} av {{total}} nycklar har använts.", "These are your backup codes. Please save and/or print them as you will not be able to read the codes again later" : "Detta är dina återställningsnycklar. Vänligen spara och/eller skriv ut dem eftersom du inte kommer kunna se dessa nycklar igen.", "Save backup codes" : "Spara återställningsnycklar", @@ -10,10 +10,10 @@ "Nextcloud backup codes" : "Nextcloud återställningsnycklar", "Two-factor authentication" : "Tvåfaktorsautentisering", "You successfully logged in using two-factor authentication (%1$s)" : "Du lyckades logga in genom att använda tvåfaktorsautentisering (%1$s)", - "A login attempt using two-factor authentication failed (%1$s)" : "Du kunde EJ logga in med tvåfaktorsautentisering (%1$s)", + "A login attempt using two-factor authentication failed (%1$s)" : "Ett inloggningsförsök med tvåfaktorsautentisering misslyckades (%1$s)", "You created two-factor backup codes for your account" : "Du skapade återställningsnycklar till tvåfaktorsautentiseringen på ditt användarkonto.", "Backup code" : "Återställningsnyckel", "Use backup code" : "Använd återställningsnyckel", - "Second-factor backup codes" : "Återställningsnycklar till tvåfaktorsautentisering" + "Second-factor backup codes" : "Tvåfaktorsautentisering" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/updatenotification/l10n/sv.js b/apps/updatenotification/l10n/sv.js index 801ca639892..86a3cf7d587 100644 --- a/apps/updatenotification/l10n/sv.js +++ b/apps/updatenotification/l10n/sv.js @@ -10,15 +10,15 @@ OC.L10N.register( "Update for {app} to version %s is available." : "Uppdatering för {app} till version %s är tillgänglig.", "A new version is available: %s" : "En ny version är tillgänglig: %s", "Open updater" : "Öppna uppdateraren", - "Download now" : "Ladda ner nu", + "Download now" : "Ladda ned nu", "Your version is up to date." : "Din version är uppdaterad.", "Checked on %s" : "Senast kontrollerad %s", "Update channel:" : "Uppdateringskanal:", "You can always update to a newer version / experimental channel. But you can never downgrade to a more stable channel." : "Du kan alltid uppdatera till en nyare version / experimentell kanal. Men du kan aldrig nedgradera till en mer stabil kanal.", "Notify members of the following groups about available updates:" : "Notifiera medlemmar i följande grupper om tillgängliga uppdateraingar:", - "Only notification for app updates are available." : "Endast notifikation för app-uppdateringar är tillgängliga", + "Only notification for app updates are available." : "Endast notifikation för app-uppdateringar är tillgängliga.", "The selected update channel makes dedicated notifications for the server obsolete." : "Den valda uppdateringskanalen gör dedikerade notiser för servern förlegade.", - "The selected update channel does not support updates of the server." : "Den valda uppdateringskanalen stödjer inte uppdateringar för servern", + "The selected update channel does not support updates of the server." : "Den valda uppdateringskanalen stödjer inte uppdateringar för servern.", "You are running PHP %s. To allow you to upgrade to Nextcloud 11 and higher you need to run at least PHP 5.6. Once you upgraded your PHP version you will be able to receive update notifications for these newer versions." : "Du använder PHP %s. För att kunna uppgradera till Nextcloud 11 och högre så behöver du minst använda PHP 5.6 . När du har uppgraderat din PHP version kommer du att kunna ta emot uppdateringsnotifikationer för de nyare versionerna." }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/updatenotification/l10n/sv.json b/apps/updatenotification/l10n/sv.json index afdcecd1212..a3a0e95079a 100644 --- a/apps/updatenotification/l10n/sv.json +++ b/apps/updatenotification/l10n/sv.json @@ -8,15 +8,15 @@ "Update for {app} to version %s is available." : "Uppdatering för {app} till version %s är tillgänglig.", "A new version is available: %s" : "En ny version är tillgänglig: %s", "Open updater" : "Öppna uppdateraren", - "Download now" : "Ladda ner nu", + "Download now" : "Ladda ned nu", "Your version is up to date." : "Din version är uppdaterad.", "Checked on %s" : "Senast kontrollerad %s", "Update channel:" : "Uppdateringskanal:", "You can always update to a newer version / experimental channel. But you can never downgrade to a more stable channel." : "Du kan alltid uppdatera till en nyare version / experimentell kanal. Men du kan aldrig nedgradera till en mer stabil kanal.", "Notify members of the following groups about available updates:" : "Notifiera medlemmar i följande grupper om tillgängliga uppdateraingar:", - "Only notification for app updates are available." : "Endast notifikation för app-uppdateringar är tillgängliga", + "Only notification for app updates are available." : "Endast notifikation för app-uppdateringar är tillgängliga.", "The selected update channel makes dedicated notifications for the server obsolete." : "Den valda uppdateringskanalen gör dedikerade notiser för servern förlegade.", - "The selected update channel does not support updates of the server." : "Den valda uppdateringskanalen stödjer inte uppdateringar för servern", + "The selected update channel does not support updates of the server." : "Den valda uppdateringskanalen stödjer inte uppdateringar för servern.", "You are running PHP %s. To allow you to upgrade to Nextcloud 11 and higher you need to run at least PHP 5.6. Once you upgraded your PHP version you will be able to receive update notifications for these newer versions." : "Du använder PHP %s. För att kunna uppgradera till Nextcloud 11 och högre så behöver du minst använda PHP 5.6 . När du har uppgraderat din PHP version kommer du att kunna ta emot uppdateringsnotifikationer för de nyare versionerna." },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/updatenotification/l10n/zh_CN.js b/apps/updatenotification/l10n/zh_CN.js index 3c18f5981a3..ddfe6179139 100644 --- a/apps/updatenotification/l10n/zh_CN.js +++ b/apps/updatenotification/l10n/zh_CN.js @@ -1,14 +1,24 @@ OC.L10N.register( "updatenotification", { + "Update notifications" : "升级通知", + "Could not start updater, please try the manual update" : "无法启动自动更新,请尝试手动更新", "{version} is available. Get more information on how to update." : "新版本 {version} 已可以使用。获取更多升级相关信息。", - "Updated channel" : "更新通道", - "Updater" : "更新管理器", + "Channel updated" : "更新通道", + "Update to %1$s is available." : " 可以更新到 %1$s 。", + "Update for %1$s to version %2$s is available." : "可以从 %1$s 版本更新到 %2$s 版本。", + "Update for {app} to version %s is available." : "可以将您的 {app} 更新到版本 %s 。", "A new version is available: %s" : "有可用的新版本: %s", - "Open updater" : "打开更新管理器", + "Open updater" : "打开更新器", + "Download now" : "开始下载", "Your version is up to date." : "您的版本已是最新。", "Checked on %s" : "检查于 %s", "Update channel:" : "更新通道:", - "You can always update to a newer version / experimental channel. But you can never downgrade to a more stable channel." : "您可以随时更新到新版本 / 实验通道。但你永远不能降级到更稳定的通道。" + "You can always update to a newer version / experimental channel. But you can never downgrade to a more stable channel." : "您可以随时更新到新版本 / 实验通道。但你永远不能降级到更稳定的通道。", + "Notify members of the following groups about available updates:" : "提醒一下组群的用户关于可用的更新:", + "Only notification for app updates are available." : "仅提醒应用更新就绪。", + "The selected update channel makes dedicated notifications for the server obsolete." : "被选中的升级通道将会通知未更新的服务器", + "The selected update channel does not support updates of the server." : "选中的更新通道不支持服务器升级。", + "You are running PHP %s. To allow you to upgrade to Nextcloud 11 and higher you need to run at least PHP 5.6. Once you upgraded your PHP version you will be able to receive update notifications for these newer versions." : "您在使用PHP %s. 如果要使用 Nextcloud 11 您至少要使用 PHP 5.6。如果您升级了您的 PHP 版本您将不会收到这些提示您更新更高版本的信息提示。" }, "nplurals=1; plural=0;"); diff --git a/apps/updatenotification/l10n/zh_CN.json b/apps/updatenotification/l10n/zh_CN.json index 47bba0c492b..fc886540cb4 100644 --- a/apps/updatenotification/l10n/zh_CN.json +++ b/apps/updatenotification/l10n/zh_CN.json @@ -1,12 +1,22 @@ { "translations": { + "Update notifications" : "升级通知", + "Could not start updater, please try the manual update" : "无法启动自动更新,请尝试手动更新", "{version} is available. Get more information on how to update." : "新版本 {version} 已可以使用。获取更多升级相关信息。", - "Updated channel" : "更新通道", - "Updater" : "更新管理器", + "Channel updated" : "更新通道", + "Update to %1$s is available." : " 可以更新到 %1$s 。", + "Update for %1$s to version %2$s is available." : "可以从 %1$s 版本更新到 %2$s 版本。", + "Update for {app} to version %s is available." : "可以将您的 {app} 更新到版本 %s 。", "A new version is available: %s" : "有可用的新版本: %s", - "Open updater" : "打开更新管理器", + "Open updater" : "打开更新器", + "Download now" : "开始下载", "Your version is up to date." : "您的版本已是最新。", "Checked on %s" : "检查于 %s", "Update channel:" : "更新通道:", - "You can always update to a newer version / experimental channel. But you can never downgrade to a more stable channel." : "您可以随时更新到新版本 / 实验通道。但你永远不能降级到更稳定的通道。" + "You can always update to a newer version / experimental channel. But you can never downgrade to a more stable channel." : "您可以随时更新到新版本 / 实验通道。但你永远不能降级到更稳定的通道。", + "Notify members of the following groups about available updates:" : "提醒一下组群的用户关于可用的更新:", + "Only notification for app updates are available." : "仅提醒应用更新就绪。", + "The selected update channel makes dedicated notifications for the server obsolete." : "被选中的升级通道将会通知未更新的服务器", + "The selected update channel does not support updates of the server." : "选中的更新通道不支持服务器升级。", + "You are running PHP %s. To allow you to upgrade to Nextcloud 11 and higher you need to run at least PHP 5.6. Once you upgraded your PHP version you will be able to receive update notifications for these newer versions." : "您在使用PHP %s. 如果要使用 Nextcloud 11 您至少要使用 PHP 5.6。如果您升级了您的 PHP 版本您将不会收到这些提示您更新更高版本的信息提示。" },"pluralForm" :"nplurals=1; plural=0;" }
\ No newline at end of file diff --git a/apps/updatenotification/lib/Notification/BackgroundJob.php b/apps/updatenotification/lib/Notification/BackgroundJob.php index 7bcc0e86905..83a9bdb599a 100644 --- a/apps/updatenotification/lib/Notification/BackgroundJob.php +++ b/apps/updatenotification/lib/Notification/BackgroundJob.php @@ -30,8 +30,6 @@ use OCP\Http\Client\IClientService; use OCP\IConfig; use OCP\IGroup; use OCP\IGroupManager; -use OCP\IURLGenerator; -use OCP\IUser; use OCP\Notification\IManager; class BackgroundJob extends TimedJob { @@ -51,10 +49,7 @@ class BackgroundJob extends TimedJob { /** @var IClientService */ protected $client; - /** @var IURLGenerator */ - protected $urlGenerator; - - /** @var IUser[] */ + /** @var string[] */ protected $users; /** @@ -65,9 +60,8 @@ class BackgroundJob extends TimedJob { * @param IGroupManager $groupManager * @param IAppManager $appManager * @param IClientService $client - * @param IURLGenerator $urlGenerator */ - public function __construct(IConfig $config, IManager $notificationManager, IGroupManager $groupManager, IAppManager $appManager, IClientService $client, IURLGenerator $urlGenerator) { + public function __construct(IConfig $config, IManager $notificationManager, IGroupManager $groupManager, IAppManager $appManager, IClientService $client) { // Run once a day $this->setInterval(60 * 60 * 24); @@ -76,7 +70,6 @@ class BackgroundJob extends TimedJob { $this->groupManager = $groupManager; $this->appManager = $appManager; $this->client = $client; - $this->urlGenerator = $urlGenerator; } protected function run($argument) { @@ -97,8 +90,7 @@ class BackgroundJob extends TimedJob { $status = $updater->check(); if (isset($status['version'])) { - $url = $this->urlGenerator->linkToRouteAbsolute('settings.AdminSettings.index') . '#updater'; - $this->createNotifications('core', $status['version'], $url, $status['versionstring']); + $this->createNotifications('core', $status['version'], $status['versionstring']); } } @@ -110,8 +102,7 @@ class BackgroundJob extends TimedJob { foreach ($apps as $app) { $update = $this->isUpdateAvailable($app); if ($update !== false) { - $url = $this->urlGenerator->linkToRouteAbsolute('settings.AppSettings.viewApps') . '#app-' . $app; - $this->createNotifications($app, $update, $url); + $this->createNotifications($app, $update); } } } @@ -121,10 +112,9 @@ class BackgroundJob extends TimedJob { * * @param string $app * @param string $version - * @param string $url * @param string $visibleVersion */ - protected function createNotifications($app, $version, $url, $visibleVersion = '') { + protected function createNotifications($app, $version, $visibleVersion = '') { $lastNotification = $this->config->getAppValue('updatenotification', $app, false); if ($lastNotification === $version) { // We already notified about this update @@ -138,8 +128,7 @@ class BackgroundJob extends TimedJob { $notification = $this->notificationManager->createNotification(); $notification->setApp('updatenotification') ->setDateTime(new \DateTime()) - ->setObject($app, $version) - ->setLink($url); + ->setObject($app, $version); if ($visibleVersion !== '') { $notification->setSubject('update_available', ['version' => $visibleVersion]); diff --git a/apps/updatenotification/lib/Notification/Notifier.php b/apps/updatenotification/lib/Notification/Notifier.php index 00cc94095ca..079ec4c5e0a 100644 --- a/apps/updatenotification/lib/Notification/Notifier.php +++ b/apps/updatenotification/lib/Notification/Notifier.php @@ -24,7 +24,10 @@ namespace OCA\UpdateNotification\Notification; +use OCP\IGroupManager; use OCP\IURLGenerator; +use OCP\IUser; +use OCP\IUserSession; use OCP\L10N\IFactory; use OCP\Notification\IManager; use OCP\Notification\INotification; @@ -41,6 +44,12 @@ class Notifier implements INotifier { /** @var IFactory */ protected $l10NFactory; + /** @var IUserSession */ + protected $userSession; + + /** @var IGroupManager */ + protected $groupManager; + /** @var string[] */ protected $appVersions; @@ -50,11 +59,15 @@ class Notifier implements INotifier { * @param IURLGenerator $url * @param IManager $notificationManager * @param IFactory $l10NFactory + * @param IUserSession $userSession + * @param IGroupManager $groupManager */ - public function __construct(IURLGenerator $url, IManager $notificationManager, IFactory $l10NFactory) { + public function __construct(IURLGenerator $url, IManager $notificationManager, IFactory $l10NFactory, IUserSession $userSession, IGroupManager $groupManager) { $this->url = $url; $this->notificationManager = $notificationManager; $this->l10NFactory = $l10NFactory; + $this->userSession = $userSession; + $this->groupManager = $groupManager; $this->appVersions = $this->getAppVersions(); } @@ -76,6 +89,10 @@ class Notifier implements INotifier { $parameters = $notification->getSubjectParameters(); $notification->setParsedSubject($l->t('Update to %1$s is available.', [$parameters['version']])); + + if ($this->isAdmin()) { + $notification->setLink($this->url->linkToRouteAbsolute('settings.AdminSettings.index') . '#updater'); + } } else { $appInfo = $this->getAppInfo($notification->getObjectType()); $appName = ($appInfo === null) ? $notification->getObjectType() : $appInfo['name']; @@ -92,6 +109,10 @@ class Notifier implements INotifier { 'name' => $appName, ] ]); + + if ($this->isAdmin()) { + $notification->setLink($this->url->linkToRouteAbsolute('settings.AppSettings.viewApps') . '#app-' . $notification->getObjectType()); + } } $notification->setIcon($this->url->getAbsoluteURL($this->url->imagePath('updatenotification', 'notification.svg'))); @@ -113,6 +134,19 @@ class Notifier implements INotifier { } } + /** + * @return bool + */ + protected function isAdmin() { + $user = $this->userSession->getUser(); + + if ($user instanceof IUser) { + return $this->groupManager->isAdmin($user->getUID()); + } + + return false; + } + protected function getCoreVersions() { return implode('.', \OCP\Util::getVersion()); } diff --git a/apps/updatenotification/tests/Notification/BackgroundJobTest.php b/apps/updatenotification/tests/Notification/BackgroundJobTest.php index 911b1cc8e2f..57771ec0ae9 100644 --- a/apps/updatenotification/tests/Notification/BackgroundJobTest.php +++ b/apps/updatenotification/tests/Notification/BackgroundJobTest.php @@ -45,18 +45,15 @@ class BackgroundJobTest extends TestCase { protected $appManager; /** @var IClientService|\PHPUnit_Framework_MockObject_MockObject */ protected $client; - /** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */ - protected $urlGenerator; public function setUp() { parent::setUp(); - $this->config = $this->getMockBuilder('OCP\IConfig')->getMock(); - $this->notificationManager = $this->getMockBuilder('OCP\Notification\IManager')->getMock(); - $this->groupManager = $this->getMockBuilder('OCP\IGroupManager')->getMock(); - $this->appManager = $this->getMockBuilder('OCP\App\IAppManager')->getMock(); - $this->client = $this->getMockBuilder('OCP\Http\Client\IClientService')->getMock(); - $this->urlGenerator = $this->getMockBuilder('OCP\IURLGenerator')->getMock(); + $this->config = $this->createMock(\OCP\IConfig::class); + $this->notificationManager = $this->createMock(\OCP\Notification\IManager::class); + $this->groupManager = $this->createMock(\OCP\IGroupManager::class); + $this->appManager = $this->createMock(\OCP\App\IAppManager::class); + $this->client = $this->createMock(\OCP\Http\Client\IClientService::class); } /** @@ -70,8 +67,7 @@ class BackgroundJobTest extends TestCase { $this->notificationManager, $this->groupManager, $this->appManager, - $this->client, - $this->urlGenerator + $this->client ); } { return $this->getMockBuilder('OCA\UpdateNotification\Notification\BackgroundJob') @@ -81,7 +77,6 @@ class BackgroundJobTest extends TestCase { $this->groupManager, $this->appManager, $this->client, - $this->urlGenerator, ]) ->setMethods($methods) ->getMock(); @@ -160,20 +155,12 @@ class BackgroundJobTest extends TestCase { } if ($notification === null) { - $this->urlGenerator->expects($this->never()) - ->method('linkToRouteAbsolute'); - $job->expects($this->never()) ->method('createNotifications'); } else { - $this->urlGenerator->expects($this->once()) - ->method('linkToRouteAbsolute') - ->with('settings.AdminSettings.index') - ->willReturn('admin-url'); - $job->expects($this->once()) ->method('createNotifications') - ->willReturn('core', $notification, 'admin-url#updater', $readableVersion); + ->willReturn('core', $notification, $readableVersion); } $this->invokePrivate($job, 'checkCoreUpdate'); @@ -188,7 +175,7 @@ class BackgroundJobTest extends TestCase { ['app2', '1.9.2'], ], [ - ['app2', '1.9.2', 'apps-url#app-app2'], + ['app2', '1.9.2'], ], ], ]; @@ -215,11 +202,6 @@ class BackgroundJobTest extends TestCase { ->method('isUpdateAvailable') ->willReturnMap($isUpdateAvailable); - $this->urlGenerator->expects($this->exactly(sizeof($notifications))) - ->method('linkToRouteAbsolute') - ->with('settings.AppSettings.viewApps') - ->willReturn('apps-url'); - $mockedMethod = $job->expects($this->exactly(sizeof($notifications))) ->method('createNotifications'); call_user_func_array([$mockedMethod, 'withConsecutive'], $notifications); @@ -229,9 +211,9 @@ class BackgroundJobTest extends TestCase { public function dataCreateNotifications() { return [ - ['app1', '1.0.0', 'link1', '1.0.0', false, false, null, null], - ['app2', '1.0.1', 'link2', '1.0.0', '1.0.0', true, ['user1'], [['user1']]], - ['app3', '1.0.1', 'link3', false, false, true, ['user2', 'user3'], [['user2'], ['user3']]], + ['app1', '1.0.0', '1.0.0', false, false, null, null], + ['app2', '1.0.1', '1.0.0', '1.0.0', true, ['user1'], [['user1']]], + ['app3', '1.0.1', false, false, true, ['user2', 'user3'], [['user2'], ['user3']]], ]; } @@ -240,14 +222,13 @@ class BackgroundJobTest extends TestCase { * * @param string $app * @param string $version - * @param string $url * @param string|false $lastNotification * @param string|false $callDelete * @param bool $createNotification * @param string[]|null $users * @param array|null $userNotifications */ - public function testCreateNotifications($app, $version, $url, $lastNotification, $callDelete, $createNotification, $users, $userNotifications) { + public function testCreateNotifications($app, $version, $lastNotification, $callDelete, $createNotification, $users, $userNotifications) { $job = $this->getJob([ 'deleteOutdatedNotifications', 'getUsersToNotify', @@ -299,10 +280,6 @@ class BackgroundJobTest extends TestCase { ->method('setSubject') ->with('update_available') ->willReturnSelf(); - $notification->expects($this->once()) - ->method('setLink') - ->with($url) - ->willReturnSelf(); if ($userNotifications !== null) { $mockedMethod = $notification->expects($this->exactly(sizeof($userNotifications))) @@ -323,7 +300,7 @@ class BackgroundJobTest extends TestCase { ->method('createNotification'); } - $this->invokePrivate($job, 'createNotifications', [$app, $version, $url]); + $this->invokePrivate($job, 'createNotifications', [$app, $version]); } public function dataGetUsersToNotify() { diff --git a/apps/updatenotification/tests/Notification/NotifierTest.php b/apps/updatenotification/tests/Notification/NotifierTest.php index 421fcada689..e809ce11635 100644 --- a/apps/updatenotification/tests/Notification/NotifierTest.php +++ b/apps/updatenotification/tests/Notification/NotifierTest.php @@ -24,7 +24,9 @@ namespace OCA\UpdateNotification\Tests\Notification; use OCA\UpdateNotification\Notification\Notifier; +use OCP\IGroupManager; use OCP\IURLGenerator; +use OCP\IUserSession; use OCP\L10N\IFactory; use OCP\Notification\IManager; use OCP\Notification\INotification; @@ -38,6 +40,10 @@ class NotifierTest extends TestCase { protected $notificationManager; /** @var IFactory|\PHPUnit_Framework_MockObject_MockObject */ protected $l10nFactory; + /** @var IUserSession|\PHPUnit_Framework_MockObject_MockObject */ + protected $userSession; + /** @var IGroupManager|\PHPUnit_Framework_MockObject_MockObject */ + protected $groupManager; public function setUp() { parent::setUp(); @@ -45,6 +51,8 @@ class NotifierTest extends TestCase { $this->urlGenerator = $this->createMock(IURLGenerator::class); $this->notificationManager = $this->createMock(IManager::class); $this->l10nFactory = $this->createMock(IFactory::class); + $this->userSession = $this->createMock(IUserSession::class); + $this->groupManager = $this->createMock(IGroupManager::class); } /** @@ -56,7 +64,9 @@ class NotifierTest extends TestCase { return new Notifier( $this->urlGenerator, $this->notificationManager, - $this->l10nFactory + $this->l10nFactory, + $this->userSession, + $this->groupManager ); } { return $this->getMockBuilder(Notifier::class) @@ -64,6 +74,8 @@ class NotifierTest extends TestCase { $this->urlGenerator, $this->notificationManager, $this->l10nFactory, + $this->userSession, + $this->groupManager, ]) ->setMethods($methods) ->getMock(); diff --git a/apps/user_ldap/l10n/de_DE.js b/apps/user_ldap/l10n/de_DE.js index 138cc30bbef..3c0de5ea9a5 100644 --- a/apps/user_ldap/l10n/de_DE.js +++ b/apps/user_ldap/l10n/de_DE.js @@ -48,7 +48,7 @@ OC.L10N.register( "Please provide a login name to test against" : "Bitte geben Sie einen Benutzernamen an, um gegen diesen zu testen", "The group box was disabled, because the LDAP / AD server does not support memberOf." : "Das Gruppenfeld wurde deaktiviert, da der LDAP / AD-Server memberOf nicht unterstützt.", "Password change rejected. Hint: " : "Passwortändertung verweigert. Hinweis:", - "LDAP / AD integration" : "LDAP / AD Integration", + "LDAP / AD integration" : "LDAP/AD-Integration", "_%s group found_::_%s groups found_" : ["%s Gruppe gefunden","%s Gruppen gefunden"], "_%s user found_::_%s users found_" : ["%s Benutzer gefunden","%s Benutzer gefunden"], "Could not detect user display name attribute. Please specify it yourself in advanced ldap settings." : "Das Anzeigename-Attribut des Benutzers konnte nicht gefunden werden. Bitte geben Sie es in den erweiterten LDAP-Einstellungen selber an.", diff --git a/apps/user_ldap/l10n/de_DE.json b/apps/user_ldap/l10n/de_DE.json index 2b4f5a6803d..2bee5da66de 100644 --- a/apps/user_ldap/l10n/de_DE.json +++ b/apps/user_ldap/l10n/de_DE.json @@ -46,7 +46,7 @@ "Please provide a login name to test against" : "Bitte geben Sie einen Benutzernamen an, um gegen diesen zu testen", "The group box was disabled, because the LDAP / AD server does not support memberOf." : "Das Gruppenfeld wurde deaktiviert, da der LDAP / AD-Server memberOf nicht unterstützt.", "Password change rejected. Hint: " : "Passwortändertung verweigert. Hinweis:", - "LDAP / AD integration" : "LDAP / AD Integration", + "LDAP / AD integration" : "LDAP/AD-Integration", "_%s group found_::_%s groups found_" : ["%s Gruppe gefunden","%s Gruppen gefunden"], "_%s user found_::_%s users found_" : ["%s Benutzer gefunden","%s Benutzer gefunden"], "Could not detect user display name attribute. Please specify it yourself in advanced ldap settings." : "Das Anzeigename-Attribut des Benutzers konnte nicht gefunden werden. Bitte geben Sie es in den erweiterten LDAP-Einstellungen selber an.", diff --git a/apps/user_ldap/l10n/sv.js b/apps/user_ldap/l10n/sv.js index 44a95044161..aba8488f0d1 100644 --- a/apps/user_ldap/l10n/sv.js +++ b/apps/user_ldap/l10n/sv.js @@ -22,7 +22,7 @@ OC.L10N.register( "Please check the credentials, they seem to be wrong." : "Var god kontrollera dina uppgifter, de verkar vara fel.", "Please specify the port, it could not be auto-detected." : "Var god specificera en port, den kunde ej autodetekteras.", "Base DN could not be auto-detected, please revise credentials, host and port." : "Grundläggande DN kunde ej autodetekteras, var god dubbelkontrollera dina uppgifter, värd och port.", - "Could not detect Base DN, please enter it manually." : "Kunde ej upptäcka Base DN, var god ange det manuellt.", + "Could not detect Base DN, please enter it manually." : "Kunde inte upptäcka Base DN, var god ange det manuellt.", "{nthServer}. Server" : "{nthServer}. Server", "No object found in the given Base DN. Please revise." : "Inget objekt funnet i den givna Base DN. Var god granska.", "More than 1,000 directory entries available." : "Mer än 1,000 katalogposter tillgängliga.", @@ -32,20 +32,18 @@ OC.L10N.register( "Confirm Deletion" : "Bekräfta radering", "Mappings cleared successfully!" : "Rensning av mappningar lyckades!", "Error while clearing the mappings." : "Fel uppstod under rensning av mappningar", - "Anonymous bind is not allowed. Please provide a User DN and Password." : "Anonymous bind är inte tillåten. Var god ange en Användar-DN och lösenord.", - "LDAP Operations error. Anonymous bind might not be allowed." : "LDAP procedursfel. Anonymous bind tillåts troligtvis inte.", "Saving failed. Please make sure the database is in Operation. Reload before continuing." : "Sparning misslyckades. Var god kontrollera att databasen är verksam. Ladda om innan du fortsätter.", "Switching the mode will enable automatic LDAP queries. Depending on your LDAP size they may take a while. Do you still want to switch the mode?" : "Byte av läge kommer aktivera automatiska LDAP förfrågningar. Beroende på din LDAP storlek kan de ta ett tag. Vill du fortfarande ändra läge?", "Mode switch" : "Lägesändring", "Select attributes" : "Välj attribut", "User not found. Please check your login attributes and username. Effective filter (to copy-and-paste for command line validation): <br/>" : "Användare inte hittad. Var god kontrollera din inloggnings attribut och användarnamn. Effektivt filter (för att kopiera och klistra in i kommandofönster validering):", "User found and settings verified." : "Användare hittad och inställnings bekräftade.", + "Settings verified, but more than one user found. Only the first will be able to login. Consider a more narrow filter." : "Inställningarna verifierade men flera än 1 användare hittades. Endast den första kommer att kunna logga in. Annars testa med att göra en mer specifik sökning.", "An unspecified error occurred. Please check the settings and the log." : "Ett ospecificerat fel uppstod. Var god kontrollera inställningar och logfilen.", "The search filter is invalid, probably due to syntax issues like uneven number of opened and closed brackets. Please revise." : "Sökfiltret är ej giltigt, antagligen på grund utav ett syntaxfel så som ojämnat antal öppna och stängda klammrar. Var god granska.", "A connection error to LDAP / AD occurred, please check host, port and credentials." : "Ett anslutningsfel till LDAP / AD uppstod. Var god granska värd, port och inloggningsuppgifter.", "Please provide a login name to test against" : "Vänligen ange ett inloggningsnamn att försöka ansluta med", "Password change rejected. Hint: " : "Lösenordsbyte nekad. Anledning/tips: ", - "LDAP / AD integration" : "LDAP / AD integration", "_%s group found_::_%s groups found_" : ["%s grupp hittad","%s grupper hittade"], "_%s user found_::_%s users found_" : ["%s användare hittad","%s användare hittade"], "Could not detect user display name attribute. Please specify it yourself in advanced ldap settings." : "Kunde inte upptäcka attributet användarvisningsnamn. Vänligen ange det själv i de avancerade LDAP-inställningarna.", @@ -67,7 +65,6 @@ OC.L10N.register( "LDAP / AD Email Address:" : "LDAP / AD E-postadress:", "Other Attributes:" : "Övriga attribut:", "Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action. Example: \"uid=%%uid\"" : "Definierar filter som tillämpas vid inloggning. %%uid ersätter användarnamn vid inloggningen. Exempel: \"uid=%%uid\"", - "Test Loginname" : "Test Inloggningsnamn", "Verify settings" : "Verifiera inställningar", "1. Server" : "1. Server", "%s. Server:" : "%s. Server:", @@ -84,6 +81,7 @@ OC.L10N.register( "For anonymous access, leave DN and Password empty." : "För anonym åtkomst, lämna DN och lösenord tomt.", "One Base DN per line" : "Ett start-DN per rad", "You can specify Base DN for users and groups in the Advanced tab" : "Du kan ange start-DN för användare och grupper under fliken Avancerat", + "Test Base DN" : "Testa Bas-DN", "Avoids automatic LDAP requests. Better for bigger setups, but requires some LDAP knowledge." : "Undviker automatiska LDAP-förfrågningar. Bättre för större installationer, men kräver en del LDAP-kunskap.", "Manually enter LDAP filters (recommended for large directories)" : "Ange LDAP-filter manuellt (rekommenderat för stora kataloger)", "The filter specifies which LDAP users shall have access to the %s instance." : "Filtret specifierar vilka LDAP-användare som skall ha åtkomst till %s instans", @@ -94,7 +92,6 @@ OC.L10N.register( "LDAP" : "LDAP", "Server" : "Server", "Users" : "Användare", - "Login Attributes" : "Login Attribut", "Groups" : "Grupper", "Expert" : "Expert", "Advanced" : "Avancerad", @@ -110,7 +107,6 @@ OC.L10N.register( "Only connect to the replica server." : "Anslut endast till replikaservern.", "Turn off SSL certificate validation." : "Stäng av verifiering av SSL-certifikat.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." : "Rekommenderas inte, använd endast för test! Om anslutningen bara fungerar med denna inställning behöver du importera LDAP-serverns SSL-certifikat till din %s server.", - "Cache Time-To-Live" : "Cache Time-To-Live", "in seconds. A change empties the cache." : "i sekunder. En förändring tömmer cache.", "Directory Settings" : "Mappinställningar", "User Display Name Field" : "Attribut för användarnamn", @@ -126,14 +122,16 @@ OC.L10N.register( "One Group Base DN per line" : "En gruppstart-DN per rad", "Group Search Attributes" : "Gruppsökningsattribut", "Group-Member association" : "Attribut för gruppmedlemmar", + "Dynamic Group Member URL" : "Dynamic Group Member URL", "Nested Groups" : "Undergrupper", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "När den är påslagen, stöds grupper som innehåller grupper. (Fungerar endast om gruppmedlemmens attribut innehåller DN.)", "Paging chunksize" : "Paging klusterstorlek", "Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)" : "Klusterstorlek som används för paged LDAP sökningar som kan komma att returnera skrymmande resultat som uppräknande av användare eller grupper. (Inställning av denna till 0 inaktiverar paged LDAP sökningar i de situationerna)", + "Enable LDAP password changes per user" : "Aktivera: Antal tillåtna lösenordsbyten för LDAP-användare", "(New password is sent as plain text to LDAP)" : "(Nytt lösenord skickas som oformaterad text till LDAP)", "Special Attributes" : "Specialattribut", - "Quota Field" : "Kvotfält", - "Quota Default" : "Datakvot standard", + "Quota Field" : "Fält för lagringsutrymme", + "Quota Default" : "Standardutrymme för lagring", "in bytes" : "i bytes", "Email Field" : "E-postfält", "User Home Folder Naming Rule" : "Namnregel för hemkatalog", diff --git a/apps/user_ldap/l10n/sv.json b/apps/user_ldap/l10n/sv.json index 88a92b97794..8f5b48cbf7e 100644 --- a/apps/user_ldap/l10n/sv.json +++ b/apps/user_ldap/l10n/sv.json @@ -20,7 +20,7 @@ "Please check the credentials, they seem to be wrong." : "Var god kontrollera dina uppgifter, de verkar vara fel.", "Please specify the port, it could not be auto-detected." : "Var god specificera en port, den kunde ej autodetekteras.", "Base DN could not be auto-detected, please revise credentials, host and port." : "Grundläggande DN kunde ej autodetekteras, var god dubbelkontrollera dina uppgifter, värd och port.", - "Could not detect Base DN, please enter it manually." : "Kunde ej upptäcka Base DN, var god ange det manuellt.", + "Could not detect Base DN, please enter it manually." : "Kunde inte upptäcka Base DN, var god ange det manuellt.", "{nthServer}. Server" : "{nthServer}. Server", "No object found in the given Base DN. Please revise." : "Inget objekt funnet i den givna Base DN. Var god granska.", "More than 1,000 directory entries available." : "Mer än 1,000 katalogposter tillgängliga.", @@ -30,20 +30,18 @@ "Confirm Deletion" : "Bekräfta radering", "Mappings cleared successfully!" : "Rensning av mappningar lyckades!", "Error while clearing the mappings." : "Fel uppstod under rensning av mappningar", - "Anonymous bind is not allowed. Please provide a User DN and Password." : "Anonymous bind är inte tillåten. Var god ange en Användar-DN och lösenord.", - "LDAP Operations error. Anonymous bind might not be allowed." : "LDAP procedursfel. Anonymous bind tillåts troligtvis inte.", "Saving failed. Please make sure the database is in Operation. Reload before continuing." : "Sparning misslyckades. Var god kontrollera att databasen är verksam. Ladda om innan du fortsätter.", "Switching the mode will enable automatic LDAP queries. Depending on your LDAP size they may take a while. Do you still want to switch the mode?" : "Byte av läge kommer aktivera automatiska LDAP förfrågningar. Beroende på din LDAP storlek kan de ta ett tag. Vill du fortfarande ändra läge?", "Mode switch" : "Lägesändring", "Select attributes" : "Välj attribut", "User not found. Please check your login attributes and username. Effective filter (to copy-and-paste for command line validation): <br/>" : "Användare inte hittad. Var god kontrollera din inloggnings attribut och användarnamn. Effektivt filter (för att kopiera och klistra in i kommandofönster validering):", "User found and settings verified." : "Användare hittad och inställnings bekräftade.", + "Settings verified, but more than one user found. Only the first will be able to login. Consider a more narrow filter." : "Inställningarna verifierade men flera än 1 användare hittades. Endast den första kommer att kunna logga in. Annars testa med att göra en mer specifik sökning.", "An unspecified error occurred. Please check the settings and the log." : "Ett ospecificerat fel uppstod. Var god kontrollera inställningar och logfilen.", "The search filter is invalid, probably due to syntax issues like uneven number of opened and closed brackets. Please revise." : "Sökfiltret är ej giltigt, antagligen på grund utav ett syntaxfel så som ojämnat antal öppna och stängda klammrar. Var god granska.", "A connection error to LDAP / AD occurred, please check host, port and credentials." : "Ett anslutningsfel till LDAP / AD uppstod. Var god granska värd, port och inloggningsuppgifter.", "Please provide a login name to test against" : "Vänligen ange ett inloggningsnamn att försöka ansluta med", "Password change rejected. Hint: " : "Lösenordsbyte nekad. Anledning/tips: ", - "LDAP / AD integration" : "LDAP / AD integration", "_%s group found_::_%s groups found_" : ["%s grupp hittad","%s grupper hittade"], "_%s user found_::_%s users found_" : ["%s användare hittad","%s användare hittade"], "Could not detect user display name attribute. Please specify it yourself in advanced ldap settings." : "Kunde inte upptäcka attributet användarvisningsnamn. Vänligen ange det själv i de avancerade LDAP-inställningarna.", @@ -65,7 +63,6 @@ "LDAP / AD Email Address:" : "LDAP / AD E-postadress:", "Other Attributes:" : "Övriga attribut:", "Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action. Example: \"uid=%%uid\"" : "Definierar filter som tillämpas vid inloggning. %%uid ersätter användarnamn vid inloggningen. Exempel: \"uid=%%uid\"", - "Test Loginname" : "Test Inloggningsnamn", "Verify settings" : "Verifiera inställningar", "1. Server" : "1. Server", "%s. Server:" : "%s. Server:", @@ -82,6 +79,7 @@ "For anonymous access, leave DN and Password empty." : "För anonym åtkomst, lämna DN och lösenord tomt.", "One Base DN per line" : "Ett start-DN per rad", "You can specify Base DN for users and groups in the Advanced tab" : "Du kan ange start-DN för användare och grupper under fliken Avancerat", + "Test Base DN" : "Testa Bas-DN", "Avoids automatic LDAP requests. Better for bigger setups, but requires some LDAP knowledge." : "Undviker automatiska LDAP-förfrågningar. Bättre för större installationer, men kräver en del LDAP-kunskap.", "Manually enter LDAP filters (recommended for large directories)" : "Ange LDAP-filter manuellt (rekommenderat för stora kataloger)", "The filter specifies which LDAP users shall have access to the %s instance." : "Filtret specifierar vilka LDAP-användare som skall ha åtkomst till %s instans", @@ -92,7 +90,6 @@ "LDAP" : "LDAP", "Server" : "Server", "Users" : "Användare", - "Login Attributes" : "Login Attribut", "Groups" : "Grupper", "Expert" : "Expert", "Advanced" : "Avancerad", @@ -108,7 +105,6 @@ "Only connect to the replica server." : "Anslut endast till replikaservern.", "Turn off SSL certificate validation." : "Stäng av verifiering av SSL-certifikat.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." : "Rekommenderas inte, använd endast för test! Om anslutningen bara fungerar med denna inställning behöver du importera LDAP-serverns SSL-certifikat till din %s server.", - "Cache Time-To-Live" : "Cache Time-To-Live", "in seconds. A change empties the cache." : "i sekunder. En förändring tömmer cache.", "Directory Settings" : "Mappinställningar", "User Display Name Field" : "Attribut för användarnamn", @@ -124,14 +120,16 @@ "One Group Base DN per line" : "En gruppstart-DN per rad", "Group Search Attributes" : "Gruppsökningsattribut", "Group-Member association" : "Attribut för gruppmedlemmar", + "Dynamic Group Member URL" : "Dynamic Group Member URL", "Nested Groups" : "Undergrupper", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "När den är påslagen, stöds grupper som innehåller grupper. (Fungerar endast om gruppmedlemmens attribut innehåller DN.)", "Paging chunksize" : "Paging klusterstorlek", "Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)" : "Klusterstorlek som används för paged LDAP sökningar som kan komma att returnera skrymmande resultat som uppräknande av användare eller grupper. (Inställning av denna till 0 inaktiverar paged LDAP sökningar i de situationerna)", + "Enable LDAP password changes per user" : "Aktivera: Antal tillåtna lösenordsbyten för LDAP-användare", "(New password is sent as plain text to LDAP)" : "(Nytt lösenord skickas som oformaterad text till LDAP)", "Special Attributes" : "Specialattribut", - "Quota Field" : "Kvotfält", - "Quota Default" : "Datakvot standard", + "Quota Field" : "Fält för lagringsutrymme", + "Quota Default" : "Standardutrymme för lagring", "in bytes" : "i bytes", "Email Field" : "E-postfält", "User Home Folder Naming Rule" : "Namnregel för hemkatalog", diff --git a/apps/user_ldap/l10n/zh_CN.js b/apps/user_ldap/l10n/zh_CN.js index e1fad014a1c..2d57b1545e9 100644 --- a/apps/user_ldap/l10n/zh_CN.js +++ b/apps/user_ldap/l10n/zh_CN.js @@ -13,6 +13,7 @@ OC.L10N.register( " Could not set configuration %s" : " 无法设定配置文件 %s", "Action does not exist" : "操作不存在", "The Base DN appears to be wrong" : "Base DN似乎错了", + "Testing configuration…" : "测试配置...", "Configuration incorrect" : "配置错误", "Configuration incomplete" : "配置未完成", "Configuration OK" : "配置完成", @@ -39,12 +40,15 @@ OC.L10N.register( "Select attributes" : "选择属性", "User not found. Please check your login attributes and username. Effective filter (to copy-and-paste for command line validation): <br/>" : "找不到用户。请检查您的登录属性和用户名。有效过滤(复制和粘贴命令行验证):", "User found and settings verified." : "用户已找到,设置已验证。", + "Settings verified, but more than one user found. Only the first will be able to login. Consider a more narrow filter." : "已验证设置,但找到了多个用户。 只有第一个用户能登录。 请您考虑使用一个更小的过滤范围。", "An unspecified error occurred. Please check the settings and the log." : "发生未指定的错误。请检查设置和日志。", "The search filter is invalid, probably due to syntax issues like uneven number of opened and closed brackets. Please revise." : "搜索的筛选条件无效,可能是由于不完全开闭括号的句法的问题,请检查。", "A connection error to LDAP / AD occurred, please check host, port and credentials." : "LDAP/ AD连接错误,请检查主机,端口和凭证。", "The %uid placeholder is missing. It will be replaced with the login name when querying LDAP / AD." : "该%uid占位符缺失。它将在LDAP/ AD登录名查询时进行替换。", "Please provide a login name to test against" : "请提供登录名以测试", "The group box was disabled, because the LDAP / AD server does not support memberOf." : "该组框被禁用,因为LDAP/ AD服务器不支持memberOf。", + "Password change rejected. Hint: " : "密码更改出错。提示:", + "LDAP / AD integration" : "LDAP / AD 整合", "_%s group found_::_%s groups found_" : ["发现 %s 个群组"], "_%s user found_::_%s users found_" : ["发现 %s 个用户"], "Could not detect user display name attribute. Please specify it yourself in advanced ldap settings." : "无法检测到用户的显示名称属性。请在高级LDAP设置中指定。", @@ -90,6 +94,7 @@ OC.L10N.register( "Test Base DN" : "测试基础DN", "Avoids automatic LDAP requests. Better for bigger setups, but requires some LDAP knowledge." : "避免自动LDAP请求。用于更精确的设置,但需要一些LDAP知识。", "Manually enter LDAP filters (recommended for large directories)" : "手动输入LDAP筛选条件(建议用于大型目录)", + "%s access is limited to users meeting these criteria:" : "%s 的访问权限限于满足以下条件的用户:", "The most common object classes for users are organizationalPerson, person, user, and inetOrgPerson. If you are not sure which object class to select, please consult your directory admin." : "对于用户最常用的对象类为organizationalPerson,person,user和inetOrgPerson。如果你不确定选择哪些对象类,请咨询您的目录管理员。", "The filter specifies which LDAP users shall have access to the %s instance." : "该筛选条件指定哪些LDAP用户有权访问%s的实例。", "Verify settings and count users" : "验证设置和统计用户", @@ -138,6 +143,9 @@ OC.L10N.register( "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "当选择后,包含组的组将启用。(只有当组成员属性包含DNs时有效。)", "Paging chunksize" : "页块大小", "Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)" : "用于在LDAP搜索返回如用户或组枚举结果时进行分页显示。 (设置它为0时,禁止在这些情况下分页LDAP搜索。)", + "Enable LDAP password changes per user" : "每个用户可以更改 LDAP 密码", + "Allow LDAP users to change their password and allow Super Administrators and Group Administrators to change the password of their LDAP users. Only works when access control policies are configured accordingly on the LDAP server. As passwords are sent in plaintext to the LDAP server, transport encryption must be used and password hashing should be configured on the LDAP server." : "允许LDAP用户更改其密码,并允许超级管理员和组管理员更改LDAP用户的密码。 仅在LDAP服务器上相应配置访问控制策略时有效。 由于密码以纯文本形式发送到LDAP服务器,因此必须使用传输加密,并在LDAP服务器上配置散列密码。", + "(New password is sent as plain text to LDAP)" : "(新的密码将以纯文本形式发送到 LDAP)", "Special Attributes" : "特殊属性", "Quota Field" : "配额字段", "Quota Default" : "默认配额", @@ -146,6 +154,7 @@ OC.L10N.register( "User Home Folder Naming Rule" : "用户主目录命名规则", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." : "指定一个 LDAP/AD 属性。留空,则使用用户名称(默认)。", "Internal Username" : "内部用户名", + "By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder. It is also a part of remote URLs, for instance for all *DAV services. With this setting, the default behavior can be overridden. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users." : "默认情况下将从UUID属性创建内部用户名。将确保用户名是唯一的字符,并且不需要转换。 内部用户名,只允许使用这些字符:[a-zA-Z0-9 _。@ - ]。 其他字符被替换为它们的ASCII对应或简单地被忽略。如果出现重复,将添加或增加一个数字。 内部用户名用于在内部标识用户。 它是用户主文件夹的默认名称。 它也是远程URL的一部分,例如对于所有* DAV 服务。 使用此设置,可以覆盖默认行为。 默认行为为空, 则更改只会对新映射 (已添加) 的LDAP用户有效。", "Internal Username Attribute:" : "内部用户名属性:", "Override UUID detection" : "超越UUID检测", "By default, the UUID attribute is automatically detected. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users and groups." : "ownCloud 默认会自动检测 UUID 属性。UUID 属性用来无误地识别 LDAP 用户和组。同时,如果上面没有特别设置,内部用户名也基于 UUID 创建。也可以覆盖设置,直接指定一个属性。但一定要确保指定的属性取得的用户和组是唯一的。留空,则执行默认操作。更改只影响新映射 (或增加) 的 LDAP 用户和组。", diff --git a/apps/user_ldap/l10n/zh_CN.json b/apps/user_ldap/l10n/zh_CN.json index e6a22c4600c..c8dbccd08c9 100644 --- a/apps/user_ldap/l10n/zh_CN.json +++ b/apps/user_ldap/l10n/zh_CN.json @@ -11,6 +11,7 @@ " Could not set configuration %s" : " 无法设定配置文件 %s", "Action does not exist" : "操作不存在", "The Base DN appears to be wrong" : "Base DN似乎错了", + "Testing configuration…" : "测试配置...", "Configuration incorrect" : "配置错误", "Configuration incomplete" : "配置未完成", "Configuration OK" : "配置完成", @@ -37,12 +38,15 @@ "Select attributes" : "选择属性", "User not found. Please check your login attributes and username. Effective filter (to copy-and-paste for command line validation): <br/>" : "找不到用户。请检查您的登录属性和用户名。有效过滤(复制和粘贴命令行验证):", "User found and settings verified." : "用户已找到,设置已验证。", + "Settings verified, but more than one user found. Only the first will be able to login. Consider a more narrow filter." : "已验证设置,但找到了多个用户。 只有第一个用户能登录。 请您考虑使用一个更小的过滤范围。", "An unspecified error occurred. Please check the settings and the log." : "发生未指定的错误。请检查设置和日志。", "The search filter is invalid, probably due to syntax issues like uneven number of opened and closed brackets. Please revise." : "搜索的筛选条件无效,可能是由于不完全开闭括号的句法的问题,请检查。", "A connection error to LDAP / AD occurred, please check host, port and credentials." : "LDAP/ AD连接错误,请检查主机,端口和凭证。", "The %uid placeholder is missing. It will be replaced with the login name when querying LDAP / AD." : "该%uid占位符缺失。它将在LDAP/ AD登录名查询时进行替换。", "Please provide a login name to test against" : "请提供登录名以测试", "The group box was disabled, because the LDAP / AD server does not support memberOf." : "该组框被禁用,因为LDAP/ AD服务器不支持memberOf。", + "Password change rejected. Hint: " : "密码更改出错。提示:", + "LDAP / AD integration" : "LDAP / AD 整合", "_%s group found_::_%s groups found_" : ["发现 %s 个群组"], "_%s user found_::_%s users found_" : ["发现 %s 个用户"], "Could not detect user display name attribute. Please specify it yourself in advanced ldap settings." : "无法检测到用户的显示名称属性。请在高级LDAP设置中指定。", @@ -88,6 +92,7 @@ "Test Base DN" : "测试基础DN", "Avoids automatic LDAP requests. Better for bigger setups, but requires some LDAP knowledge." : "避免自动LDAP请求。用于更精确的设置,但需要一些LDAP知识。", "Manually enter LDAP filters (recommended for large directories)" : "手动输入LDAP筛选条件(建议用于大型目录)", + "%s access is limited to users meeting these criteria:" : "%s 的访问权限限于满足以下条件的用户:", "The most common object classes for users are organizationalPerson, person, user, and inetOrgPerson. If you are not sure which object class to select, please consult your directory admin." : "对于用户最常用的对象类为organizationalPerson,person,user和inetOrgPerson。如果你不确定选择哪些对象类,请咨询您的目录管理员。", "The filter specifies which LDAP users shall have access to the %s instance." : "该筛选条件指定哪些LDAP用户有权访问%s的实例。", "Verify settings and count users" : "验证设置和统计用户", @@ -136,6 +141,9 @@ "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" : "当选择后,包含组的组将启用。(只有当组成员属性包含DNs时有效。)", "Paging chunksize" : "页块大小", "Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)" : "用于在LDAP搜索返回如用户或组枚举结果时进行分页显示。 (设置它为0时,禁止在这些情况下分页LDAP搜索。)", + "Enable LDAP password changes per user" : "每个用户可以更改 LDAP 密码", + "Allow LDAP users to change their password and allow Super Administrators and Group Administrators to change the password of their LDAP users. Only works when access control policies are configured accordingly on the LDAP server. As passwords are sent in plaintext to the LDAP server, transport encryption must be used and password hashing should be configured on the LDAP server." : "允许LDAP用户更改其密码,并允许超级管理员和组管理员更改LDAP用户的密码。 仅在LDAP服务器上相应配置访问控制策略时有效。 由于密码以纯文本形式发送到LDAP服务器,因此必须使用传输加密,并在LDAP服务器上配置散列密码。", + "(New password is sent as plain text to LDAP)" : "(新的密码将以纯文本形式发送到 LDAP)", "Special Attributes" : "特殊属性", "Quota Field" : "配额字段", "Quota Default" : "默认配额", @@ -144,6 +152,7 @@ "User Home Folder Naming Rule" : "用户主目录命名规则", "Leave empty for user name (default). Otherwise, specify an LDAP/AD attribute." : "指定一个 LDAP/AD 属性。留空,则使用用户名称(默认)。", "Internal Username" : "内部用户名", + "By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [ a-zA-Z0-9_.@- ]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder. It is also a part of remote URLs, for instance for all *DAV services. With this setting, the default behavior can be overridden. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users." : "默认情况下将从UUID属性创建内部用户名。将确保用户名是唯一的字符,并且不需要转换。 内部用户名,只允许使用这些字符:[a-zA-Z0-9 _。@ - ]。 其他字符被替换为它们的ASCII对应或简单地被忽略。如果出现重复,将添加或增加一个数字。 内部用户名用于在内部标识用户。 它是用户主文件夹的默认名称。 它也是远程URL的一部分,例如对于所有* DAV 服务。 使用此设置,可以覆盖默认行为。 默认行为为空, 则更改只会对新映射 (已添加) 的LDAP用户有效。", "Internal Username Attribute:" : "内部用户名属性:", "Override UUID detection" : "超越UUID检测", "By default, the UUID attribute is automatically detected. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users and groups." : "ownCloud 默认会自动检测 UUID 属性。UUID 属性用来无误地识别 LDAP 用户和组。同时,如果上面没有特别设置,内部用户名也基于 UUID 创建。也可以覆盖设置,直接指定一个属性。但一定要确保指定的属性取得的用户和组是唯一的。留空,则执行默认操作。更改只影响新映射 (或增加) 的 LDAP 用户和组。", diff --git a/apps/workflowengine/l10n/sv.js b/apps/workflowengine/l10n/sv.js index 02835814d8d..9e6178c895d 100644 --- a/apps/workflowengine/l10n/sv.js +++ b/apps/workflowengine/l10n/sv.js @@ -51,6 +51,13 @@ OC.L10N.register( "The given start time is invalid" : "Den valda starttiden är felaktig", "The given end time is invalid" : "Den valda sluttiden är felaktig", "The given group does not exist" : "Den valda gruppen finns inte", + "Check %s is invalid or does not exist" : "Kontroll av %s är ogiltig eller existerar inte", + "Operation #%s does not exist" : "Operationen #%s existerar inte", + "Operation %s does not exist" : "Operationen %s existerar inte", + "Operation %s is invalid" : "Operationen %s är ogiltig", + "Check %s does not exist" : "Kontroll av %s existerar inte", + "Check %s is invalid" : "Kontroll av %s är ogiltig", + "Check #%s does not exist" : "Kontroll av #%s existerar inte", "Workflow" : "Arbetsflöde", "Open documentation" : "Öppna dokumentation", "Add rule group" : "Lägg till regelgrupp", diff --git a/apps/workflowengine/l10n/sv.json b/apps/workflowengine/l10n/sv.json index f1acd41bf9f..114eb6a77be 100644 --- a/apps/workflowengine/l10n/sv.json +++ b/apps/workflowengine/l10n/sv.json @@ -49,6 +49,13 @@ "The given start time is invalid" : "Den valda starttiden är felaktig", "The given end time is invalid" : "Den valda sluttiden är felaktig", "The given group does not exist" : "Den valda gruppen finns inte", + "Check %s is invalid or does not exist" : "Kontroll av %s är ogiltig eller existerar inte", + "Operation #%s does not exist" : "Operationen #%s existerar inte", + "Operation %s does not exist" : "Operationen %s existerar inte", + "Operation %s is invalid" : "Operationen %s är ogiltig", + "Check %s does not exist" : "Kontroll av %s existerar inte", + "Check %s is invalid" : "Kontroll av %s är ogiltig", + "Check #%s does not exist" : "Kontroll av #%s existerar inte", "Workflow" : "Arbetsflöde", "Open documentation" : "Öppna dokumentation", "Add rule group" : "Lägg till regelgrupp", diff --git a/autotest-js.sh b/autotest-js.sh index 475a61df59e..bd7310c4e43 100755 --- a/autotest-js.sh +++ b/autotest-js.sh @@ -22,6 +22,10 @@ fi # update/install test packages mkdir -p "$PREFIX" && $NPM install --link --prefix "$PREFIX" || exit 3 +# create scss test +mkdir -p tests/css +./build/bin/node-sass --output tests/css core/css + KARMA="$PREFIX/node_modules/karma/bin/karma" NODE_PATH='build/node_modules' KARMA_TESTSUITE="$1" $KARMA start tests/karma.config.js --single-run diff --git a/build/autoloaderchecker.sh b/build/autoloaderchecker.sh index 0a1fb8ac351..b8ac493cb24 100644 --- a/build/autoloaderchecker.sh +++ b/build/autoloaderchecker.sh @@ -1,23 +1,33 @@ #!/usr/bin/env bash -#Make sure we are on the latest composer -if [ -e "composer.phar" ] +COMPOSER_COMMAND=$(which "composer") +if [ "$COMPOSER_COMMAND" = '' ] then - echo "Composer found: checking for update" - php composer.phar self-update + #No global composer found, try local or download it + if [ -e "composer.phar" ] + then + echo "Composer found: checking for update" + else + echo "Composer not found: fetching" + php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" + php composer-setup.php + php -r "unlink('composer-setup.php');" + fi + + COMPOSER_COMMAND="php composer.phar" else - echo "Composer not found: fetching" - php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" - php composer-setup.php - php -r "unlink('composer-setup.php');" + echo "Global composer found: checking for update" fi +#Make sure we are on the latest composer +$COMPOSER_COMMAND self-update + REPODIR=`git rev-parse --show-toplevel` #Redump the autoloader echo echo "Regenerating autoloader" -php composer.phar dump-autoload -d $REPODIR +$COMPOSER_COMMAND dump-autoload -d $REPODIR files=`git diff --name-only` composerfile=false diff --git a/build/integration/features/tags.feature b/build/integration/features/tags.feature index 35784419080..0c6cd06f9f9 100644 --- a/build/integration/features/tags.feature +++ b/build/integration/features/tags.feature @@ -70,12 +70,13 @@ Feature: tags When "user0" edits the tag with name "TagWithGroups" and sets its groups to "group1|group3" Then The response should have a status code "403" - Scenario: Deleting a normal tag as regular user should work + Scenario: Deleting a normal tag as regular user should fail Given user "user0" exists Given "admin" creates a "normal" tag with name "MySuperAwesomeTagName" When "user0" deletes the tag with name "MySuperAwesomeTagName" - Then The response should have a status code "204" - And "0" tags should exist for "admin" + Then The response should have a status code "403" + And The following tags should exist for "admin" + |MySuperAwesomeTagName|true|true| Scenario: Deleting a not user-assignable tag as regular user should fail Given user "user0" exists @@ -93,6 +94,12 @@ Feature: tags And The following tags should exist for "admin" |MySuperAwesomeTagName|false|true| + Scenario: Deleting a normal tag as admin should work + Given "admin" creates a "normal" tag with name "MySuperAwesomeTagName" + When "admin" deletes the tag with name "MySuperAwesomeTagName" + Then The response should have a status code "204" + And "0" tags should exist for "admin" + Scenario: Deleting a not user-assignable tag as admin should work Given "admin" creates a "not user-assignable" tag with name "MySuperAwesomeTagName" When "admin" deletes the tag with name "MySuperAwesomeTagName" diff --git a/build/package.json b/build/package.json index 6058d6785e0..67e999aaf31 100644 --- a/build/package.json +++ b/build/package.json @@ -17,7 +17,8 @@ "karma-coverage": "*", "karma-phantomjs-launcher": "*", "phantomjs-prebuilt": "*", - "jasmine-core": "~2.5.2" + "jasmine-core": "~2.5.2", + "node-sass": "~4.1.1" }, "engine": "node >= 0.8" } diff --git a/config/config.sample.php b/config/config.sample.php index 8b714a1763c..a9bb0067e5b 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -909,7 +909,6 @@ $CONFIG = array( * * Available cache backends: * - * * ``\OC\Memcache\APC`` Alternative PHP Cache backend * * ``\OC\Memcache\APCu`` APC user backend * * ``\OC\Memcache\ArrayCache`` In-memory array-based backend (not recommended) * * ``\OC\Memcache\Memcached`` Memcached backend diff --git a/core/Application.php b/core/Application.php index 545b5fe420b..dad7546dcb8 100644 --- a/core/Application.php +++ b/core/Application.php @@ -30,10 +30,11 @@ namespace OC\Core; -use OC\AppFramework\Utility\SimpleContainer; use OC\Security\IdentityProof\Manager; use OCP\AppFramework\App; -use OCP\Files\IAppData; +use OC\Core\Controller\CssController; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\IRequest; use OCP\Util; /** @@ -57,5 +58,13 @@ class Application extends App { \OC::$server->getCrypto() ); }); + $container->registerService(CssController::class, function () use ($container) { + return new CssController( + $container->query('appName'), + $container->query(IRequest::class), + \OC::$server->getAppDataDir('css'), + $container->query(ITimeFactory::class) + ); + }); } } diff --git a/core/Controller/CssController.php b/core/Controller/CssController.php new file mode 100644 index 00000000000..1206c95a5b8 --- /dev/null +++ b/core/Controller/CssController.php @@ -0,0 +1,79 @@ +<?php +/** + * @copyright Copyright (c) 2016, John Molakvoæ (skjnldsv@protonmail.com) + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Core\Controller; + +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\NotFoundResponse; +use OCP\AppFramework\Http\FileDisplayResponse; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Files\IAppData; +use OCP\Files\NotFoundException; +use OCP\IRequest; + +class CssController extends Controller { + + /** @var IAppData */ + protected $appData; + + /** @var ITimeFactory */ + protected $timeFactory; + + /** + * @param string $appName + * @param IRequest $request + * @param IAppData $appData + * @param ITimeFactory $timeFactory + */ + public function __construct($appName, IRequest $request, IAppData $appData, ITimeFactory $timeFactory) { + parent::__construct($appName, $request); + + $this->appData = $appData; + $this->timeFactory = $timeFactory; + } + + /** + * @PublicPage + * @NoCSRFRequired + * + * @param string $fileName css filename with extension + * @param string $appName css folder name + * @return FileDisplayResponse|NotFoundResponse + */ + public function getCss($fileName, $appName) { + try { + $folder = $this->appData->getFolder($appName); + $cssFile = $folder->getFile($fileName); + } catch(NotFoundException $e) { + return new NotFoundResponse(); + } + + $response = new FileDisplayResponse($cssFile, Http::STATUS_OK, ['Content-Type' => 'text/css']); + $response->cacheFor(86400); + $expires = new \DateTime(); + $expires->setTimestamp($this->timeFactory->getTime()); + $expires->add(new \DateInterval('PT24H')); + $response->addHeader('Expires', $expires->format(\DateTime::RFC1123)); + $response->addHeader('Pragma', 'cache'); + return $response; + } +} diff --git a/core/Controller/LoginController.php b/core/Controller/LoginController.php index b6add48ef61..3c81ed5242a 100644 --- a/core/Controller/LoginController.php +++ b/core/Controller/LoginController.php @@ -298,14 +298,10 @@ class LoginController extends Controller { $currentDelay = $this->throttler->getDelay($this->request->getRemoteAddress()); $this->throttler->sleepDelay($this->request->getRemoteAddress()); - $user = $this->userSession->getUser(); - if (!$user instanceof IUser) { - return new DataResponse([], Http::STATUS_UNAUTHORIZED); - } - - $loginResult = $this->userManager->checkPassword($user->getUID(), $password); + $loginName = $this->userSession->getLoginName(); + $loginResult = $this->userManager->checkPassword($loginName, $password); if ($loginResult === false) { - $this->throttler->registerAttempt('sudo', $this->request->getRemoteAddress(), ['user' => $user->getUID()]); + $this->throttler->registerAttempt('sudo', $this->request->getRemoteAddress(), ['user' => $loginName]); if ($currentDelay === 0) { $this->throttler->sleepDelay($this->request->getRemoteAddress()); } diff --git a/core/css/apps.css b/core/css/apps.css deleted file mode 100644 index e709f9d901f..00000000000 --- a/core/css/apps.css +++ /dev/null @@ -1,700 +0,0 @@ -/* APP STYLING -------------------------------------------------------------- */ - - -#app { - height: 100%; - width: 100%; -} -#app * { - box-sizing: border-box; -} - - - - - -/* APP-NAVIGATION ------------------------------------------------------------*/ - - -/* Navigation: folder like structure */ -#app-navigation { - width: 250px; - height: 100%; - float: left; - box-sizing: border-box; - background-color: #fff; - padding-bottom: 44px; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - border-right: 1px solid #eee; -} -#app-navigation > ul { - position: relative; - height: 100%; - width: inherit; - overflow: auto; - box-sizing: border-box; -} -#app-navigation li { - position: relative; - width: 100%; - box-sizing: border-box; -} - -#app-navigation.without-app-settings { - padding-bottom: 0; -} - -#app-navigation .active.with-menu > a, -#app-navigation .with-counter > a { - padding-right: 50px; -} - -#app-navigation .active.with-menu.with-counter > a { - padding-right: 90px; -} - -#app-navigation .with-icon a, -#app-navigation .app-navigation-entry-loading a { - padding-left: 44px; - background-size: 16px 16px; - background-position: 14px center; - background-repeat: no-repeat; -} - -#app-navigation li > a { - display: block; - width: 100%; - line-height: 44px; - min-height: 44px; - padding: 0 12px; - overflow: hidden; - box-sizing: border-box; - white-space: nowrap; - text-overflow: ellipsis; - color: #000; - opacity: .57; -} -#app-navigation .active, -#app-navigation .active a, -#app-navigation li:hover > a, -#app-navigation li:focus > a, -#app-navigation a:focus, -#app-navigation .selected, -#app-navigation .selected a { - opacity: 1; -} - -#app-navigation .collapse { - display: none; /* hide collapse button initially */ -} -#app-navigation .collapsible > .collapse { - position: absolute; - height: 44px; - width: 44px; - margin: 0; - padding: 0; - background: none; background-image: url('../img/actions/triangle-s.svg?v=1'); - background-size: 16px; background-repeat: no-repeat; background-position: center; - border: none; - border-radius: 0; - outline: none !important; - box-shadow: none; -} -#app-navigation .collapsible:hover > a, -#app-navigation .collapsible:focus > a { - background-image: none; -} -#app-navigation .collapsible:hover > .collapse, -#app-navigation .collapsible:focus > .collapse { - display: block; -} - -#app-navigation .collapsible .collapse { - -webkit-transform: rotate(-90deg); - -ms-transform:rotate(-90deg); - transform: rotate(-90deg); -} -#app-navigation .collapsible.open .collapse { - -webkit-transform: rotate(0); - -ms-transform:rotate(0); - transform: rotate(0); -} - -/* Second level nesting for lists */ -#app-navigation > ul ul { - display: none; -} -#app-navigation > ul ul li > a { - padding-left: 32px; -} -#app-navigation > .with-icon ul li > a, -#app-navigation > .with-icon ul li.app-navigation-entry-loading > a { - padding-left: 68px; - background-position: 44px center; -} - -#app-navigation .collapsible.open { - background-image: linear-gradient(top, rgb(238,238,238) 0%, rgb(245,245,245) 100%); - background-image: -webkit-linear-gradient(top, rgb(238,238,238) 0%, rgb(245,245,245) 100%); - background-image: -ms-linear-gradient(top, rgb(238,238,238) 0%, rgb(245,245,245) 100%); -} - -#app-navigation > ul .collapsible.open:hover, -#app-navigation > ul .collapsible.open:focus { - box-shadow: inset 0 0 3px #ddd; -} - -#app-navigation > ul .collapsible.open ul { - display: block; -} - - -/* Deleted entries with undo button */ -#app-navigation .app-navigation-entry-deleted { - display: inline-block; - height: 44px; - width: 100%; -} - - #app-navigation .app-navigation-entry-deleted-description { - padding-left: 12px; - position: relative; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - display: inline-block; - width: calc(100% - 49px); - line-height: 44px; - float: left; - } - - #app-navigation .app-navigation-entry-deleted-button { - margin: 0; - height: 44px; - width: 44px; - line-height: 44px; - border: 0; - display: inline-block; - background-color: transparent; - opacity: .5; - } - - #app-navigation .app-navigation-entry-deleted-button:hover, - #app-navigation .app-navigation-entry-deleted-button:focus { - opacity: 1; - } - -/* counter and actions, legacy code */ -#app-navigation .utils { - position: absolute; - padding: 7px 7px 0 0; - right: 0; - top: 0; - bottom: 0; - font-size: 12px; -} - #app-navigation .utils button, - #app-navigation .utils .counter { - width: 44px; - height: 44px; - padding-top: 12px; - } - - -/* drag and drop */ -#app-navigation .drag-and-drop { - -webkit-transition: padding-bottom 500ms ease 0s; - transition: padding-bottom 500ms ease 0s; - padding-bottom: 40px; -} -#app-navigation .error { - color: #dd1144; -} - -#app-navigation .app-navigation-separator { - border-bottom: 1px solid #ddd; -} - -/** - * App navigation utils, buttons and counters for drop down menu - */ -#app-navigation .app-navigation-entry-utils { - position: absolute; - top: 0; - right: 0; - z-index: 105; -} - - #app-navigation .app-navigation-entry-utils ul { - display: block !important; - } - - - #app-navigation .app-navigation-entry-utils li { - float: left; - width: 44px !important; - height: 44px; - line-height: 44px; - } - - #app-navigation .active > .app-navigation-entry-utils li { - display: inline-block; - } - - #app-navigation .app-navigation-entry-utils button { - height: 38px; - width: 38px; - line-height: 38px; - float: left; - } - - #app-navigation .app-navigation-entry-utils-menu-button { - display: none; - } - #app-navigation .app-navigation-entry-utils-menu-button button { - border: 0; - opacity: .5; - background-color: transparent; - background-repeat: no-repeat; - background-position: center; - background-image: url('../img/actions/more.svg?v=1'); - } - - #app-navigation .app-navigation-entry-utils-menu-button:hover button, - #app-navigation .app-navigation-entry-utils-menu-button:focus button { - background-color: transparent; - opacity: 1; - } - - #app-navigation .app-navigation-entry-utils-counter { - overflow: hidden; - text-overflow: hidden; - text-align: right; - font-size: 9pt; - width: 38px; - line-height: 44px; - padding: 0 10px; - } - - #app-navigation .app-navigation-entry-utils ul, - #app-navigation .app-navigation-entry-menu ul { - list-style-type: none; - } - -/* menu bubble / popover */ -.bubble, -#app-navigation .app-navigation-entry-menu { - position: absolute; - background-color: #fff; - color: #333; - border-radius: 3px; - border-top-right-radius: 0; - z-index: 110; - margin: 5px; - margin-top: -5px; - right: 0; - -webkit-filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75)); - -moz-filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75)); - -ms-filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75)); - -o-filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75)); - filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75)); -} - -.ie .bubble, -.ie #app-navigation .app-navigation-entry-menu, -.ie .bubble:after, -.ie #app-navigation .app-navigation-entry-menu:after, -.edge .bubble, -.edge #app-navigation .app-navigation-entry-menu, -.edge .bubble:after, -.edge #app-navigation .app-navigation-entry-menu:after { - border: 1px solid #eee; -} -/* miraculous border arrow stuff */ -.bubble:after, -#app-navigation .app-navigation-entry-menu:after { - bottom: 100%; - right: 6px; /* change this to adjust the arrow position */ - border: solid transparent; - content: " "; - height: 0; - width: 0; - position: absolute; - pointer-events: none; -} -.bubble:after, -#app-navigation .app-navigation-entry-menu:after { - border-color: rgba(238, 238, 238, 0); - border-bottom-color: #fff; - border-width: 10px; -} -.bubble .action { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)" !important; - filter: alpha(opacity=50) !important; - opacity: .5 !important; -} -.bubble .action:hover, -.bubble .action:focus, -.bubble .action.active { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)" !important; - filter: alpha(opacity=100) !important; - opacity: 1 !important; -} - -#app-navigation .app-navigation-entry-menu { - display: none; -} - -#app-navigation .app-navigation-entry-menu.open { - display: block; -} - - /* list of options for an entry */ - #app-navigation .app-navigation-entry-menu ul { - display: block !important; - } - - #app-navigation .app-navigation-entry-menu li { - float: left; - width: 38px !important; - } - - #app-navigation .app-navigation-entry-menu li button { - float: right; - width: 36px !important; - height: 36px; - line-height: 36px; - border: 0; - opacity: .5; - background-color: transparent; - } - - #app-navigation .app-navigation-entry-menu li button:hover, - #app-navigation .app-navigation-entry-menu li button:focus { - opacity: 1; - background-color: transparent; - } - -/* editing an entry */ -#app-navigation .app-navigation-entry-edit { - padding-left: 5px; - padding-right: 5px; - display: inline-block; - height: 39px; - width: 100%; -} - - #app-navigation .app-navigation-entry-edit input { - border-bottom-right-radius: 0; - border-top-right-radius: 0; - width: calc(100% - 36px); - padding: 5px; - margin-right: 0; - height: 38px; - float: left; - border: 1px solid rgba(190,190,190,.9); - } - - #app-navigation .app-navigation-entry-edit button, - #app-navigation .app-navigation-entry-edit input[type="submit"] { - width: 36px; - height: 38px; - float: left; - } - - #app-navigation .app-navigation-entry-edit .icon-checkmark { - border-bottom-left-radius: 0; - border-top-left-radius: 0; - border-left: 0; - margin-right: 0; - } - - -/* APP-CONTENT ---------------------------------------------------------------*/ - - -/* Part where the content will be loaded into */ -#app-content { - position: relative; - height: 100%; - overflow-y: auto; -} - -#app-content-wrapper { - min-width: 100%; - min-height: 100%; -} - -/* APP-SIDEBAR ----------------------------------------------------------------*/ - -/* - Sidebar: a sidebar to be used within #app-content - have it as first element within app-content in order to shrink other - sibling containers properly. Compare Files app for example. -*/ -#app-sidebar { - position: fixed; - top: 45px; - right: 0; - left: auto; - bottom: 0; - width: 27%; - min-width: 300px; - display: block; - background: #fff; - border-left: 1px solid #eee; - -webkit-transition: margin-right 300ms; - transition: margin-right 300ms; - overflow-x: hidden; - overflow-y: auto; - visibility: visible; - z-index: 500; -} - -#app-content.with-app-sidebar { - margin-right: 27%; -} - -#app-sidebar.disappear { - visibility: hidden; -} - -/* APP-SETTINGS ---------------------------------------------------------------*/ - -/* settings area */ -#app-settings { - position: fixed; - width: 250px; /* change to 100% when layout positions are absolute */ - bottom: 0; - z-index: 140; -} -#app-settings.open #app-settings-content, -#app-settings.opened #app-settings-content { - display: block; -} -#app-settings-content { - display: none; - padding: 10px; - background-color: #fff; - /* restrict height of settings and make scrollable */ - max-height: 300px; - overflow-y: auto; -} -#app-settings-content, -#app-settings-header { - border-right: 1px solid #eee; - width: 250px; - box-sizing: border-box; -} - -/* display input fields at full width */ -#app-settings-content input[type='text'] { - width: 93%; -} - -.settings-button { - display: block; - height: 44px; - width: 100%; - padding: 0; - margin: 0; - background-color: #fff; - background-image: url('../img/actions/settings.svg?v=1'); - background-position: 14px center; - background-repeat: no-repeat; - box-shadow: none; - border: 0; - border-radius: 0; - text-align: left; - padding-left: 42px; - font-weight: normal; -} -.settings-button:hover, -.settings-button:focus { - background-color: #fff; -} -.settings-button.opened:hover, -.settings-button.opened:focus { - background-color: #fff; -} - -/* buttons */ -button.loading { - background-image: url('../img/loading.gif'); - background-position: right 10px center; background-repeat: no-repeat; - background-size: 16px; - padding-right: 30px; -} - -/* general styles for the content area */ -.section { - display: block; - padding: 30px; - color: #555; - margin-bottom: 24px; -} -.section.hidden { - display: none !important; -} -.sub-section { - position: relative; - margin-top: 10px; - margin-left: 27px; - margin-bottom: 10px; -} -/* no top border for first settings item */ -#app-content > .section:first-child { - border-top: none; -} - -/* heading styles */ -h2 { - font-size: 20px; - font-weight: 300; - margin-bottom: 12px; - line-height: 140%; -} -h3 { - font-size: 15px; - font-weight: 300; - margin: 12px 0; -} - -/* slight position correction of checkboxes and radio buttons */ -.section input[type="checkbox"], -.section input[type="radio"] { - vertical-align: -2px; - margin-right: 4px; -} -.appear { - opacity: 1; - -webkit-transition: opacity 500ms ease 0s; - -moz-transition: opacity 500ms ease 0s; - -ms-transition: opacity 500ms ease 0s; - -o-transition: opacity 500ms ease 0s; - transition: opacity 500ms ease 0s; -} -.appear.transparent { - opacity: 0; -} - - -/* do not use italic typeface style, instead lighter color */ -em { - font-style: normal; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; - opacity: .5; -} - -/* generic dropdown style */ -.dropdown { - background:#eee; - border-bottom-left-radius: 5px; - border-bottom-right-radius: 5px; - box-shadow:0 1px 1px #777; - display:block; - margin-right: 0; - position:absolute; - right:0; - width:420px; - z-index:500; - padding:16px; -} - -/* generic tab styles */ -.tabHeaders { - display: inline-block; - margin: 15px; -} -.tabHeaders .tabHeader { - float: left; - padding: 5px; - cursor: pointer; -} -.tabHeaders .tabHeader, .tabHeaders .tabHeader a { - color: #888; - margin-bottom: 1px; -} -.tabHeaders .tabHeader.selected { - font-weight: 600; -} -.tabHeaders .tabHeader.selected, -.tabHeaders .tabHeader:hover { - border-bottom: 1px solid #333; -} -.tabHeaders .tabHeader.selected, -.tabHeaders .tabHeader.selected a, -.tabHeaders .tabHeader:hover, -.tabHeaders .tabHeader:hover a { - margin-bottom: 0px; - color: #000; -} -.tabsContainer { - clear: left; -} -.tabsContainer .tab { - padding: 0 15px 15px; -} - -/* popover menu styles (use together with "bubble" class) */ -.popovermenu .menuitem, -.popovermenu .menuitem>span { - cursor: pointer; - vertical-align: middle; -} - -.popovermenu .menuitem { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; - filter: alpha(opacity=50); - opacity: .5; -} - -.popovermenu .menuitem:hover, -.popovermenu .menuitem:focus, -.popovermenu .menuitem.active { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; - filter: alpha(opacity=100); - opacity: 1; -} - -.popovermenu .menuitem img { - padding: initial; -} - -.popovermenu a.menuitem, -.popovermenu label.menuitem, -.popovermenu .menuitem { - padding: 10px !important; - width: auto; -} - -.popovermenu.hidden { - display: none; -} - -.popovermenu .menuitem { - display: flex !important; - line-height: 30px; - color: #000; - align-items: center; -} - -.popovermenu .menuitem .icon, -.popovermenu .menuitem .no-icon { - display: inline-block; - width: 16px; - height: 16px; - margin-right: 10px; - vertical-align: middle; -} - -.popovermenu .menuitem { - opacity: 0.5; -} - -.popovermenu li:hover .menuitem { - opacity: 1; -} diff --git a/core/css/apps.scss b/core/css/apps.scss new file mode 100644 index 00000000000..8bb380ad0ae --- /dev/null +++ b/core/css/apps.scss @@ -0,0 +1,689 @@ +/* APP STYLING -------------------------------------------------------------- */ + +#app { + height: 100%; + width: 100%; + * { + box-sizing: border-box; + } +} + +/* APP-NAVIGATION ------------------------------------------------------------*/ + +/* Navigation: folder like structure */ + +#app-navigation { + width: 250px; + height: 100%; + float: left; + box-sizing: border-box; + background-color: #fff; + padding-bottom: 44px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + border-right: 1px solid #eee; + > ul { + position: relative; + height: 100%; + width: inherit; + overflow: auto; + box-sizing: border-box; + } + li { + position: relative; + width: 100%; + box-sizing: border-box; + } + &.without-app-settings { + padding-bottom: 0; + } + .active.with-menu > a, .with-counter > a { + padding-right: 50px; + } + .active.with-menu.with-counter > a { + padding-right: 90px; + } + .with-icon a, .app-navigation-entry-loading a { + padding-left: 44px; + background-size: 16px 16px; + background-position: 14px center; + background-repeat: no-repeat; + } + li > a { + display: block; + width: 100%; + line-height: 44px; + min-height: 44px; + padding: 0 12px; + overflow: hidden; + box-sizing: border-box; + white-space: nowrap; + text-overflow: ellipsis; + color: #000; + opacity: .57; + } + .active { + opacity: 1; + a { + opacity: 1; + } + } + li { + &:hover > a, &:focus > a { + opacity: 1; + } + } + a:focus { + opacity: 1; + } + .selected { + opacity: 1; + a { + opacity: 1; + } + } + .collapse { + display: none; + /* hide collapse button initially */ + } + .collapsible { + > .collapse { + position: absolute; + height: 44px; + width: 44px; + margin: 0; + padding: 0; + background: none; + background-image: url('../img/actions/triangle-s.svg?v=1'); + background-size: 16px; + background-repeat: no-repeat; + background-position: center; + border: none; + border-radius: 0; + outline: none !important; + box-shadow: none; + } + &:hover > a, &:focus > a { + background-image: none; + } + &:hover > .collapse, &:focus > .collapse { + display: block; + } + .collapse { + -webkit-transform: rotate(-90deg); + -ms-transform: rotate(-90deg); + transform: rotate(-90deg); + } + &.open { + .collapse { + -webkit-transform: rotate(0); + -ms-transform: rotate(0); + transform: rotate(0); + } + background-image: linear-gradient(top, rgb(238, 238, 238) 0%, rgb(245, 245, 245) 100%); + background-image: -webkit-linear-gradient(top, rgb(238, 238, 238) 0%, rgb(245, 245, 245) 100%); + background-image: -ms-linear-gradient(top, rgb(238, 238, 238) 0%, rgb(245, 245, 245) 100%); + } + } + > { + ul ul { + display: none; + li > a { + padding-left: 32px; + } + } + .with-icon ul li { + > a, &.app-navigation-entry-loading > a { + padding-left: 68px; + background-position: 44px center; + } + } + } + > ul .collapsible.open { + &:hover, &:focus { + box-shadow: inset 0 0 3px #ddd; + } + ul { + display: block; + } + } + .app-navigation-entry-deleted { + display: inline-block; + height: 44px; + width: 100%; + } + .app-navigation-entry-deleted-description { + padding-left: 12px; + position: relative; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + display: inline-block; + width: calc(100% - 49px); + line-height: 44px; + float: left; + } + .app-navigation-entry-deleted-button { + margin: 0; + height: 44px; + width: 44px; + line-height: 44px; + border: 0; + display: inline-block; + background-color: transparent; + opacity: .5; + &:hover, &:focus { + opacity: 1; + } + } + .utils { + position: absolute; + padding: 7px 7px 0 0; + right: 0; + top: 0; + bottom: 0; + font-size: 12px; + button, .counter { + width: 44px; + height: 44px; + padding-top: 12px; + } + } + .drag-and-drop { + -webkit-transition: padding-bottom 500ms ease 0s; + transition: padding-bottom 500ms ease 0s; + padding-bottom: 40px; + } + .error { + color: #dd1144; + } + .app-navigation-separator { + border-bottom: 1px solid #ddd; + } + .app-navigation-entry-utils { + position: absolute; + top: 0; + right: 0; + z-index: 105; + ul { + display: block !important; + } + li { + float: left; + width: 44px !important; + height: 44px; + line-height: 44px; + } + } + .active > .app-navigation-entry-utils li { + display: inline-block; + } + .app-navigation-entry-utils button { + height: 38px; + width: 38px; + line-height: 38px; + float: left; + } + .app-navigation-entry-utils-menu-button { + display: none; + button { + border: 0; + opacity: .5; + background-color: transparent; + background-repeat: no-repeat; + background-position: center; + background-image: url('../img/actions/more.svg?v=1'); + } + &:hover button, &:focus button { + background-color: transparent; + opacity: 1; + } + } + .app-navigation-entry-utils-counter { + overflow: hidden; + text-overflow: hidden; + text-align: right; + font-size: 9pt; + width: 38px; + line-height: 44px; + padding: 0 10px; + } + .app-navigation-entry-utils ul, .app-navigation-entry-menu ul { + list-style-type: none; + } +} + +/* Second level nesting for lists */ + +/* Deleted entries with undo button */ + +/* counter and actions, legacy code */ + +/* drag and drop */ + +/** + * App navigation utils, buttons and counters for drop down menu + */ + +/* menu bubble / popover */ + +.bubble, #app-navigation .app-navigation-entry-menu { + position: absolute; + background-color: #fff; + color: #333; + border-radius: 3px; + border-top-right-radius: 0; + z-index: 110; + margin: 5px; + margin-top: -5px; + right: 0; + -webkit-filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75)); + -moz-filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75)); + -ms-filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75)); + -o-filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75)); + filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75)); +} + +.ie { + .bubble, #app-navigation .app-navigation-entry-menu, .bubble:after, #app-navigation .app-navigation-entry-menu:after { + border: 1px solid #eee; + } +} + +.edge { + .bubble, #app-navigation .app-navigation-entry-menu, .bubble:after, #app-navigation .app-navigation-entry-menu:after { + border: 1px solid #eee; + } +} + +/* miraculous border arrow stuff */ + +.bubble:after, #app-navigation .app-navigation-entry-menu:after { + bottom: 100%; + right: 6px; + /* change this to adjust the arrow position */ + border: solid transparent; + content: ' '; + height: 0; + width: 0; + position: absolute; + pointer-events: none; +} + +.bubble:after, #app-navigation .app-navigation-entry-menu:after { + border-color: rgba(238, 238, 238, 0); + border-bottom-color: #fff; + border-width: 10px; +} + +.bubble .action { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)' !important; + filter: alpha(opacity = 50) !important; + opacity: .5 !important; + &:hover, &:focus, &.active { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)' !important; + filter: alpha(opacity = 100) !important; + opacity: 1 !important; + } +} + +#app-navigation { + .app-navigation-entry-menu { + display: none; + &.open { + display: block; + } + ul { + display: block !important; + } + li { + float: left; + width: 38px !important; + button { + float: right; + width: 36px !important; + height: 36px; + line-height: 36px; + border: 0; + opacity: .5; + background-color: transparent; + &:hover, &:focus { + opacity: 1; + background-color: transparent; + } + } + } + } + .app-navigation-entry-edit { + padding-left: 5px; + padding-right: 5px; + display: inline-block; + height: 39px; + width: 100%; + input { + border-bottom-right-radius: 0; + border-top-right-radius: 0; + width: calc(100% - 36px); + padding: 5px; + margin-right: 0; + height: 38px; + float: left; + border: 1px solid rgba(190, 190, 190, 0.9); + } + button, input[type='submit'] { + width: 36px; + height: 38px; + float: left; + } + .icon-checkmark { + border-bottom-left-radius: 0; + border-top-left-radius: 0; + border-left: 0; + margin-right: 0; + } + } +} + +/* list of options for an entry */ + +/* editing an entry */ + +/* APP-CONTENT ---------------------------------------------------------------*/ + +/* Part where the content will be loaded into */ + +#app-content { + position: relative; + height: 100%; + overflow-y: auto; +} + +#app-content-wrapper { + min-width: 100%; + min-height: 100%; +} + +/* APP-SIDEBAR ----------------------------------------------------------------*/ + +/* + Sidebar: a sidebar to be used within #app-content + have it as first element within app-content in order to shrink other + sibling containers properly. Compare Files app for example. +*/ + +#app-sidebar { + position: fixed; + top: 45px; + right: 0; + left: auto; + bottom: 0; + width: 27%; + min-width: 300px; + display: block; + background: #fff; + border-left: 1px solid #eee; + -webkit-transition: margin-right 300ms; + transition: margin-right 300ms; + overflow-x: hidden; + overflow-y: auto; + visibility: visible; + z-index: 500; +} + +#app-content.with-app-sidebar { + margin-right: 27%; +} + +#app-sidebar.disappear { + visibility: hidden; +} + +/* APP-SETTINGS ---------------------------------------------------------------*/ + +/* settings area */ + +#app-settings { + position: fixed; + width: 250px; + /* change to 100% when layout positions are absolute */ + bottom: 0; + z-index: 140; + &.open #app-settings-content, &.opened #app-settings-content { + display: block; + } +} + +#app-settings-content { + display: none; + padding: 10px; + background-color: #fff; + /* restrict height of settings and make scrollable */ + max-height: 300px; + overflow-y: auto; + border-right: 1px solid #eee; + width: 250px; + box-sizing: border-box; +} + +#app-settings-header { + border-right: 1px solid #eee; + width: 250px; + box-sizing: border-box; +} + +/* display input fields at full width */ + +#app-settings-content input[type='text'] { + width: 93%; +} + +.settings-button { + display: block; + height: 44px; + width: 100%; + padding: 0; + margin: 0; + background-color: #fff; + background-image: url('../img/actions/settings.svg?v=1'); + background-position: 14px center; + background-repeat: no-repeat; + box-shadow: none; + border: 0; + border-radius: 0; + text-align: left; + padding-left: 42px; + font-weight: normal; + &:hover, &:focus { + background-color: #fff; + } + &.opened { + &:hover, &:focus { + background-color: #fff; + } + } +} + +/* buttons */ + +button.loading { + background-image: url('../img/loading.gif'); + background-position: right 10px center; + background-repeat: no-repeat; + background-size: 16px; + padding-right: 30px; +} + +/* general styles for the content area */ + +.section { + display: block; + padding: 30px; + color: #555; + margin-bottom: 24px; + &.hidden { + display: none !important; + } +} + +.sub-section { + position: relative; + margin-top: 10px; + margin-left: 27px; + margin-bottom: 10px; +} + +/* no top border for first settings item */ + +#app-content > .section:first-child { + border-top: none; +} + +/* heading styles */ + +h2 { + font-size: 20px; + font-weight: 300; + margin-bottom: 12px; + line-height: 140%; +} + +h3 { + font-size: 15px; + font-weight: 300; + margin: 12px 0; +} + +/* slight position correction of checkboxes and radio buttons */ + +.section input { + &[type='checkbox'], &[type='radio'] { + vertical-align: -2px; + margin-right: 4px; + } +} + +.appear { + opacity: 1; + -webkit-transition: opacity 500ms ease 0s; + -moz-transition: opacity 500ms ease 0s; + -ms-transition: opacity 500ms ease 0s; + -o-transition: opacity 500ms ease 0s; + transition: opacity 500ms ease 0s; + &.transparent { + opacity: 0; + } +} + +/* do not use italic typeface style, instead lighter color */ + +em { + font-style: normal; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)'; + opacity: .5; +} + +/* generic dropdown style */ + +.dropdown { + background: #eee; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + box-shadow: 0 1px 1px #777; + display: block; + margin-right: 0; + position: absolute; + right: 0; + width: 420px; + z-index: 500; + padding: 16px; +} + +/* generic tab styles */ + +.tabHeaders { + display: inline-block; + margin: 15px; + .tabHeader { + float: left; + padding: 5px; + cursor: pointer; + color: #888; + margin-bottom: 1px; + a { + color: #888; + margin-bottom: 1px; + } + &.selected { + font-weight: 600; + border-bottom: 1px solid #333; + } + &:hover { + border-bottom: 1px solid #333; + } + &.selected, &:hover { + margin-bottom: 0px; + color: #000; + a { + margin-bottom: 0px; + color: #000; + } + } + } +} + +.tabsContainer { + clear: left; + .tab { + padding: 0 15px 15px; + } +} + +/* popover menu styles (use together with 'bubble' class) */ + +.popovermenu { + .menuitem { + cursor: pointer; + vertical-align: middle; + > span { + cursor: pointer; + vertical-align: middle; + } + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)'; + filter: alpha(opacity = 50); + opacity: .5; + &:hover, &:focus, &.active { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + filter: alpha(opacity = 100); + opacity: 1; + } + img { + padding: initial; + } + } + a.menuitem, label.menuitem, .menuitem { + padding: 10px !important; + width: auto; + } + &.hidden { + display: none; + } + .menuitem { + display: flex !important; + line-height: 30px; + color: #000; + align-items: center; + .icon, .no-icon { + display: inline-block; + width: 16px; + height: 16px; + margin-right: 10px; + vertical-align: middle; + } + opacity: 0.5; + } + li:hover .menuitem { + opacity: 1; + } +} diff --git a/core/css/header.css b/core/css/header.css deleted file mode 100644 index d18181d13a0..00000000000 --- a/core/css/header.css +++ /dev/null @@ -1,384 +0,0 @@ -/* prevent ugly selection effect on accidental selection */ -#header, -#navigation, -#expanddiv { - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; -} - -/* removed until content-focusing issue is fixed */ -#skip-to-content a { - position: absolute; - left: -10000px; - top: auto; - width: 1px; - height: 1px; - overflow: hidden; -} -#skip-to-content a:focus { - left: 76px; - top: -9px; - color: #fff; - width: auto; - height: auto; -} - - - -/* HEADERS ------------------------------------------------------------------ */ - -#body-user #header, -#body-settings #header, -#body-public #header { - position: fixed; - top: 0; - left: 0; - right: 0; - z-index: 2000; - height: 45px; - line-height: 2.5em; - background-color: #0082c9; - box-sizing: border-box; -} - - - -/* LOGO and APP NAME -------------------------------------------------------- */ - -#nextcloud { - position: absolute; - top: 0; - left: 0; - padding: 5px; - padding-bottom: 0; - height: 45px; /* header height */ - box-sizing: border-box; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; - opacity: 1; -} -#nextcloud:focus { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=75)"; - opacity: .75; -} -#nextcloud:hover, -#nextcloud:active { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; - opacity: 1; -} - -#header .logo { - background-image: url('../img/logo-icon.svg?v=1'); - background-repeat: no-repeat; - background-size: 175px; - background-position: center; - width: 252px; - height: 120px; - margin: 0 auto; -} - -#header .logo-icon { - /* display logo so appname can be shown next to it */ - display: inline-block; - background-image: url('../img/logo-icon.svg?v=1'); - background-repeat: no-repeat; - background-position: center center; - width: 62px; - height: 34px; -} - -#header .header-appname-container { - display: inline-block; - position: absolute; - left: 70px; - height: 27px; - padding-top: 18px; - padding-right: 10px; -} - -/* hover effect for app switcher label */ -.header-appname-container .header-appname, -.menutoggle .icon-caret { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=75)"; - opacity: .75; -} -.menutoggle:hover .header-appname, -.menutoggle:hover .icon-caret, -.menutoggle:focus .header-appname, -.menutoggle:focus .icon-caret, -.menutoggle.active .header-appname, -.menutoggle.active .icon-caret { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; - opacity: 1; -} - -/* show appname next to logo */ -.header-appname { - display: inline-block; - position: relative; - color: #fff; - font-size: 16px; - font-weight: 300; - margin: 0; - margin-top: -24px; - padding: 7px 0 7px 5px; - vertical-align: middle; -} -/* show caret indicator next to logo to make clear it is tappable */ -#header .icon-caret { - display: inline-block; - width: 12px; - height: 12px; - margin: 0; - margin-top: -21px; - padding: 0; - vertical-align: middle; -} -/* do not show menu toggle on public share links as there is no menu */ -#body-public #header .icon-caret { - display: none; -} - - - -/* NAVIGATION --------------------------------------------------------------- */ - -#navigation { - position: fixed; - top: 45px; - left: 10px; - width: 265px; - max-height: 85%; - margin-top: 0; - padding-bottom: 10px; - background-color: rgba(255, 255, 255, .97); - box-shadow: 0 1px 10px rgba(150, 150, 150, .75); - border-radius: 3px; - border-top-left-radius: 0; - border-top-right-radius: 0; - display: none; - /*overflow-y: auto; - overflow-x: hidden;*/ - z-index: 2000; -} -/* arrow look */ -#navigation:after, #expanddiv:after { - bottom: 100%; - border: solid transparent; - content: " "; - height: 0; - width: 0; - position: absolute; - pointer-events: none; - border-color: rgba(0, 0, 0, 0); - border-bottom-color: rgba(255, 255, 255, .97); - border-width: 10px; - margin-left: -10px; -} -/* position of dropdown arrow */ -#navigation:after { - left: 47%; -} -#expanddiv:after { - right: 15px; -} - -#navigation, #navigation * { - box-sizing:border-box; -} -#navigation li { - display: inline-block; -} -#navigation a { - position: relative; - width: 80px; - height: 80px; - display: inline-block; - text-align: center; - padding: 20px 0; -} -#navigation a span { - display: inline-block; - font-size: 13px; - padding-bottom: 0; - padding-left: 0; - width: 80px; - text-align: center; - color: #000; - white-space:nowrap; - overflow:hidden; - text-overflow:ellipsis; -} - /* icon opacity and hover effect */ - #navigation a svg, - #navigation a span { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; - opacity: .5; - } - #navigation a:hover svg, - #navigation a:focus svg, - #navigation a:hover span, - #navigation a:focus span, - #navigation a.active svg, - #navigation a.active span, - #apps-management a:hover svg, - #apps-management a:focus svg, - #apps-management a.active svg, - #apps-management a:hover span, - #apps-management a:focus span, - #apps-management a.active span { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; - opacity: 1; - } - -#navigation .app-icon { - margin: 0 auto; - padding: 0; - max-height: 32px; - max-width: 32px; -} - -/* Apps management */ -#apps-management { - min-height: initial; - height: initial; - margin: 0; -} -#apps-management a svg, -#apps-management a span { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; - opacity: .3; -} - - -/* loading feedback for apps */ -#navigation .app-loading .icon-loading-dark { - display: inline !important; - position: absolute; - top: 20px; - left: 24px; - width: 32px; - height: 32px; -} -#navigation .app-loading .app-icon { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; - opacity: 0; -} - -#apps { - max-height: calc(100vh - 100px); - overflow:auto; -} - - -/* USER MENU -----------------------------------------------------------------*/ - -/* info part on the right, used e.g. for info on who shared something */ -.header-right { - position: absolute; - right: 0; - padding: 7px 5px; - color: #fff; - height: 100%; - max-width: 80%; - white-space: nowrap; - box-sizing: border-box; -} - -/* Profile picture in header */ -#header .avatardiv { - float: left; - display: inline-block; - margin-right: 8px; - cursor: pointer; - height: 32px; - width: 32px; -} -#header .avatardiv img { - opacity: 1; - cursor: pointer; -} - -#settings { - float: right; - color: #ddd; - cursor: pointer; -} -#settings .icon-loading-small-dark { - display: inline-block; - margin-bottom: -3px; - margin-right: 6px; - background-size: 16px 16px; -} -#expand { - display: block; - padding: 7px 30px 6px 10px; - cursor: pointer; -} -#expand * { - cursor: pointer; -} -#expand:hover, -#expand:focus, -#expand:active { - color: #fff; -} -#expand img { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; - opacity: .7; - margin-bottom: -2px; -} -#expand:hover img, -#expand:focus img, -#expand:active img { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; - opacity: 1; -} -#expand .icon-caret { - margin-top: 0; -} -#expanddiv { - position: absolute; - right: 13px; - top: 45px; - z-index: 2000; - display: none; - background: rgb(255, 255, 255); - box-shadow: 0 1px 10px rgba(150, 150, 150, .75); - border-radius: 3px; - border-top-left-radius: 0; - border-top-right-radius: 0; - box-sizing: border-box; -} -#expanddiv:after { - border-color: rgba(0, 0, 0, 0); - border-bottom-color: rgba(255, 255, 255, 1); -} - #expanddiv a { - display: block; - height: 40px; - color: #000; - padding: 4px 12px 0; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; - opacity: .5; - box-sizing: border-box; - } - #expanddiv a img { - margin-bottom: -3px; - margin-right: 6px; - } - #expanddiv a:hover, - #expanddiv a:focus, - #expanddiv a:active, - #expanddiv a.active { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; - opacity: 1; - } - -/* do not show display name when profile picture is present */ -#header .avatardiv.avatardiv-shown + #expandDisplayName { - display: none; -} -#header #expand { - display: block; -} diff --git a/core/css/header.scss b/core/css/header.scss new file mode 100644 index 00000000000..8035f7e568a --- /dev/null +++ b/core/css/header.scss @@ -0,0 +1,423 @@ +/* prevent ugly selection effect on accidental selection */ + +#header, #navigation, #expanddiv { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +/* removed until content-focusing issue is fixed */ + +#skip-to-content a { + position: absolute; + left: -10000px; + top: auto; + width: 1px; + height: 1px; + overflow: hidden; + &:focus { + left: 76px; + top: -9px; + color: #fff; + width: auto; + height: auto; + } +} + +/* HEADERS ------------------------------------------------------------------ */ + +#body-user #header, #body-settings #header, #body-public #header { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 2000; + height: 45px; + line-height: 2.5em; + background-color: #0082c9; + box-sizing: border-box; +} + +/* LOGO and APP NAME -------------------------------------------------------- */ + +#nextcloud { + position: absolute; + top: 0; + left: 0; + padding: 5px; + padding-bottom: 0; + height: 45px; + /* header height */ + box-sizing: border-box; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + &:focus { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=75)'; + opacity: .75; + } + &:hover, &:active { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } +} + +#header { + .logo { + background-image: url('../img/logo-icon.svg?v=1'); + background-repeat: no-repeat; + background-size: 175px; + background-position: center; + width: 252px; + height: 120px; + margin: 0 auto; + } + .logo-icon { + /* display logo so appname can be shown next to it */ + display: inline-block; + background-image: url('../img/logo-icon.svg?v=1'); + background-repeat: no-repeat; + background-position: center center; + width: 62px; + height: 34px; + } + .header-appname-container { + display: inline-block; + position: absolute; + left: 70px; + height: 27px; + padding-top: 18px; + padding-right: 10px; + } +} + +/* hover effect for app switcher label */ + +.header-appname-container .header-appname { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=75)'; + opacity: .75; +} + +.menutoggle { + .icon-caret { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=75)'; + opacity: .75; + } + &:hover { + .header-appname, .icon-caret { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } + } + &:focus { + .header-appname, .icon-caret { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } + } + &.active { + .header-appname, .icon-caret { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } + } +} + +/* show appname next to logo */ + +.header-appname { + display: inline-block; + position: relative; + color: #fff; + font-size: 16px; + font-weight: 300; + margin: 0; + margin-top: -24px; + padding: 7px 0 7px 5px; + vertical-align: middle; +} + +/* show caret indicator next to logo to make clear it is tappable */ + +#header .icon-caret { + display: inline-block; + width: 12px; + height: 12px; + margin: 0; + margin-top: -21px; + padding: 0; + vertical-align: middle; +} + +/* do not show menu toggle on public share links as there is no menu */ + +#body-public #header .icon-caret { + display: none; +} + +/* NAVIGATION --------------------------------------------------------------- */ + +#navigation { + position: fixed; + top: 45px; + left: 10px; + width: 265px; + max-height: 85%; + margin-top: 0; + padding-bottom: 10px; + background-color: rgba(255, 255, 255, 0.97); + box-shadow: 0 1px 10px rgba(150, 150, 150, 0.75); + border-radius: 3px; + border-top-left-radius: 0; + border-top-right-radius: 0; + display: none; + /*overflow-y: auto; + overflow-x: hidden;*/ + z-index: 2000; + &:after { + bottom: 100%; + border: solid transparent; + content: ' '; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + border-color: rgba(0, 0, 0, 0); + border-bottom-color: rgba(255, 255, 255, 0.97); + border-width: 10px; + margin-left: -10px; + } +} + +/* arrow look */ + +#expanddiv:after { + bottom: 100%; + border: solid transparent; + content: ' '; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + border-color: rgba(0, 0, 0, 0); + border-bottom-color: rgba(255, 255, 255, 0.97); + border-width: 10px; + margin-left: -10px; +} + +/* position of dropdown arrow */ + +#navigation:after { + left: 47%; +} + +#expanddiv:after { + right: 15px; +} + +#navigation { + box-sizing: border-box; + * { + box-sizing: border-box; + } + li { + display: inline-block; + } + a { + position: relative; + width: 80px; + height: 80px; + display: inline-block; + text-align: center; + padding: 20px 0; + span { + display: inline-block; + font-size: 13px; + padding-bottom: 0; + padding-left: 0; + width: 80px; + text-align: center; + color: #000; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + svg, span { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)'; + opacity: .5; + } + &:hover svg, &:focus svg, &:hover span, &:focus span { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } + &.active { + svg, span { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } + } + } +} + +/* icon opacity and hover effect */ + +#apps-management a { + &:hover svg, &:focus svg, &.active svg, &:hover span, &:focus span, &.active span { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } +} + +#navigation .app-icon { + margin: 0 auto; + padding: 0; + max-height: 32px; + max-width: 32px; +} + +/* Apps management */ + +#apps-management { + min-height: initial; + height: initial; + margin: 0; + a { + svg, span { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=30)'; + opacity: .3; + } + } +} + +/* loading feedback for apps */ + +#navigation .app-loading { + .icon-loading-dark { + display: inline !important; + position: absolute; + top: 20px; + left: 24px; + width: 32px; + height: 32px; + } + .app-icon { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=0)'; + opacity: 0; + } +} + +#apps { + max-height: calc(100vh - 100px); + overflow: auto; +} + +/* USER MENU -----------------------------------------------------------------*/ + +/* info part on the right, used e.g. for info on who shared something */ + +.header-right { + position: absolute; + right: 0; + padding: 7px 5px; + color: #fff; + height: 100%; + max-width: 80%; + white-space: nowrap; + box-sizing: border-box; +} + +/* Profile picture in header */ + +#header .avatardiv { + float: left; + display: inline-block; + margin-right: 8px; + cursor: pointer; + height: 32px; + width: 32px; + img { + opacity: 1; + cursor: pointer; + } +} + +#settings { + float: right; + color: #ddd; + cursor: pointer; + .icon-loading-small-dark { + display: inline-block; + margin-bottom: -3px; + margin-right: 6px; + background-size: 16px 16px; + } +} + +#expand { + display: block; + padding: 7px 30px 6px 10px; + cursor: pointer; + * { + cursor: pointer; + } + &:hover, &:focus, &:active { + color: #fff; + } + img { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=70)'; + opacity: .7; + margin-bottom: -2px; + } + &:hover img, &:focus img, &:active img { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } + .icon-caret { + margin-top: 0; + } +} + +#expanddiv { + position: absolute; + right: 13px; + top: 45px; + z-index: 2000; + display: none; + background: rgb(255, 255, 255); + box-shadow: 0 1px 10px rgba(150, 150, 150, 0.75); + border-radius: 3px; + border-top-left-radius: 0; + border-top-right-radius: 0; + box-sizing: border-box; + &:after { + border-color: rgba(0, 0, 0, 0); + border-bottom-color: rgba(255, 255, 255, 1); + } + a { + display: block; + height: 40px; + color: #000; + padding: 4px 12px 0; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)'; + opacity: .5; + box-sizing: border-box; + img { + margin-bottom: -3px; + margin-right: 6px; + } + &:hover, &:focus, &:active, &.active { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } + } +} + +/* do not show display name when profile picture is present */ + +#header { + .avatardiv.avatardiv-shown + #expandDisplayName { + display: none; + } + #expand { + display: block; + } +} diff --git a/core/css/icons.css b/core/css/icons.scss index a2869dfac1c..9afb5630181 100644 --- a/core/css/icons.css +++ b/core/css/icons.scss @@ -1,35 +1,23 @@ -[class^="icon-"], [class*=" icon-"] { +[class^='icon-'], [class*=' icon-'] { background-repeat: no-repeat; background-position: center; min-width: 16px; min-height: 16px; } - - - /* general assets */ .icon-breadcrumb { background-image: url('../img/breadcrumb.svg?v=1'); } -.loading, -.loading-small, -.icon-loading, -.icon-loading-dark, -.icon-loading-small, -.icon-loading-small-dark { +.loading, .loading-small, .icon-loading, .icon-loading-dark, .icon-loading-small, .icon-loading-small-dark { position: relative; } -.loading:after, -.loading-small:after, -.icon-loading:after, -.icon-loading-dark:after, -.icon-loading-small:after, -.icon-loading-small-dark:after { + +.loading:after, .loading-small:after, .icon-loading:after, .icon-loading-dark:after, .icon-loading-small:after, .icon-loading-small-dark:after { z-index: 2; - content: ""; + content: ''; height: 30px; width: 30px; margin: -16px 0 0 -16px; @@ -43,41 +31,39 @@ -ms-transform-origin: center; transform-origin: center; } -.loading:after, -.loading-small:after, -.icon-loading:after, -.icon-loading-dark:after, -.icon-loading-small:after, -.icon-loading-small-dark:after { - border: 2px solid rgba(150, 150, 150, .5); + +.loading:after, .loading-small:after, .icon-loading:after, .icon-loading-dark:after, .icon-loading-small:after, .icon-loading-small-dark:after { + border: 2px solid rgba(150, 150, 150, 0.5); border-top-color: rgb(100, 100, 100); } -.icon-loading-dark:after, -.icon-loading-small-dark:after { - border: 2px solid rgba(187, 187, 187, .5); +.icon-loading-dark:after, .icon-loading-small-dark:after { + border: 2px solid rgba(187, 187, 187, 0.5); border-top-color: #bbb; } -.icon-loading-small:after, -.icon-loading-small-dark:after { +.icon-loading-small:after, .icon-loading-small-dark:after { height: 14px; width: 14px; margin: -8px 0 0 -8px; } /* Css replaced elements don't have ::after nor ::before */ + img.icon-loading, object.icon-loading, video.icon-loading, button.icon-loading, textarea.icon-loading, input.icon-loading, select.icon-loading { - background-image: url("../img/loading.gif"); + background-image: url('../img/loading.gif'); } + img.icon-loading-dark, object.icon-loading-dark, video.icon-loading-dark, button.icon-loading-dark, textarea.icon-loading-dark, input.icon-loading-dark, select.icon-loading-dark { - background-image: url("../img/loading-dark.gif"); + background-image: url('../img/loading-dark.gif'); } + img.icon-loading-small, object.icon-loading-small, video.icon-loading-small, button.icon-loading-small, textarea.icon-loading-small, input.icon-loading-small, select.icon-loading-small { - background-image: url("../img/loading-small.gif"); + background-image: url('../img/loading-small.gif'); } + img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading-small-dark, button.icon-loading-small-dark, textarea.icon-loading-small-dark, input.icon-loading-small-dark, select.icon-loading-small-dark { - background-image: url("../img/loading-small-dark.gif"); + background-image: url('../img/loading-small-dark.gif'); } @-webkit-keyframes rotate { @@ -85,29 +71,31 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- -webkit-transform: rotate(0deg); transform: rotate(0deg); } + to { -webkit-transform: rotate(360deg); transform: rotate(360deg); } } + + @keyframes rotate { from { -webkit-transform: rotate(0deg); transform: rotate(0deg); } + to { -webkit-transform: rotate(360deg); transform: rotate(360deg); } } + .icon-32 { background-size: 32px !important; } - - - /* action icons */ .icon-add { @@ -117,12 +105,15 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-audio { background-image: url('../img/actions/audio.svg?v=1'); } + .icon-audio-white { background-image: url('../img/actions/audio-white.svg?v=2'); } + .icon-audio-off { background-image: url('../img/actions/audio-off.svg?v=1'); } + .icon-audio-off-white { background-image: url('../img/actions/audio-off-white.svg?v=1'); } @@ -130,6 +121,7 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-caret { background-image: url('../img/actions/caret.svg?v=1'); } + .icon-caret-dark { background-image: url('../img/actions/caret-dark.svg?v=1'); } @@ -137,9 +129,11 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-checkmark { background-image: url('../img/actions/checkmark.svg?v=1'); } + .icon-checkmark-white { background-image: url('../img/actions/checkmark-white.svg?v=1'); } + .icon-checkmark-color { background-image: url('../img/actions/checkmark-color.svg?v=1'); } @@ -159,19 +153,23 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-confirm { background-image: url('../img/actions/confirm.svg?v=2'); } + .icon-confirm-white { background-image: url('../img/actions/confirm-white.svg?v=2'); } -.icon-delete, -.icon-delete.no-permission:hover, -.icon-delete.no-permission:focus { +.icon-delete { background-image: url('../img/actions/delete.svg?v=1'); + &.no-permission { + &:hover, &:focus { + background-image: url('../img/actions/delete.svg?v=1'); + } + } + &:hover, &:focus { + background-image: url('../img/actions/delete-hover.svg?v=1'); + } } -.icon-delete:hover, -.icon-delete:focus { - background-image: url('../img/actions/delete-hover.svg?v=1'); -} + .icon-delete-white { background-image: url('../img/actions/delete-white.svg?v=1'); } @@ -183,6 +181,7 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-download { background-image: url('../img/actions/download.svg?v=1'); } + .icon-download-white { background-image: url('../img/actions/download-white.svg?v=1'); } @@ -194,9 +193,11 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-error { background-image: url('../img/actions/error.svg?v=1'); } + .icon-error-white { background-image: url('../img/actions/error-white.svg?v=1'); } + .icon-error-color { background-image: url('../img/actions/error-color.svg?v=1'); } @@ -208,6 +209,7 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-fullscreen { background-image: url('../img/actions/fullscreen.svg?v=1'); } + .icon-fullscreen-white { background-image: url('../img/actions/fullscreen-white.svg?v=2'); } @@ -219,6 +221,7 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-info { background-image: url('../img/actions/info.svg?v=1'); } + .icon-info-white { background-image: url('../img/actions/info-white.svg?v=1'); } @@ -238,6 +241,7 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-more { background-image: url('../img/actions/more.svg?v=1'); } + .icon-more-white { background-image: url('../img/actions/more-white.svg?v=1'); } @@ -249,6 +253,7 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-pause { background-image: url('../img/actions/pause.svg?v=1'); } + .icon-pause-big { background-image: url('../img/actions/pause-big.svg?v=1'); } @@ -256,15 +261,19 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-play { background-image: url('../img/actions/play.svg?v=1'); } + .icon-play-add { background-image: url('../img/actions/play-add.svg?v=1'); } + .icon-play-big { background-image: url('../img/actions/play-big.svg?v=1'); } + .icon-play-next { background-image: url('../img/actions/play-next.svg?v=1'); } + .icon-play-previous { background-image: url('../img/actions/play-previous.svg?v=1'); } @@ -280,6 +289,7 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-search { background-image: url('../img/actions/search.svg?v=1'); } + .icon-search-white { background-image: url('../img/actions/search-white.svg?v=1'); } @@ -291,6 +301,7 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-share { background-image: url('../img/actions/share.svg?v=1'); } + .icon-shared { background-image: url('../img/actions/shared.svg?v=1'); } @@ -298,26 +309,32 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-sound { background-image: url('../img/actions/sound.svg?v=1'); } + .icon-sound-off { background-image: url('../img/actions/sound-off.svg?v=1'); } -.icon-favorite { +.icon-favorite { background-image: url('../img/actions/star-dark.svg?v=1'); } -.icon-star, -.icon-starred:hover, -.icon-starred:focus { +.icon-star { background-image: url('../img/actions/star.svg?v=1'); } -.icon-starred, -.icon-star:hover, -.icon-star:focus { +.icon-starred { + &:hover, &:focus { + background-image: url('../img/actions/star.svg?v=1'); + } background-image: url('../img/actions/starred.svg?v=1'); } +.icon-star { + &:hover, &:focus { + background-image: url('../img/actions/starred.svg?v=1'); + } +} + .icon-tag { background-image: url('../img/actions/tag.svg?v=1'); } @@ -329,9 +346,11 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-triangle-e { background-image: url('../img/actions/triangle-e.svg?v=1'); } + .icon-triangle-n { background-image: url('../img/actions/triangle-n.svg?v=1'); } + .icon-triangle-s { background-image: url('../img/actions/triangle-s.svg?v=1'); } @@ -339,6 +358,7 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-upload { background-image: url('../img/actions/upload.svg?v=1'); } + .icon-upload-white { background-image: url('../img/actions/upload-white.svg?v=1'); } @@ -350,12 +370,15 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-video { background-image: url('../img/actions/video.svg?v=1'); } + .icon-video-white { background-image: url('../img/actions/video-white.svg?v=2'); } + .icon-video-off { background-image: url('../img/actions/video-off.svg?v=1'); } + .icon-video-off-white { background-image: url('../img/actions/video-off-white.svg?v=1'); } @@ -363,25 +386,27 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-view-close { background-image: url('../img/actions/view-close.svg?v=1'); } + .icon-view-download { background-image: url('../img/actions/view-download.svg?v=1'); } + .icon-view-next { background-image: url('../img/actions/view-next.svg?v=1'); } + .icon-view-pause { background-image: url('../img/actions/view-pause.svg?v=1'); } + .icon-view-play { background-image: url('../img/actions/view-play.svg?v=1'); } + .icon-view-previous { background-image: url('../img/actions/view-previous.svg?v=1'); } - - - /* places icons */ .icon-calendar-dark { @@ -395,20 +420,21 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- .icon-files { background-image: url('../img/places/files.svg?v=1'); } + .icon-files-dark { background-image: url('../img/places/files-dark.svg?v=1'); } -.icon-file, -.icon-filetype-text { + +.icon-file, .icon-filetype-text { background-image: url('../img/filetypes/text.svg?v=1'); } -.icon-folder, -.icon-filetype-folder { + +.icon-folder, .icon-filetype-folder { background-image: url('../img/filetypes/folder.svg?v=1'); } .icon-filetype-folder-drag-accept { - background-image: url('../img/filetypes/folder-drag-accept.svg?v=1')!important; + background-image: url('../img/filetypes/folder-drag-accept.svg?v=1') !important; } .icon-home { diff --git a/core/css/inputs.css b/core/css/inputs.css deleted file mode 100644 index 4ca6c823cc9..00000000000 --- a/core/css/inputs.css +++ /dev/null @@ -1,472 +0,0 @@ -/* INPUTS */ - -/* specifically override browser styles */ -input, textarea, select, button { - font-family: 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif; -} - -.select2-container-multi .select2-choices .select2-search-field input, -.select2-search input, -.ui-widget { - font-family: 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif !important; -} - -input[type="text"], -input[type="password"], -input[type="search"], -input[type="number"], -input[type="email"], -input[type="tel"], -input[type="url"], -input[type="time"], -input[type="date"], -textarea, -select, -button, .button, -input[type="submit"], -input[type="button"], -#quota, -.pager li a { - width: 130px; - margin: 3px 3px 3px 0; - padding: 7px 6px 5px; - font-size: 13px; - background-color: #fff; - color: #333; - border: 1px solid #ddd; - outline: none; - border-radius: 3px; -} -input[type="hidden"] { - height: 0; - width: 0; -} -input[type="text"], -input[type="password"], -input[type="search"], -input[type="number"], -input[type="email"], -input[type="tel"], -input[type="url"], -input[type="time"], -textarea { - background: #fff; - color: #555; - cursor: text; - font-family: inherit; /* use default ownCloud font instead of default textarea monospace */ -} -input[type="text"], -input[type="password"], -input[type="search"], -input[type="number"], -input[type="email"], -input[type="tel"], -input[type="url"], -input[type="time"] { - -webkit-appearance:textfield; - -moz-appearance:textfield; - box-sizing:content-box; -} -input[type="text"]:hover, input[type="text"]:focus, input[type="text"]:active, -input[type="password"]:hover, input[type="password"]:focus, input[type="password"]:active, -input[type="number"]:hover, input[type="number"]:focus, input[type="number"]:active, -input[type="search"]:hover, input[type="search"]:focus, input[type="search"]:active, -input[type="email"]:hover, input[type="email"]:focus, input[type="email"]:active, -input[type="tel"]:hover, input[type="tel"]:focus, input[type="tel"]:active, -input[type="url"]:hover, input[type="url"]:focus, input[type="url"]:active, -input[type="time"]:hover, input[type="time"]:focus, input[type="time"]:active, -textarea:hover, textarea:focus, textarea:active { - color: #333; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; - opacity: 1; -} - -input[type="checkbox"].checkbox { - position: absolute; - left:-10000px; - top: auto; - width: 1px; - height: 1px; - overflow: hidden; -} - -input[type="checkbox"].checkbox + label:before { - content: ""; - display: inline-block; - - height: 20px; - width: 20px; - vertical-align: middle; - - background: url('../img/actions/checkbox.svg') left top no-repeat; -} - -input[type="checkbox"].checkbox:disabled +label:before { opacity: .6; } - -input[type="checkbox"].checkbox.u-left +label:before { float: left; } -input[type="checkbox"].checkbox.u-hidden + label:before { display: none; } - -input[type="checkbox"].checkbox:checked + label:before { - background-image: url('../img/actions/checkbox-checked.svg'); -} - -input[type="checkbox"].checkbox:indeterminate + label:before { - background-image: url('../img/actions/checkbox-mixed.svg'); -} - -input[type="checkbox"].checkbox:disabled + label:before { - background-image: url('../img/actions/checkbox-disabled.svg'); -} - -input[type="checkbox"].checkbox:checked:disabled + label:before { - background-image: url('../img/actions/checkbox-checked-disabled.svg'); -} - -input[type="checkbox"].checkbox:indeterminate:disabled + label:before { - background-image: url('../img/actions/checkbox-mixed-disabled.svg'); -} - -input[type="checkbox"].checkbox--white + label:before { - background-image: url('../img/actions/checkbox-white.svg'); -} - -input[type="checkbox"].checkbox--white:checked + label:before { - background-image: url('../img/actions/checkbox-checked-white.svg'); -} - -input[type="checkbox"].checkbox--white:indeterminate + label:before { - background-image: url('../img/actions/checkbox-mixed-white.svg'); -} - -input[type="checkbox"].checkbox--white:disabled + label:before { - background-image: url('../img/actions/checkbox-disabled-white.svg'); -} - -input[type="checkbox"].checkbox--white:checked:disabled + label:before { - background-image: url('../img/actions/checkbox-checked-disabled.svg'); -} - -input[type="checkbox"].checkbox--white:indeterminate:disabled + label:before { - background-image: url('../img/actions/checkbox-mixed-disabled.svg'); -} - -input[type="checkbox"].checkbox:hover+label:before, input[type="checkbox"]:focus+label:before { - color:#111 !important; -} - -input[type="radio"].radio { - position: absolute; - left:-10000px; - top: auto; - width: 1px; - height: 1px; - overflow: hidden; -} - -input[type="radio"].radio + label:before { - content: ""; - display: inline-block; - - height: 20px; - width: 20px; - vertical-align: middle; - - background: url('../img/actions/radio.svg') left top no-repeat; -} - -input[type="radio"].radio:checked + label:before { - background-image: url('../img/actions/radio-checked.svg'); -} - -input[type="radio"].radio:disabled + label:before { - background-image: url('../img/actions/radio-disabled.svg'); -} - -input[type="radio"].radio:checked:disabled + label:before { - background-image: url('../img/actions/radio-checked-disabled.svg'); -} - -input[type="radio"].radio--white + label:before { - background-image: url('../img/actions/radio-white.svg'); -} - -input[type="radio"].radio--white:checked + label:before { - background-image: url('../img/actions/radio-checked-white.svg'); -} - -input[type="radio"].radio--white:disabled + label:before { - background-image: url('../img/actions/radio-disabled.svg'); -} - -input[type="radio"].radio--white:checked:disabled + label:before { - background-image: url('../img/actions/radio-checked-disabled.svg'); -} - -input[type="time"] { - width: initial; - height: 31px; - box-sizing: border-box; -} - -select { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background: url('../../core/img/actions/triangle-s.svg') no-repeat right 8px center rgba(240, 240, 240, 0.90); - outline: 0; - padding-right: 24px !important; -} - -select:hover { - background-color: #fefefe; -} - - -/* select2 adjustments */ -#select2-drop { - margin-top: -2px; -} -#select2-drop.select2-drop-active { - border-color: #ddd; -} -#select2-drop .avatar { - display: inline-block; - margin-right: 8px; - vertical-align: middle; -} -#select2-drop .avatar img, -.select2-chosen .avatar img, -#select2-drop .avatar, -.select2-chosen .avatar { - cursor: pointer; -} -#select2-drop .select2-search input { - width: calc(100% - 14px); - min-height: auto; - background: url('../img/actions/search.svg') no-repeat right center !important; - background-origin: content-box !important; -} -#select2-drop .select2-results { - max-height: 250px; - margin: 0; - padding: 0; -} -#select2-drop .select2-results .select2-result-label { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -#select2-drop .select2-results .select2-result-label span { - cursor: pointer; -} -#select2-drop .select2-results .select2-result, -#select2-drop .select2-results .select2-no-results, -#select2-drop .select2-results .select2-searching { - position: relative; - display: list-item; - padding: 12px; - background-color: #fff; - cursor: pointer; - color: #222; -} -#select2-drop .select2-results .select2-result.select2-selected { - background-color: #f8f8f8; -} -#select2-drop .select2-results .select2-result.select2-highlighted { - background-color: #f8f8f8; - color: #000; -} - -.select2-container-multi .select2-choices, -.select2-container-multi.select2-container-active .select2-choices, -.select2-container .select2-choice { - box-shadow: none; - white-space: nowrap; - text-overflow: ellipsis; - background: #fff; - color: #555; - box-sizing: content-box; - border-radius: 3px; - border: 1px solid #ddd; - margin: 0; - padding: 2px 0; - min-height: auto; -} -.select2-container-multi .select2-choices .select2-search-choice, -.select2-container-multi.select2-container-active .select2-choices .select2-search-choice, -.select2-container .select2-choice .select2-search-choice { - line-height: 20px; - padding-left: 5px; - background-image: none; - background-color: #f8f8f8; - border-color: #f8f8f8; -} -.select2-container-multi .select2-choices .select2-search-choice.select2-search-choice-focus, .select2-container-multi .select2-choices .select2-search-choice:hover, -.select2-container-multi.select2-container-active .select2-choices .select2-search-choice.select2-search-choice-focus, -.select2-container-multi.select2-container-active .select2-choices .select2-search-choice:hover, -.select2-container .select2-choice .select2-search-choice.select2-search-choice-focus, -.select2-container .select2-choice .select2-search-choice:hover { - background-color: #f0f0f0; - border-color: #f0f0f0; -} -.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close, -.select2-container-multi.select2-container-active .select2-choices .select2-search-choice .select2-search-choice-close, -.select2-container .select2-choice .select2-search-choice .select2-search-choice-close { - display: none; -} -.select2-container-multi .select2-choices .select2-search-field input, -.select2-container-multi.select2-container-active .select2-choices .select2-search-field input, -.select2-container .select2-choice .select2-search-field input { - line-height: 20px; -} - -.select2-container { - margin: 3px 3px 3px 0; -} -.select2-container.select2-container-multi .select2-choices { - display: flex; - flex-wrap: wrap; -} -.select2-container.select2-container-multi .select2-choices li { - float: none; -} -.select2-container .select2-choice { - padding-left: 38px; -} -.select2-container .select2-choice .select2-arrow { - background: none; - border-radius: 0; - border: none; -} -.select2-container .select2-choice .select2-arrow b { - background: url('../img/actions/triangle-s.svg') no-repeat center !important; - opacity: .5; -} -.select2-container .select2-choice:hover .select2-arrow b, -.select2-container .select2-choice:focus .select2-arrow b, -.select2-container .select2-choice:active .select2-arrow b { - opacity: .7; -} - - -/* jQuery UI fixes */ -.ui-menu { - padding: 0; -} -.ui-menu .ui-menu-item a.ui-state-focus, .ui-menu .ui-menu-item a.ui-state-active { - font-weight: inherit; - margin: 0; -} -.ui-widget-content { - background: #fff; - border-top: none; -} -.ui-corner-all { - border-radius: 0; - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; -} -.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { - border: none; - background: #f8f8f8; -} - - - -/* correctly align images inside of buttons */ -input img, button img, .button img { - vertical-align: text-bottom; -} - -input[type="submit"].enabled { - background-color: #66f866; - border: 1px solid #5e5; -} - -.input-button-inline { - position: absolute !important; - right: 0; - background-color: transparent !important; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; - opacity: .3; -} - - -/* BUTTONS */ -input[type="submit"], input[type="button"], -button, .button, -#quota, select, .pager li a { - width: auto; - min-width: 25px; - padding: 5px; - background-color: rgba(240,240,240,.9); - font-weight: 600; - color: #555; - border: 1px solid rgba(240,240,240,.9); - cursor: pointer; -} -select, .button.multiselect { - font-weight: 400; -} -input[type="submit"]:hover, input[type="submit"]:focus, -input[type="button"]:hover, input[type="button"]:focus, -button:hover, button:focus, -.button:hover, .button:focus, -.button a:focus, -select:hover, select:focus, select:active { - background-color: rgba(255, 255, 255, .95); - color: #111; -} -input[type="submit"] img, input[type="button"] img, button img, .button img { cursor:pointer; } -#header .button { - border: none; - box-shadow: none; -} - -/* disabled input fields and buttons */ -input:disabled, input:disabled:hover, input:disabled:focus, -button:disabled, button:disabled:hover, button:disabled:focus, -.button:disabled, .button:disabled:hover, .button:disabled:focus, -a.disabled, a.disabled:hover, a.disabled:focus, -textarea:disabled { - background-color: rgba(230,230,230,.9); - color: #999; - cursor: default; -} -input:disabled+label, input:disabled:hover+label, input:disabled:focus+label { - color: #999 !important; - cursor: default; -} - -/* Primary action button, use sparingly */ -.primary, input[type="submit"].primary, input[type="button"].primary, button.primary, .button.primary { - border: 1px solid #0082c9; - background-color: #00a2e9; - color: #fff; -} -.primary:hover, input[type="submit"].primary:hover, input[type="button"].primary:hover, button.primary:hover, .button.primary:hover, -.primary:focus, input[type="submit"].primary:focus, input[type="button"].primary:focus, button.primary:focus, .button.primary:focus { - background-color: #0092d9; - color: #fff; -} -.primary:active, input[type="submit"].primary:active, input[type="button"].primary:active, button.primary:active, .button.primary:active, -.primary:disabled, input[type="submit"].primary:disabled, input[type="button"].primary:disabled, button.primary:disabled, .button.primary:disabled, -.primary:disabled:hover, input[type="submit"].primary:disabled:hover, input[type="button"].primary:disabled:hover, button.primary:disabled:hover, .button.primary:disabled:hover, -.primary:disabled:focus, input[type="submit"].primary:disabled:focus, input[type="button"].primary:disabled:focus, button.primary:disabled:focus, .button.primary:disabled:focus { - background-color: #00a2e9; - color: #bbb; -} - -@keyframes shake { - 0% { transform: translate(-5px, 0); } - 20% { transform: translate(5px, 0); } - 40% { transform: translate(-5px, 0); } - 60% { transform: translate(5px, 0); } - 80% { transform: translate(-5px, 0); } - 100% { transform: translate(5px, 0); } -} -.shake { - animation-name: shake; - animation-duration: .3s; - animation-timing-function: ease-out; -} diff --git a/core/css/inputs.scss b/core/css/inputs.scss new file mode 100644 index 00000000000..aa9c841f2b2 --- /dev/null +++ b/core/css/inputs.scss @@ -0,0 +1,782 @@ +/* INPUTS */ + +/* specifically override browser styles */ + +input, textarea, select, button { + font-family: 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif; +} + +.select2-container-multi .select2-choices .select2-search-field input, .select2-search input, .ui-widget { + font-family: 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif !important; +} + +input { + &[type='text'], &[type='password'], &[type='search'], &[type='number'], &[type='email'], &[type='tel'], &[type='url'], &[type='time'], &[type='date'] { + width: 130px; + margin: 3px 3px 3px 0; + padding: 7px 6px 5px; + font-size: 13px; + background-color: #fff; + color: #333; + border: 1px solid #ddd; + outline: none; + border-radius: 3px; + } +} + +textarea, select, button, .button { + width: 130px; + margin: 3px 3px 3px 0; + padding: 7px 6px 5px; + font-size: 13px; + background-color: #fff; + color: #333; + border: 1px solid #ddd; + outline: none; + border-radius: 3px; +} + +input { + &[type='submit'], &[type='button'] { + width: 130px; + margin: 3px 3px 3px 0; + padding: 7px 6px 5px; + font-size: 13px; + background-color: #fff; + color: #333; + border: 1px solid #ddd; + outline: none; + border-radius: 3px; + } +} + +#quota, .pager li a { + width: 130px; + margin: 3px 3px 3px 0; + padding: 7px 6px 5px; + font-size: 13px; + background-color: #fff; + color: #333; + border: 1px solid #ddd; + outline: none; + border-radius: 3px; +} + +input { + &[type='hidden'] { + height: 0; + width: 0; + } + &[type='text'], &[type='password'], &[type='search'], &[type='number'], &[type='email'], &[type='tel'], &[type='url'], &[type='time'] { + background: #fff; + color: #555; + cursor: text; + font-family: inherit; + /* use default ownCloud font instead of default textarea monospace */ + } +} + +textarea { + background: #fff; + color: #555; + cursor: text; + font-family: inherit; + /* use default ownCloud font instead of default textarea monospace */ +} + +input { + &[type='text'], &[type='password'], &[type='search'], &[type='number'], &[type='email'], &[type='tel'], &[type='url'], &[type='time'] { + -webkit-appearance: textfield; + -moz-appearance: textfield; + box-sizing: content-box; + } + &[type='text'] { + &:hover, &:focus, &:active { + color: #333; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } + } + &[type='password'] { + &:hover, &:focus, &:active { + color: #333; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } + } + &[type='number'] { + &:hover, &:focus, &:active { + color: #333; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } + } + &[type='search'] { + &:hover, &:focus, &:active { + color: #333; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } + } + &[type='email'] { + &:hover, &:focus, &:active { + color: #333; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } + } + &[type='tel'] { + &:hover, &:focus, &:active { + color: #333; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } + } + &[type='url'] { + &:hover, &:focus, &:active { + color: #333; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } + } + &[type='time'] { + &:hover, &:focus, &:active { + color: #333; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } + } +} + +textarea { + &:hover, &:focus, &:active { + color: #333; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } +} + +input { + &[type='checkbox'] { + &.checkbox { + position: absolute; + left: -10000px; + top: auto; + width: 1px; + height: 1px; + overflow: hidden; + + label:before { + content: ''; + display: inline-block; + height: 20px; + width: 20px; + vertical-align: middle; + background: url('../img/actions/checkbox.svg') left top no-repeat; + } + &:disabled + label:before { + opacity: .6; + } + &.u-left + label:before { + float: left; + } + &.u-hidden + label:before { + display: none; + } + &:checked + label:before { + background-image: url('../img/actions/checkbox-checked.svg'); + } + &:indeterminate + label:before { + background-image: url('../img/actions/checkbox-mixed.svg'); + } + &:disabled + label:before { + background-image: url('../img/actions/checkbox-disabled.svg'); + } + &:checked:disabled + label:before { + background-image: url('../img/actions/checkbox-checked-disabled.svg'); + } + &:indeterminate:disabled + label:before { + background-image: url('../img/actions/checkbox-mixed-disabled.svg'); + } + } + &.checkbox--white { + + label:before { + background-image: url('../img/actions/checkbox-white.svg'); + } + &:checked + label:before { + background-image: url('../img/actions/checkbox-checked-white.svg'); + } + &:indeterminate + label:before { + background-image: url('../img/actions/checkbox-mixed-white.svg'); + } + &:disabled + label:before { + background-image: url('../img/actions/checkbox-disabled-white.svg'); + } + &:checked:disabled + label:before { + background-image: url('../img/actions/checkbox-checked-disabled.svg'); + } + &:indeterminate:disabled + label:before { + background-image: url('../img/actions/checkbox-mixed-disabled.svg'); + } + } + &.checkbox:hover + label:before, &:focus + label:before { + color: #111 !important; + } + } + &[type='radio'] { + &.radio { + position: absolute; + left: -10000px; + top: auto; + width: 1px; + height: 1px; + overflow: hidden; + + label:before { + content: ''; + display: inline-block; + height: 20px; + width: 20px; + vertical-align: middle; + background: url('../img/actions/radio.svg') left top no-repeat; + } + &:checked + label:before { + background-image: url('../img/actions/radio-checked.svg'); + } + &:disabled + label:before { + background-image: url('../img/actions/radio-disabled.svg'); + } + &:checked:disabled + label:before { + background-image: url('../img/actions/radio-checked-disabled.svg'); + } + } + &.radio--white { + + label:before { + background-image: url('../img/actions/radio-white.svg'); + } + &:checked + label:before { + background-image: url('../img/actions/radio-checked-white.svg'); + } + &:disabled + label:before { + background-image: url('../img/actions/radio-disabled.svg'); + } + &:checked:disabled + label:before { + background-image: url('../img/actions/radio-checked-disabled.svg'); + } + } + } + &[type='time'] { + width: initial; + height: 31px; + box-sizing: border-box; + } +} + +select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: url('../../core/img/actions/triangle-s.svg') no-repeat right 8px center rgba(240, 240, 240, 0.9); + outline: 0; + padding-right: 24px !important; + &:hover { + background-color: #fefefe; + } +} + +/* select2 adjustments */ + +#select2-drop { + margin-top: -2px; + &.select2-drop-active { + border-color: #ddd; + } + .avatar { + display: inline-block; + margin-right: 8px; + vertical-align: middle; + img { + cursor: pointer; + } + } +} + +.select2-chosen .avatar img, #select2-drop .avatar, .select2-chosen .avatar { + cursor: pointer; +} + +#select2-drop { + .select2-search input { + width: calc(100% - 14px); + min-height: auto; + background: url('../img/actions/search.svg') no-repeat right center !important; + background-origin: content-box !important; + } + .select2-results { + max-height: 250px; + margin: 0; + padding: 0; + .select2-result-label { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + span { + cursor: pointer; + } + } + .select2-result, .select2-no-results, .select2-searching { + position: relative; + display: list-item; + padding: 12px; + background-color: #fff; + cursor: pointer; + color: #222; + } + .select2-result { + &.select2-selected { + background-color: #f8f8f8; + } + &.select2-highlighted { + background-color: #f8f8f8; + color: #000; + } + } + } +} + +.select2-container-multi { + .select2-choices, &.select2-container-active .select2-choices { + box-shadow: none; + white-space: nowrap; + text-overflow: ellipsis; + background: #fff; + color: #555; + box-sizing: content-box; + border-radius: 3px; + border: 1px solid #ddd; + margin: 0; + padding: 2px 0; + min-height: auto; + } +} + +.select2-container .select2-choice { + box-shadow: none; + white-space: nowrap; + text-overflow: ellipsis; + background: #fff; + color: #555; + box-sizing: content-box; + border-radius: 3px; + border: 1px solid #ddd; + margin: 0; + padding: 2px 0; + min-height: auto; +} + +.select2-container-multi { + .select2-choices .select2-search-choice, &.select2-container-active .select2-choices .select2-search-choice { + line-height: 20px; + padding-left: 5px; + background-image: none; + background-color: #f8f8f8; + border-color: #f8f8f8; + } +} + +.select2-container .select2-choice .select2-search-choice { + line-height: 20px; + padding-left: 5px; + background-image: none; + background-color: #f8f8f8; + border-color: #f8f8f8; +} + +.select2-container-multi { + .select2-choices .select2-search-choice { + &.select2-search-choice-focus, &:hover { + background-color: #f0f0f0; + border-color: #f0f0f0; + } + } + &.select2-container-active .select2-choices .select2-search-choice { + &.select2-search-choice-focus, &:hover { + background-color: #f0f0f0; + border-color: #f0f0f0; + } + } +} + +.select2-container .select2-choice .select2-search-choice { + &.select2-search-choice-focus, &:hover { + background-color: #f0f0f0; + border-color: #f0f0f0; + } +} + +.select2-container-multi { + .select2-choices .select2-search-choice .select2-search-choice-close, &.select2-container-active .select2-choices .select2-search-choice .select2-search-choice-close { + display: none; + } +} + +.select2-container .select2-choice .select2-search-choice .select2-search-choice-close { + display: none; +} + +.select2-container-multi { + .select2-choices .select2-search-field input, &.select2-container-active .select2-choices .select2-search-field input { + line-height: 20px; + } +} + +.select2-container { + .select2-choice .select2-search-field input { + line-height: 20px; + } + margin: 3px 3px 3px 0; + &.select2-container-multi .select2-choices { + display: flex; + flex-wrap: wrap; + li { + float: none; + } + } + .select2-choice { + padding-left: 38px; + .select2-arrow { + background: none; + border-radius: 0; + border: none; + b { + background: url('../img/actions/triangle-s.svg') no-repeat center !important; + opacity: .5; + } + } + &:hover .select2-arrow b, &:focus .select2-arrow b, &:active .select2-arrow b { + opacity: .7; + } + } +} + +/* jQuery UI fixes */ + +.ui-menu { + padding: 0; + .ui-menu-item a { + &.ui-state-focus, &.ui-state-active { + font-weight: inherit; + margin: 0; + } + } +} + +.ui-widget-content { + background: #fff; + border-top: none; +} + +.ui-corner-all { + border-radius: 0; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; +} + +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { + border: none; + background: #f8f8f8; +} + +/* correctly align images inside of buttons */ + +input img, button img, .button img { + vertical-align: text-bottom; +} + +input[type='submit'].enabled { + background-color: #66f866; + border: 1px solid #5e5; +} + +.input-button-inline { + position: absolute !important; + right: 0; + background-color: transparent !important; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=30)'; + opacity: .3; +} + +/* BUTTONS */ + +input { + &[type='submit'], &[type='button'] { + width: auto; + min-width: 25px; + padding: 5px; + background-color: rgba(240, 240, 240, 0.9); + font-weight: 600; + color: #555; + border: 1px solid rgba(240, 240, 240, 0.9); + cursor: pointer; + } +} + +button, .button, #quota, select, .pager li a { + width: auto; + min-width: 25px; + padding: 5px; + background-color: rgba(240, 240, 240, 0.9); + font-weight: 600; + color: #555; + border: 1px solid rgba(240, 240, 240, 0.9); + cursor: pointer; +} + +select, .button.multiselect { + font-weight: 400; +} + +input { + &[type='submit'] { + &:hover, &:focus { + background-color: rgba(255, 255, 255, 0.95); + color: #111; + } + } + &[type='button'] { + &:hover, &:focus { + background-color: rgba(255, 255, 255, 0.95); + color: #111; + } + } +} + +button { + &:hover, &:focus { + background-color: rgba(255, 255, 255, 0.95); + color: #111; + } +} + +.button { + &:hover, &:focus, a:focus { + background-color: rgba(255, 255, 255, 0.95); + color: #111; + } +} + +select { + &:hover, &:focus, &:active { + background-color: rgba(255, 255, 255, 0.95); + color: #111; + } +} + +input { + &[type='submit'] img, &[type='button'] img { + cursor: pointer; + } +} + +button img, .button img { + cursor: pointer; +} + +#header .button { + border: none; + box-shadow: none; +} + +/* disabled input fields and buttons */ + +input:disabled { + background-color: rgba(230, 230, 230, 0.9); + color: #999; + cursor: default; + &:hover, &:focus { + background-color: rgba(230, 230, 230, 0.9); + color: #999; + cursor: default; + } +} + +button:disabled { + background-color: rgba(230, 230, 230, 0.9); + color: #999; + cursor: default; + &:hover, &:focus { + background-color: rgba(230, 230, 230, 0.9); + color: #999; + cursor: default; + } +} + +.button:disabled { + background-color: rgba(230, 230, 230, 0.9); + color: #999; + cursor: default; + &:hover, &:focus { + background-color: rgba(230, 230, 230, 0.9); + color: #999; + cursor: default; + } +} + +a.disabled { + background-color: rgba(230, 230, 230, 0.9); + color: #999; + cursor: default; + &:hover, &:focus { + background-color: rgba(230, 230, 230, 0.9); + color: #999; + cursor: default; + } +} + +textarea:disabled { + background-color: rgba(230, 230, 230, 0.9); + color: #999; + cursor: default; +} + +input:disabled { + + label, &:hover + label, &:focus + label { + color: #999 !important; + cursor: default; + } +} + +/* Primary action button, use sparingly */ + +.primary { + border: 1px solid #0082c9; + background-color: #00a2e9; + color: #fff; +} + +input { + &[type='submit'].primary, &[type='button'].primary { + border: 1px solid #0082c9; + background-color: #00a2e9; + color: #fff; + } +} + +button.primary, .button.primary { + border: 1px solid #0082c9; + background-color: #00a2e9; + color: #fff; +} + +.primary:hover { + background-color: #0092d9; + color: #fff; +} + +input { + &[type='submit'].primary:hover, &[type='button'].primary:hover { + background-color: #0092d9; + color: #fff; + } +} + +button.primary:hover, .button.primary:hover, .primary:focus { + background-color: #0092d9; + color: #fff; +} + +input { + &[type='submit'].primary:focus, &[type='button'].primary:focus { + background-color: #0092d9; + color: #fff; + } +} + +button.primary:focus, .button.primary:focus { + background-color: #0092d9; + color: #fff; +} + +.primary:active { + background-color: #00a2e9; + color: #bbb; +} + +input { + &[type='submit'].primary:active, &[type='button'].primary:active { + background-color: #00a2e9; + color: #bbb; + } +} + +button.primary:active, .button.primary:active, .primary:disabled { + background-color: #00a2e9; + color: #bbb; +} + +input { + &[type='submit'].primary:disabled, &[type='button'].primary:disabled { + background-color: #00a2e9; + color: #bbb; + } +} + +button.primary:disabled, .button.primary:disabled, .primary:disabled:hover { + background-color: #00a2e9; + color: #bbb; +} + +input { + &[type='submit'].primary:disabled:hover, &[type='button'].primary:disabled:hover { + background-color: #00a2e9; + color: #bbb; + } +} + +button.primary:disabled:hover, .button.primary:disabled:hover, .primary:disabled:focus { + background-color: #00a2e9; + color: #bbb; +} + +input { + &[type='submit'].primary:disabled:focus, &[type='button'].primary:disabled:focus { + background-color: #00a2e9; + color: #bbb; + } +} + +button.primary:disabled:focus, .button.primary:disabled:focus { + background-color: #00a2e9; + color: #bbb; +} + +@keyframes shake { + 0% { + transform: translate(-5px, 0); + } + + 20% { + transform: translate(5px, 0); + } + + 40% { + transform: translate(-5px, 0); + } + + 60% { + transform: translate(5px, 0); + } + + 80% { + transform: translate(-5px, 0); + } + + 100% { + transform: translate(5px, 0); + } +} + + +.shake { + animation-name: shake; + animation-duration: .3s; + animation-timing-function: ease-out; +} diff --git a/core/css/installation.css b/core/css/installation.css new file mode 100644 index 00000000000..69c98f1ea22 --- /dev/null +++ b/core/css/installation.css @@ -0,0 +1,802 @@ +/* +* Installation css file. +* This file is used on the install page only when the database +* isn't set, preventing scss files to be stored using the AppdataController. +* It should contain every style needed to correctly display the installation template. +* +*/ + +/* Reset */ +html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, dialog, figure, footer, header, hgroup, nav, section { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-weight: inherit; + font-size: 100%; + font-family: inherit; + vertical-align: baseline; + cursor: default; +} + +html, body { + height: 100%; +} + +article, aside, dialog, figure, footer, header, hgroup, nav, section { + display: block; +} + +body { + line-height: 1.5; +} + +table { + border-collapse: separate; + border-spacing: 0; + white-space: nowrap; +} + +caption, th, td { + text-align: left; + font-weight: normal; +} + +table, td, th { + vertical-align: middle; +} + +a { + border: 0; + color: #000; + text-decoration: none; + cursor: pointer; +} +a * { + cursor: pointer; +} + +input { + cursor: pointer; +} +input * { + cursor: pointer; +} + +select, .button span, label { + cursor: pointer; +} + +ul { + list-style: none; +} + +body { + background-color: #ffffff; + font-weight: 400; + font-size: .8em; + line-height: 1.6em; + font-family: 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif; + color: #000; + height: auto; +} + +/* Global */ +#body-login { + text-align: center; + background-color: #0082c9; + background-image: url("../img/background.jpg?v=1"); + background-position: 50% 50%; + background-repeat: no-repeat; + background-size: cover; + background-attachment: fixed; + /* fix background gradient */ + height: 100%; + /* fix sticky footer */ +} + + +/* heading styles */ +h2 { + font-size: 20px; + font-weight: 300; + margin-bottom: 12px; + line-height: 140%; +} +h3 { + font-size: 15px; + font-weight: 300; + margin: 12px 0; +} + + +/* do not use italic typeface style, instead lighter color */ +em { + font-style: normal; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + opacity: .5; +} + +#header { + padding-top: 100px; +} + +p.info, form fieldset legend, #datadirContent label { + text-align: center; + color: #fff; +} + +form fieldset .warning-info, form input[type='checkbox'] + label { + text-align: center; + color: #fff; +} +form .warning input[type='checkbox']:hover + label, form .warning input[type='checkbox']:focus + label, form .warning input[type='checkbox'] + label { + color: #fff !important; +} + +.infogroup { + margin-bottom: 15px; +} + +p#message img { + vertical-align: middle; + padding: 5px; +} + +div.buttons { + text-align: center; +} + +p.info { + width: 22em; + margin: 0 auto; + padding-top: 20px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +p.info a { + font-weight: 600; + padding: 13px; + margin: -13px; + color: #fff; +} + +#body-login .warning, #body-login .update, #body-login .error { + display: block; + padding: 10px; + background-color: rgba(0, 0, 0, 0.3); + color: #fff; + text-align: left; + border-radius: 3px; + cursor: default; +} + +#body-login .warning { + margin: 0 7px 5px 4px; +} + +form { + position: relative; + width: 280px; + margin: 16px auto; + padding: 0; +} +form fieldset { + margin-bottom: 20px; + text-align: left; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +form #sqliteInformation { + margin-top: -20px; + margin-bottom: 20px; +} +form #adminaccount { + margin-bottom: 15px; +} +form fieldset legend { + width: 100%; +} +form fieldset.warning legend, form fieldset.update legend { + top: 18px; + position: relative; +} +form fieldset.warning legend + p, form fieldset.update legend + p { + margin-top: 12px; +} +form input[type='checkbox'] + label { + position: relative; + margin: 0; + padding: 14px; + vertical-align: middle; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +form .errors { + background: #fed7d7; + border: 1px solid #f00; + list-style-indent: inside; + margin: 0 0 2em; + padding: 1em; +} +form .success { + background: #d7fed7; + border: 1px solid #0f0; + width: 35%; + margin: 30px auto; + padding: 1em; + text-align: center; +} +form #showAdvanced > img { + padding: 4px; + box-sizing: border-box; +} +form p.info a, form #showAdvanced { + color: #fff; +} +form #remember_login:hover + label, form #remember_login:focus + label { + opacity: .6; +} +form #forgot-password:hover, form #forgot-password:focus { + opacity: .6; +} +form p.info a:hover, form p.info a:focus { + opacity: .6; +} +form footer .info { + white-space: nowrap; +} + +#datadirContent label { + display: block; + width: 100%; + margin: 0; +} + +form #datadirField legend { + margin-bottom: 15px; +} + +#showAdvanced { + padding: 13px; + /* increase clickable area of Advanced dropdown */ +} +#showAdvanced img { + vertical-align: bottom; + /* adjust position of Advanced dropdown arrow */ + margin-left: -4px; +} + +.icon-info-white { + padding: 10px; +} + +.float-spinner { + height: 32px; + display: none; +} + +.strengthify-wrapper { + display: inline-block; + position: relative; + left: 15px; + top: -23px; + width: 250px; +} + +.tipsy-inner { + font-weight: bold; + color: #ccc; +} + + +/* LOGO */ +#header .logo { + background-image: url(../img/logo-icon.svg?v=1); + background-repeat: no-repeat; + background-size: 175px; + background-position: center; + width: 252px; + height: 120px; + margin: 0 auto; +} + +/* Show password toggle */ +#show, #dbpassword { + position: absolute; + right: 1em; + top: .8em; + float: right; +} + +#show, #dbpassword, #personal-show { + display: none; +} + +#show + label, #dbpassword + label { + right: 21px; + top: 15px !important; + margin: -14px !important; + padding: 14px !important; +} + +#show:checked + label, #dbpassword:checked + label, #personal-show:checked + label { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=80)'; + opacity: .8; +} + +#show + label, #dbpassword + label, #personal-show + label { + position: absolute !important; + height: 20px; + width: 24px; + background-image: url("../img/actions/toggle.svg?v=1"); + background-repeat: no-repeat; + background-position: center; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=30)'; + opacity: .3; +} + +#show + label:before, #dbpassword + label:before, #personal-show + label:before { + display: none; +} + +#pass2, input[name='personal-password-clone'] { + padding: .6em 2.5em .4em .4em; + width: 8em; +} + +#personal-show + label { + height: 14px; + margin-top: -25px; + left: 295px; + display: block; +} + +#passwordbutton { + margin-left: .5em; +} + +/* LOADER */ +#body-login .float-spinner { + margin-top: -32px; + padding-top: 32px; +} + +[class^='icon-'], [class*=' icon-'] { + background-repeat: no-repeat; + background-position: center; + min-width: 16px; + min-height: 16px; +} + +.loading, .loading-small, .icon-loading, .icon-loading-dark, .icon-loading-small, .icon-loading-small-dark { + position: relative; +} + +.loading:after, .loading-small:after, .icon-loading:after, .icon-loading-dark:after, .icon-loading-small:after, .icon-loading-small-dark:after { + z-index: 2; + content: ''; + height: 32px; + width: 32px; + margin: -17px 0 0 -17px; + position: absolute; + top: 50%; + left: 50%; + border-radius: 100%; + -webkit-animation: rotate .8s infinite linear; + animation: rotate .8s infinite linear; + -webkit-transform-origin: center; + -ms-transform-origin: center; + transform-origin: center; +} + +.loading:after, .loading-small:after, .icon-loading:after, .icon-loading-dark:after, .icon-loading-small:after, .icon-loading-small-dark:after { + border: 2px solid rgba(150, 150, 150, 0.5); + border-top-color: #646464; +} + +.icon-loading-dark:after, .icon-loading-small-dark:after { + border: 2px solid rgba(187, 187, 187, 0.5); + border-top-color: #bbb; +} + +.icon-loading-small:after, .icon-loading-small-dark:after { + height: 16px; + width: 16px; + margin: -9px 0 0 -9px; +} + +.icon-info-white { + background-image: url(../img/actions/info-white.svg?v=1); +} + +@-webkit-keyframes rotate { + from { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes rotate { + from { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +/*! + * Bootstrap v3.3.5 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-style: normal; + font-weight: normal; + letter-spacing: normal; + line-break: auto; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + white-space: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + font-size: 12px; + opacity: 0; + filter: alpha(opacity=0); +} +.tooltip.in { + opacity: 0.9; + filter: alpha(opacity=90); +} +.tooltip.top { + margin-top: -3px; + padding: 5px 0; +} +.tooltip.right { + margin-left: 3px; + padding: 0 5px; +} +.tooltip.bottom { + margin-top: 3px; + padding: 5px 0; +} +.tooltip.left { + margin-left: -3px; + padding: 0 5px; +} + +.tooltip-inner { + max-width: 350px; + padding: 3px 8px; + color: #ffffff; + text-align: center; + background-color: #000000; + border-radius: 4px; +} + +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000000; +} +.tooltip.top-left .tooltip-arrow { + bottom: 0; + right: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000000; +} +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; +} + +/* SCROLLING */ +::-webkit-scrollbar { + width: 5px; +} + +::-webkit-scrollbar-track-piece { + background-color: transparent; +} + +::-webkit-scrollbar-thumb { + background: #ddd; + border-radius: 3px; +} +.error-wide { + width: 700px; + margin-left: -200px !important; +} + +/* Config write issue */ +#body-login .v-align { + width: inherit; +} +#body-login .wrapper { + min-height: 100%; + margin: 0 auto -70px; + width: 300px; +} +.warning legend, .warning a, .error a { + color: #fff !important; + font-weight: 600 !important; +} +#body-login ul.error-wide { + margin-top: 35px; +} + +/* Update info */ +#body-login .update { + width: inherit; + text-align: center; +} +#body-login .update h2 { + margin: 0 0 20px; +} + +#body-login .update a { + color: #fff; + border-bottom: 1px solid #aaa; +} + +/* INPUTS */ +input[type="text"], input[type="password"], input[type="search"], input[type="number"], input[type="email"], input[type="tel"], input[type="url"], input[type="time"], input[type="date"], textarea, select, button, .button, input[type="submit"], input[type="button"], #quota, .pager li a { + width: 130px; + margin: 3px 3px 3px 0; + padding: 7px 6px 5px; + font-size: 13px; + background-color: #fff; + color: #333; + border: 1px solid #ddd; + outline: none; + border-radius: 3px; +} +#body-login input { + font-size: 20px; + margin: 5px; + padding: 11px 10px 9px; +} +input[type="submit"], input[type="button"], button, .button { + width: auto; + min-width: 25px; + padding: 5px; + background-color: rgba(240, 240, 240, 0.9); + font-weight: 600; + color: #555; + border: 1px solid rgba(240, 240, 240, 0.9); + cursor: pointer; +} + +input { + font-size: 20px; + margin: 5px; + padding: 11px 10px 9px; +} +input[type='text'], input[type='password'], input[type='email'] { + font-family: 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif; + border: none; + font-weight: 300; + font-size: 20px; + margin: 5px; + padding: 11px 10px 9px; + -webkit-appearance: textfield; + -moz-appearance: textfield; + box-sizing: content-box; + background: #fff; + color: #555; + cursor: text; + font-family: inherit; + outline: none; + border-radius: 3px; + width: 249px; +} +input.login { + width: 269px; + background-position: right 16px center; +} +input[type='submit'] { + padding: 10px 20px; + /* larger log in and installation buttons */ +} + +/* Nicely grouping input field sets */ +.grouptop, .groupmiddle, .groupbottom { + position: relative; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.grouptop input { + margin-bottom: 0 !important; + border-bottom: 0 !important; + border-bottom-left-radius: 0 !important; + border-bottom-right-radius: 0 !important; +} + +.groupmiddle input { + margin-top: 0 !important; + margin-bottom: 0 !important; + border-top: 0 !important; + border-bottom: 0 !important; + border-radius: 0 !important; + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1) inset !important; +} + +.groupbottom input { + margin-top: 0 !important; + border-top: 0 !important; + border-top-right-radius: 0 !important; + border-top-left-radius: 0 !important; + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1) inset !important; +} + +.groupbottom input[type=submit] { + box-shadow: none !important; +} + +label.infield { + display: none; +} + +/* Primary action button, use sparingly */ +.primary { + border: 1px solid #0082c9; + background-color: #00a2e9; + color: #fff; +} + +input[type='submit'].primary, input[type='button'].primary { + border: 1px solid #0082c9; + background-color: #00a2e9; + color: #fff; +} + +button.primary, .button.primary { + border: 1px solid #0082c9; + background-color: #00a2e9; + color: #fff; +} + +.primary:hover { + background-color: #0092d9; + color: #fff; +} + +input[type='submit'].primary:hover, input[type='button'].primary:hover { + background-color: #0092d9; + color: #fff; +} + +button.primary:hover, .button.primary:hover, .primary:focus { + background-color: #0092d9; + color: #fff; +} + +input[type='submit'].primary:focus, input[type='button'].primary:focus { + background-color: #0092d9; + color: #fff; +} + +button.primary:focus, .button.primary:focus { + background-color: #0092d9; + color: #fff; +} + +.primary:active { + background-color: #00a2e9; + color: #bbb; +} + +input[type='submit'].primary:active, input[type='button'].primary:active { + background-color: #00a2e9; + color: #bbb; +} + +button.primary:active, .button.primary:active, .primary:disabled { + background-color: #00a2e9; + color: #bbb; +} + +input[type='submit'].primary:disabled, input[type='button'].primary:disabled { + background-color: #00a2e9; + color: #bbb; +} + +button.primary:disabled, .button.primary:disabled, .primary:disabled:hover { + background-color: #00a2e9; + color: #bbb; +} + +input[type='submit'].primary:disabled:hover, input[type='button'].primary:disabled:hover { + background-color: #00a2e9; + color: #bbb; +} + +button.primary:disabled:hover, .button.primary:disabled:hover, .primary:disabled:focus { + background-color: #00a2e9; + color: #bbb; +} + +input[type='submit'].primary:disabled:focus, input[type='button'].primary:disabled:focus { + background-color: #00a2e9; + color: #bbb; +} + +button.primary:disabled:focus, .button.primary:disabled:focus { + background-color: #00a2e9; + color: #bbb; +} +input, textarea, select, button { + font-family: 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif; +} diff --git a/core/css/jquery-ui-fixes.css b/core/css/jquery-ui-fixes.scss index e8cf2b769b8..4cf4f4cdd4a 100644 --- a/core/css/jquery-ui-fixes.css +++ b/core/css/jquery-ui-fixes.scss @@ -1,134 +1,124 @@ /* Component containers ----------------------------------*/ + .ui-widget { - font-family: "Lucida Grande", Arial, Verdana, sans-serif; + font-family: 'Lucida Grande', Arial, Verdana, sans-serif; font-size: 1em; + button { + font-family: 'Lucida Grande', Arial, Verdana, sans-serif; + } } -.ui-widget button { - font-family: "Lucida Grande", Arial, Verdana, sans-serif; -} + .ui-widget-content { border: 1px solid #dddddd; background: #eeeeee url('images/ui-bg_highlight-soft_100_eeeeee_1x100.png') 50% top repeat-x; color: #333333; + a { + color: #333333; + } } -.ui-widget-content a { - color: #333333; -} + .ui-widget-header { border: 1px solid #0082c9; background: #0082c9; color: #ffffff; -} -.ui-widget-header a { - color: #ffffff; + a { + color: #ffffff; + } } /* Interaction states ----------------------------------*/ -.ui-state-default, -.ui-widget-content .ui-state-default, -.ui-widget-header .ui-state-default { + +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #ddd; background: #f8f8f8 url('images/ui-bg_glass_100_f8f8f8_1x400.png') 50% 50% repeat-x; font-weight: bold; color: #555; } -.ui-state-default a, -.ui-state-default a:link, -.ui-state-default a:visited { + +.ui-state-default a { color: #555; + &:link, &:visited { + color: #555; + } } -.ui-state-hover, -.ui-widget-content .ui-state-hover, -.ui-widget-header .ui-state-hover, -.ui-state-focus, -.ui-widget-content .ui-state-focus, -.ui-widget-header .ui-state-focus { + +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #ddd; background: #ffffff url('images/ui-bg_flat_100_ffffff_40x100.png') 50% 50% repeat-x; font-weight: bold; color: #333; } -.ui-state-hover a, -.ui-state-hover a:hover, -.ui-state-hover a:link, -.ui-state-hover a:visited { + +.ui-state-hover a { color: #333; + &:hover, &:link, &:visited { + color: #333; + } } -.ui-state-active, -.ui-widget-content .ui-state-active, -.ui-widget-header .ui-state-active { + +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #0082c9; background: #f8f8f8 url('images/ui-bg_glass_100_f8f8f8_1x400.png') 50% 50% repeat-x; font-weight: bold; color: #0082c9; } -.ui-state-active a, -.ui-state-active a:link, -.ui-state-active a:visited { + +.ui-state-active a { color: #0082c9; + &:link, &:visited { + color: #0082c9; + } } /* Interaction Cues ----------------------------------*/ -.ui-state-highlight, -.ui-widget-content .ui-state-highlight, -.ui-widget-header .ui-state-highlight { + +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight { border: 1px solid #ddd; background: #f8f8f8 url('images/ui-bg_highlight-hard_100_f8f8f8_1x100.png') 50% top repeat-x; color: #555; } -.ui-state-highlight a, -.ui-widget-content .ui-state-highlight a, -.ui-widget-header .ui-state-highlight a { + +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a, .ui-widget-header .ui-state-highlight a { color: #555; } -.ui-state-error, -.ui-widget-content .ui-state-error, -.ui-widget-header .ui-state-error { + +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error { border: 1px solid #cd0a0a; background: #b81900 url('images/ui-bg_diagonals-thick_18_b81900_40x40.png') 50% 50% repeat; color: #ffffff; } -.ui-state-error a, -.ui-widget-content .ui-state-error a, -.ui-widget-header .ui-state-error a { - color: #ffffff; -} -.ui-state-error-text, -.ui-widget-content .ui-state-error-text, -.ui-widget-header .ui-state-error-text { + +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a, .ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; } /* Icons ----------------------------------*/ -.ui-state-default .ui-icon { - background-image: url('images/ui-icons_1d2d44_256x240.png'); -} -.ui-state-hover .ui-icon, -.ui-state-focus .ui-icon { - background-image: url('images/ui-icons_1d2d44_256x240.png'); -} -.ui-state-active .ui-icon { + +.ui-state-default .ui-icon, .ui-state-hover .ui-icon, .ui-state-focus .ui-icon, .ui-state-active .ui-icon { background-image: url('images/ui-icons_1d2d44_256x240.png'); } + .ui-state-highlight .ui-icon { background-image: url('images/ui-icons_ffffff_256x240.png'); } -.ui-state-error .ui-icon, -.ui-state-error-text .ui-icon { + +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon { background-image: url('images/ui-icons_ffd27a_256x240.png'); } /* Misc visuals ----------------------------------*/ /* Overlays */ + .ui-widget-overlay { background: #666666 url('images/ui-bg_diagonals-thick_20_666666_40x40.png') 50% 50% repeat; opacity: .5; } + .ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; diff --git a/core/css/multiselect.css b/core/css/multiselect.css deleted file mode 100644 index 8bcbd0e563d..00000000000 --- a/core/css/multiselect.css +++ /dev/null @@ -1,113 +0,0 @@ -/* Copyright (c) 2011, Jan-Christoph Borchardt, http: //jancborchardt.net -This file is licensed under the Affero General Public License version 3 or later. -See the COPYING-README file. */ - -ul.multiselectoptions { - background-color: #fff; - border: 1px solid #ddd; - border-top: none; - box-shadow: 0 1px 1px #ddd; - padding-top: 8px; - position: absolute; - max-height: 20em; - overflow-y: auto; - z-index: 49; -} - -ul.multiselectoptions.down { - border-bottom-left-radius: 8px; - border-bottom-right-radius: 8px; - width: 100%; /* do not cut off group names */ - -webkit-box-shadow: 0px 0px 20px rgba(29,45,68,.4); - -moz-box-shadow: 0px 0px 20px rgba(29,45,68,.4); - box-shadow: 0px 0px 20px rgba(29,45,68,.4); -} - -ul.multiselectoptions.up { - border-top-left-radius: 8px; - border-top-right-radius: 8px; -} - -ul.multiselectoptions>li { - overflow: hidden; - white-space: nowrap; - margin-left: 7px; -} -ul.multiselectoptions > li input[type='checkbox']+label { - font-weight: normal; - display: inline-block; - width: 100%; - padding: 5px 27px; - margin-left: -27px; /* to have area around checkbox clickable as well */ - text-overflow: ellipsis; - overflow: hidden; -} -ul.multiselectoptions > li input[type='checkbox']:checked+label { - font-weight: bold; -} - -div.multiselect, select.multiselect { - display: inline-block; - max-width: 200px; - min-width: 150px !important; - padding-right: 10px; - min-height: 20px; - position: relative; - vertical-align: bottom; -} - -/* To make a select look like a multiselect until it's initialized */ -select.multiselect { - height: 30px; - min-width: 113px; -} - -div.multiselect.active { - background-color: #fff; - position: relative; - z-index: 50; -} - -div.multiselect.up { - border-top: 0 none; - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -div.multiselect.down { - border-bottom: none; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} - -div.multiselect>span:first-child { - float: left; - margin-right: 32px; - overflow: hidden; - text-overflow: ellipsis; - width: 90%; - white-space: nowrap; -} - -div.multiselect>span:last-child { - position: absolute; - right: 8px; - top: 8px; -} - -ul.multiselectoptions input.new { - padding-bottom: 3px; - padding-top: 3px; - margin: 0; -} - -ul.multiselectoptions > li.creator { - padding: 10px; - margin: 0; - font-weight: bold; -} -ul.multiselectoptions > li.creator > input { - width: 95% !important; /* do not constrain size of text input */ - padding: 5px; - margin: -5px; -} diff --git a/core/css/multiselect.scss b/core/css/multiselect.scss new file mode 100644 index 00000000000..ea7a481f360 --- /dev/null +++ b/core/css/multiselect.scss @@ -0,0 +1,124 @@ +/* Copyright (c) 2011, Jan-Christoph Borchardt, http: //jancborchardt.net +This file is licensed under the Affero General Public License version 3 or later. +See the COPYING-README file. */ + +ul.multiselectoptions { + background-color: #fff; + border: 1px solid #ddd; + border-top: none; + box-shadow: 0 1px 1px #ddd; + padding-top: 8px; + position: absolute; + max-height: 20em; + overflow-y: auto; + z-index: 49; + &.down { + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + width: 100%; + /* do not cut off group names */ + -webkit-box-shadow: 0px 0px 20px rgba(29, 45, 68, 0.4); + -moz-box-shadow: 0px 0px 20px rgba(29, 45, 68, 0.4); + box-shadow: 0px 0px 20px rgba(29, 45, 68, 0.4); + } + &.up { + border-top-left-radius: 8px; + border-top-right-radius: 8px; + } + > li { + overflow: hidden; + white-space: nowrap; + margin-left: 7px; + input[type='checkbox'] { + + label { + font-weight: normal; + display: inline-block; + width: 100%; + padding: 5px 27px; + margin-left: -27px; + /* to have area around checkbox clickable as well */ + text-overflow: ellipsis; + overflow: hidden; + } + &:checked + label { + font-weight: bold; + } + } + } +} + +div.multiselect { + display: inline-block; + max-width: 200px; + min-width: 150px !important; + padding-right: 10px; + min-height: 20px; + position: relative; + vertical-align: bottom; +} + +select.multiselect { + display: inline-block; + max-width: 200px; + min-width: 150px !important; + padding-right: 10px; + min-height: 20px; + position: relative; + vertical-align: bottom; + height: 30px; + min-width: 113px; +} + +/* To make a select look like a multiselect until it's initialized */ + +div.multiselect { + &.active { + background-color: #fff; + position: relative; + z-index: 50; + } + &.up { + border-top: 0 none; + border-top-left-radius: 0; + border-top-right-radius: 0; + } + &.down { + border-bottom: none; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } + > span { + &:first-child { + float: left; + margin-right: 32px; + overflow: hidden; + text-overflow: ellipsis; + width: 90%; + white-space: nowrap; + } + &:last-child { + position: absolute; + right: 8px; + top: 8px; + } + } +} + +ul.multiselectoptions { + input.new { + padding-bottom: 3px; + padding-top: 3px; + margin: 0; + } + > li.creator { + padding: 10px; + margin: 0; + font-weight: bold; + > input { + width: 95% !important; + /* do not constrain size of text input */ + padding: 5px; + margin: -5px; + } + } +} diff --git a/core/css/share.css b/core/css/share.css deleted file mode 100644 index eba22cf743e..00000000000 --- a/core/css/share.css +++ /dev/null @@ -1,199 +0,0 @@ -/* Copyright (c) 2011, Jan-Christoph Borchardt, http://jancborchardt.net - This file is licensed under the Affero General Public License version 3 or later. - See the COPYING-README file. */ - -#dropdown { - background: #eee; - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; - box-shadow: 0 2px 3px rgba(50, 50, 50, .4); - display: block; - margin-right: 0; - position: absolute; - right: 0; - width: 420px; - z-index: 500; - padding: 16px; -} - -@media only screen and (min-width: 768px) and (max-width: 990px) { - #dropdown { - /* this limits the dropdown to float below the sidebar for mid narrow screens */ - left: 20px; - } -} - -.shareTabView .unshare.icon-loading-small { - margin-top: 1px; -} - -.shareTabView .shareWithLoading, -.shareTabView .linkShare .icon-loading-small { - display: inline-block !important; - padding-left: 10px; -} -.shareTabView .shareWithLoading { - position: relative; - right: 70px; - top: 2px; -} -.shareTabView .icon-loading-small.hidden { - display: none !important; -} - -.shareTabView .avatar { - margin-right: 8px; - display: inline-block; - overflow: hidden; - vertical-align: middle; - width: 32px; - height: 32px; -} - -.share-autocomplete-item { - display: flex; -} -.share-autocomplete-item .autocomplete-item-text { - margin-left: 10px; - margin-right: 10px; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - line-height: 32px; - vertical-align: middle; -} - -#shareWithList { - list-style-type:none; - padding:8px; -} - -#shareWithList li { - padding-top: 10px; - padding-bottom: 10px; - font-weight: bold; - line-height: 21px; - white-space: normal; - width: 100%; -} - -#shareWithList .sharingOptionsGroup { - flex-shrink: 0; - position: relative; -} - -#shareWithList .sharingOptionsGroup .popovermenu { - right: -6px; - top: 40px; - padding: 3px 6px; -} - -#shareWithList .shareOption { - white-space: nowrap; - display: inline-block; -} - -#shareWithList .unshare img, #shareWithList .showCruds img { - vertical-align:text-bottom; /* properly align icons */ -} - -#shareWithList label input[type=checkbox]{ - margin-left: 0; - position: relative; -} -#shareWithList .username{ - padding-right: 8px; - white-space: nowrap; - text-overflow: ellipsis; - display: inline-block; - overflow: hidden; - vertical-align: middle; - flex-grow: 5; -} -#shareWithList li label{ - margin-right: 8px; -} -.shareTabView label { - font-weight:400; - white-space: nowrap; -} - -.shareTabView input[type="checkbox"] { - margin:0 3px 0 8px; - vertical-align: middle; -} - -a.showCruds { - display:inline; - opacity:.5; -} - -a.unshare { - display:inline-block; - opacity:.5; - padding: 10px; -} - -#link { - border-top:1px solid #ddd; - padding-top:8px; -} - -.shareTabView input[type="submit"] { - margin-left: 7px; -} - -.shareTabView form { - font-size: 100%; - margin-left: 0; - margin-right: 0; -} - -.shareTabView .error { - color: #e9322d; - border-color: #e9322d; - box-shadow: 0 0 6px #f8b9b7; -} - -#link #showPassword img { - padding-left:5px; - width:12px; -} - -.reshare,#link label, -#expiration label { - display: inline-block; - padding: 6px 4px; -} - -a.showCruds:hover,a.unshare:hover { - opacity:1; -} - -#defaultExpireMessage, /* fix expire message going out of box */ -.reshare { /* fix shared by text going out of box */ - white-space:normal; -} - -#defaultExpireMessage { /* show message on new line */ - display: block; - padding-left: 4px; - /* TODO: style the dropdown in a proper way - border-box, etc. */ - width: 90%; -} - -.ui-autocomplete { /* limit dropdown height to 4 1/2 entries */ - max-height: 200px; - overflow-y: auto; - overflow-x: hidden; -} - -.notCreatable { - padding-left: 12px; - padding-top: 12px; - color: #999; -} - -.shareTabView .mailView .icon-mail { - opacity: 0.5; -} diff --git a/core/css/share.scss b/core/css/share.scss new file mode 100644 index 00000000000..a72437c4aeb --- /dev/null +++ b/core/css/share.scss @@ -0,0 +1,199 @@ +/* Copyright (c) 2011, Jan-Christoph Borchardt, http://jancborchardt.net + This file is licensed under the Affero General Public License version 3 or later. + See the COPYING-README file. */ + +#dropdown { + background: #eee; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; + box-shadow: 0 2px 3px rgba(50, 50, 50, 0.4); + display: block; + margin-right: 0; + position: absolute; + right: 0; + width: 420px; + z-index: 500; + padding: 16px; +} + +@media only screen and (min-width: 768px) and (max-width: 990px) { + #dropdown { + /* this limits the dropdown to float below the sidebar for mid narrow screens */ + left: 20px; + } +} + +.shareTabView { + .unshare.icon-loading-small { + margin-top: 1px; + } + .shareWithLoading, .linkShare .icon-loading-small { + display: inline-block !important; + padding-left: 10px; + } + .shareWithLoading { + position: relative; + right: 70px; + top: 2px; + } + .icon-loading-small.hidden { + display: none !important; + } + .avatar { + margin-right: 8px; + display: inline-block; + overflow: hidden; + vertical-align: middle; + width: 32px; + height: 32px; + } +} + +.share-autocomplete-item { + display: flex; + .autocomplete-item-text { + margin-left: 10px; + margin-right: 10px; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + line-height: 32px; + vertical-align: middle; + } +} + +#shareWithList { + list-style-type: none; + padding: 8px; + li { + padding-top: 10px; + padding-bottom: 10px; + font-weight: bold; + line-height: 21px; + white-space: normal; + width: 100%; + } + .sharingOptionsGroup { + flex-shrink: 0; + position: relative; + .popovermenu { + right: -6px; + top: 40px; + padding: 3px 6px; + } + } + .shareOption { + white-space: nowrap; + display: inline-block; + } + .unshare img, .showCruds img { + vertical-align: text-bottom; + /* properly align icons */ + } + label input[type=checkbox] { + margin-left: 0; + position: relative; + } + .username { + padding-right: 8px; + white-space: nowrap; + text-overflow: ellipsis; + display: inline-block; + overflow: hidden; + vertical-align: middle; + flex-grow: 5; + } + li label { + margin-right: 8px; + } +} + +.shareTabView { + label { + font-weight: 400; + white-space: nowrap; + } + input[type='checkbox'] { + margin: 0 3px 0 8px; + vertical-align: middle; + } +} + +a { + &.showCruds { + display: inline; + opacity: .5; + } + &.unshare { + display: inline-block; + opacity: .5; + padding: 10px; + } +} + +#link { + border-top: 1px solid #ddd; + padding-top: 8px; +} + +.shareTabView { + input[type='submit'] { + margin-left: 7px; + } + form { + font-size: 100%; + margin-left: 0; + margin-right: 0; + } + .error { + color: #e9322d; + border-color: #e9322d; + box-shadow: 0 0 6px #f8b9b7; + } +} + +#link #showPassword img { + padding-left: 5px; + width: 12px; +} + +.reshare, #link label, #expiration label { + display: inline-block; + padding: 6px 4px; +} + +a { + &.showCruds:hover, &.unshare:hover { + opacity: 1; + } +} + +#defaultExpireMessage, .reshare { + /* fix shared by text going out of box */ + white-space: normal; +} + +#defaultExpireMessage { + /* show message on new line */ + display: block; + padding-left: 4px; + /* TODO: style the dropdown in a proper way - border-box, etc. */ + width: 90%; +} + +.ui-autocomplete { + /* limit dropdown height to 4 1/2 entries */ + max-height: 200px; + overflow-y: auto; + overflow-x: hidden; +} + +.notCreatable { + padding-left: 12px; + padding-top: 12px; + color: #999; +} + +.shareTabView .mailView .icon-mail { + opacity: 0.5; +} diff --git a/core/css/styles.css b/core/css/styles.css deleted file mode 100644 index 5ea4ca53707..00000000000 --- a/core/css/styles.css +++ /dev/null @@ -1,999 +0,0 @@ -/* Copyright (c) 2011, Jan-Christoph Borchardt, http://jancborchardt.net - This file is licensed under the Affero General Public License version 3 or later. - See the COPYING-README file. */ - -html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, dialog, figure, footer, header, hgroup, nav, section { margin:0; padding:0; border:0; outline:0; font-weight:inherit; font-size:100%; font-family:inherit; vertical-align:baseline; cursor:default; } -html, body { height:100%; } -article, aside, dialog, figure, footer, header, hgroup, nav, section { display:block; } -body { line-height:1.5; } -table { border-collapse:separate; border-spacing:0; white-space:nowrap; } -caption, th, td { text-align:left; font-weight:normal; } -table, td, th { vertical-align:middle; } -a { border:0; color:#000; text-decoration:none;} -a, a *, input, input *, select, .button span, label { cursor:pointer; } -ul { list-style:none; } - -body { - background-color: #ffffff; - font-weight: 400; - font-size: .8em; - line-height: 1.6em; - font-family: 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif; - color: #000; - height: auto; -} - -#body-login { - text-align: center; - background-color: #0082c9; - background-image: url('../img/background.jpg?v=1'); - background-position: 50% 50%; - background-repeat: no-repeat; - background-size: cover; -} - -.two-factor-header { - text-align: center; -} - -.two-factor-provider { - text-align: center; - width: 258px !important; - display: inline-block; - margin-bottom: 0 !important; - background-color: rgba(0,0,0,0.3) !important; - border: none !important; -} - -.two-factor-link { - display: inline-block; - padding: 12px; - color: rgba(255, 255, 255, .75); -} - -.float-spinner { - height: 32px; - display: none; -} -#body-login .float-spinner { - margin-top: -32px; - padding-top: 32px; -} - -#nojavascript { - position: fixed; - top: 0; - bottom: 0; - height: 100%; - width: 100%; - z-index: 9000; - text-align: center; - background-color: rgba(0,0,0,0.5); - color: #fff; - line-height: 125%; - font-size: 24px; -} -#nojavascript div { - display: block; - position: relative; - width: 50%; - top: 35%; - margin: 0px auto; -} -#nojavascript a { - color: #fff; - border-bottom: 2px dotted #fff; -} -#nojavascript a:hover, -#nojavascript a:focus { - color: #ddd; -} - -/* SCROLLING */ -::-webkit-scrollbar { - width: 5px; -} -::-webkit-scrollbar-track-piece { - background-color: transparent; -} -::-webkit-scrollbar-thumb { - background: #ddd; - border-radius: 3px; -} - -/* Searchbox */ -.searchbox input[type="search"] { - position: relative; - font-size: 1.2em; - padding: 3px; - padding-left: 25px; - background: transparent url('../img/actions/search-white.svg?v=1') no-repeat 6px center; - color: #fff; - border: 0; - border-radius: 3px; - margin-top: 9px; - float: right; - width: 0; - cursor: pointer; - -webkit-transition: all 100ms; - transition: all 100ms; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; - opacity: .7; -} -.searchbox input[type="search"]:focus, -.searchbox input[type="search"]:active, -.searchbox input[type="search"]:valid { - color: #fff; - width: 155px; - max-width: 50%; - cursor: text; - background-color: #0082c9; - border: 1px solid rgba(255, 255, 255, .5); -} - -/* CONTENT ------------------------------------------------------------------ */ -#controls { - box-sizing: border-box; - position: fixed; - top: 45px; - right: 0; - left: 0; - height: 44px; - width: 100%; - padding: 0; - margin: 0; - background-color: rgba(255, 255, 255, .95); - z-index: 50; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -/* position controls for apps with app-navigation */ -#app-navigation+#app-content #controls { - left: 250px; -} -.viewer-mode #app-navigation+#app-content #controls { - left: 0; -} - -#controls .button, -#controls button, -#controls input[type='submit'], -#controls input[type='text'], -#controls input[type='password'], -#controls select { - box-sizing: border-box; - display: inline-block; - height: 36px; - padding: 7px 10px -} - -#controls .button.hidden { - display: none; -} - -#content { - position: relative; - height: 100%; - width: 100%; -} -#content .hascontrols { - margin-top: 45px; -} -#content-wrapper { - position: absolute; - height: 100%; - width: 100%; - overflow-x: hidden; /* prevent horizontal scrollbar */ - padding-top: 45px; - box-sizing:border-box; -} -/* allow horizontal scrollbar for personal and admin settings */ -#body-settings:not(.snapjs-left) .app-settings { - overflow-x: auto; -} - -#emptycontent, -.emptycontent { - color: #888; - text-align: center; - margin-top: 30vh; - width: 100%; -} -#emptycontent.emptycontent-search, -.emptycontent.emptycontent-search { - position: static; -} -#emptycontent h2, -.emptycontent h2 { - margin-bottom: 10px; - line-height: 150%; -} -#emptycontent [class^="icon-"], -.emptycontent [class^="icon-"], -#emptycontent [class*=" icon-"], -.emptycontent [class*=" icon-"] { - background-size: 64px; - height: 64px; - width: 64px; - margin: 0 auto 15px; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)"; - opacity: .4; -} - - -/* LOG IN & INSTALLATION ------------------------------------------------------------ */ - -/* Some whitespace to the top */ -#body-login #header { - padding-top: 100px; -} -#body-login { - background-attachment: fixed; /* fix background gradient */ - height: 100%; /* fix sticky footer */ -} - -/* Dark subtle label text */ -#body-login p.info, -#body-login form fieldset legend, -#body-login #datadirContent label, -#body-login form fieldset .warning-info, -#body-login form input[type="checkbox"]+label { - text-align: center; - color: #fff; -} -/* overrides another !important statement that sets this to unreadable black */ -#body-login form .warning input[type="checkbox"]:hover+label, -#body-login form .warning input[type="checkbox"]:focus+label, -#body-login form .warning input[type="checkbox"]+label { - color: #fff !important; -} - -#body-login .update h2 { - margin: 0 0 20px; -} - -#body-login .update a { - color: #fff; - border-bottom: 1px solid #aaa; -} - -#body-login .infogroup { - margin-bottom: 15px; -} - -#body-login p#message img { - vertical-align: middle; - padding: 5px; -} - -#body-login div.buttons { - text-align: center; -} -#body-login p.info { - margin: 0 auto; - padding-top: 20px; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -#body-login p.info a { - font-weight: 600; - padding: 13px; - margin: -13px; -} - -#body-login form { - position: relative; - width: 280px; - margin: 16px auto; - padding: 0; -} -#body-login form fieldset { - margin-bottom: 20px; - text-align: left; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -#body-login form #sqliteInformation { - margin-top: -20px; - margin-bottom: 20px; -} -#body-login form #adminaccount { - margin-bottom: 15px; -} -#body-login form fieldset legend, #datadirContent label { - width: 100%; -} -#body-login #datadirContent label { - display: block; - margin: 0; -} -#body-login form #datadirField legend { - margin-bottom: 15px; -} -#body-login #showAdvanced { - padding: 13px; /* increase clickable area of Advanced dropdown */ -} -#body-login #showAdvanced img { - vertical-align: bottom; /* adjust position of Advanced dropdown arrow */ - margin-left: -4px; -} -#body-login .icon-info-white { - padding: 10px; -} - -/* strengthify wrapper */ -#body-login .strengthify-wrapper { - display: inline-block; - position: relative; - left: 15px; - top: -23px; - width: 250px; -} - -/* tipsy for the strengthify wrapper looks better with following font settings */ -#body-login .tipsy-inner { - font-weight: bold; - color: #ccc; -} - -/* General new input field look */ -#body-login input[type="text"], -#body-login input[type="password"], -#body-login input[type="email"] { - border: none; - font-weight: 300; -} - -/* Nicely grouping input field sets */ -.grouptop, -.groupmiddle, -.groupbottom { - position: relative; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -#body-login .grouptop input, -.grouptop input { - margin-bottom: 0 !important; - border-bottom: 0 !important; - border-bottom-left-radius: 0 !important; - border-bottom-right-radius: 0 !important; -} -#body-login .groupmiddle input, -.groupmiddle input { - margin-top: 0 !important; - margin-bottom: 0 !important; - border-top: 0 !important; - border-bottom: 0 !important; - border-radius: 0 !important; - box-shadow: 0 1px 0 rgba(0,0,0,.1) inset !important; -} -#body-login .groupbottom input, -.groupbottom input { - margin-top: 0 !important; - border-top: 0 !important; - border-top-right-radius: 0 !important; - border-top-left-radius: 0 !important; - box-shadow: 0 1px 0 rgba(0,0,0,.1) inset !important; -} -#body-login .groupbottom input[type=submit] { - box-shadow: none !important; -} - -/* keep the labels for screen readers but hide them since we use placeholders */ -label.infield { - display: none; -} - -#body-login form input[type="checkbox"]+label { - position: relative; - margin: 0; - padding: 14px; - vertical-align: middle; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -#body-login form .errors { background:#fed7d7; border:1px solid #f00; list-style-indent:inside; margin:0 0 2em; padding:1em; } -#body-login .success { background:#d7fed7; border:1px solid #0f0; width: 35%; margin: 30px auto; padding:1em; text-align: center;} - -#body-login #showAdvanced > img { - padding: 4px; - box-sizing: border-box; -} - -#body-login p.info a, #body-login #showAdvanced { - color: #fff; -} - -#body-login #remember_login:hover+label, -#body-login #remember_login:focus+label, -#body-login #forgot-password:hover, -#body-login #forgot-password:focus, -#body-login p.info a:hover, -#body-login p.info a:focus { - opacity: .6; -} - -/* Show password toggle */ -#show, #dbpassword { - position: absolute; - right: 1em; - top: .8em; - float: right; -} -#show, #dbpassword, #personal-show { - display: none; -} -#show + label, #dbpassword + label { - right: 21px; - top: 15px !important; - margin: -14px !important; - padding: 14px !important; -} -#show:checked + label, #dbpassword:checked + label, #personal-show:checked + label { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; - opacity: .8; -} -#show + label, #dbpassword + label, #personal-show + label { - position: absolute !important; - height: 20px; - width: 24px; - background-image: url('../img/actions/toggle.svg?v=1'); - background-repeat: no-repeat; - background-position: center; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; - opacity: .3; -} -#show + label:before, #dbpassword + label:before, #personal-show + label:before { - display: none; -} -#pass2, input[name="personal-password-clone"] { - padding: .6em 2.5em .4em .4em; - width: 8em; -} -#personal-show + label { - height: 14px; - margin-top: -25px; - left: 295px; - display: block; -} -#passwordbutton { - margin-left: .5em; -} - -/* Database selector */ -#body-login form #selectDbType { - text-align:center; - white-space: nowrap; - margin: 0; -} -#body-login form #selectDbType .info { - white-space: normal; -} -#body-login form #selectDbType label { - position: static; - margin: 0 -3px 5px; - font-size: 12px; - background:#f8f8f8; - color:#888; - cursor:pointer; - border: 1px solid #ddd; -} -#body-login form #selectDbType label span { - cursor: pointer; - padding: 10px 20px; -} -#body-login form #selectDbType label.ui-state-hover, -#body-login form #selectDbType label.ui-state-active { - color:#000; - background-color:#e8e8e8; } - - -/* Warnings and errors are the same */ -#body-login .warning, -#body-login .update, -#body-login .error { - display: block; - padding: 10px; - background-color: rgba(0,0,0,.3); - color: #fff; - text-align: left; - border-radius: 3px; - cursor: default; -} - -#body-login .update { - width: inherit; - text-align: center; -} - -#body-login .update .appList { - list-style: disc; - text-align: left; - margin-left: 25px; - margin-right: 25px; -} - -#body-login .v-align { - width: inherit; -} - -#body-login .update img.float-spinner { - float: left; -} - -#body-user .warning, #body-settings .warning { - margin-top: 8px; - padding: 5px; - background: #fdd; - border-radius: 3px; -} - -.warning legend, -.warning a, -.error a { - color: #fff !important; - font-weight: 600 !important; -} -.error a.button { - color: #555 !important; - display: inline-block; - text-align: center; -} -.error pre { - white-space: pre-wrap; - text-align: left; -} - -.error-wide { - width: 700px; - margin-left: -200px !important; -} - -.error-wide .button { - color: black !important; -} - -.warning-input { - border-color: #ce3702 !important; -} - -/* Fixes for log in page, TODO should be removed some time */ -#body-login ul.error-wide { - margin-top: 35px; -} -#body-login .warning { - margin: 0 7px 5px 4px; -} -#body-login .warning legend { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; - opacity: 1; -} -#body-login a.warning { - cursor: pointer; -} - -/* fixes for update page TODO should be fixed some time in a proper way */ -/* this is just for an error while updating the ownCloud instance */ -#body-login .updateProgress .error { - margin-top: 10px; - margin-bottom: 10px; -} - -/* Alternative Logins */ -#alternative-logins legend { margin-bottom:10px; } -#alternative-logins li { height:40px; display:inline-block; white-space:nowrap; } - -/* Log in and install button */ -#body-login input { - font-size: 20px; - margin: 5px; - padding: 11px 10px 9px; -} -#body-login input[type="text"], -#body-login input[type="password"] { - width: 249px; -} -#body-login input.login { - width: 269px; - background-position: right 16px center; -} -#body-login input[type="submit"] { - padding: 10px 20px; /* larger log in and installation buttons */ -} -#remember_login { - margin: 18px 5px 0 16px !important; -} -#body-login .remember-login-container { - display: inline-block; - margin: 10px 0; - text-align: center; - width: 100%; -} -#body-login #forgot-password { - padding: 11px; - float: right; - color: #fff; -} - -/* Sticky footer */ -#body-login .wrapper { - min-height: 100%; - margin: 0 auto -70px; - width: 300px; -} -#body-login footer, #body-login .push { - height: 70px; -} - -/* round profile photos */ -.avatar, -.avatar img, -.avatardiv, -.avatardiv img { - border-radius: 50%; - flex-shrink: 0; -} -td.avatar { - border-radius: 0; -} - - -#notification-container { - position: absolute; - top: 0; - width: 100%; - text-align: center; -} -#notification { - margin: 0 auto; - max-width: 60%; - z-index: 8000; - background-color: #fff; - border: 0; - padding: 1px 8px; - display: none; - position: relative; - top: 0; - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=90)"; - opacity: .9; -} -#notification span { - cursor: pointer; - margin-left: 1em; -} -#notification { - overflow-x: hidden; - overflow-y: auto; - max-height: 100px; -} -#notification .row { - position: relative; -} -#notification .row .close { - display: inline-block; - vertical-align: middle; - position: absolute; - right: 0; - top: 0; - margin-top: 2px; -} -#notification .row.closeable { - padding-right: 20px; -} - -tr .action:not(.permanent), -.selectedActions a { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; - opacity: 0; -} -tr:hover .action, -tr:focus .action, -tr .action.permanent, -.selectedActions a { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; - opacity: .5; -} -tr .action { - width: 16px; - height: 16px; -} -.header-action { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; - opacity: .8; -} -tr:hover .action:hover, -tr:focus .action:focus, -.selectedActions a:hover, -.selectedActions a:focus, -.header-action:hover, -.header-action:focus { - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; - opacity: 1; -} -tbody tr:hover, -tbody tr:focus, -tbody tr:active { - background-color: #f8f8f8; -} - -code { font-family:"Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", monospace; } - -#quota { - cursor: default; - margin: 30px !important; - position: relative; - padding: 0 !important; -} -#quota div { - padding: 0; - background-color: rgb(220,220,220); - font-weight: normal; - white-space: nowrap; - border-bottom-left-radius: 3px; - border-top-left-radius: 3px; - min-width: 1%; - max-width: 100%; -} -#quotatext {padding:.6em 1em;} - -#quota div.quota-warning { - background-color: #fc4; -} - -.pager { list-style:none; float:right; display:inline; margin:.7em 13em 0 0; } -.pager li { display:inline-block; } - -.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { overflow:hidden; text-overflow:ellipsis; } -.separator { display:inline; border-left:1px solid #d3d3d3; border-right:1px solid #fff; height:10px; width:0px; margin:4px; } - -a.bookmarklet { background-color:#ddd; border:1px solid #ccc; padding:5px;padding-top:0px;padding-bottom:2px; text-decoration:none; margin-top:5px } - -.exception{color:#000;} -.exception textarea{width:95%;height:200px;background:#ffe;border:0;} - -.ui-icon-circle-triangle-e{ background-image:url('../img/actions/play-next.svg?v=1'); } -.ui-icon-circle-triangle-w{ background-image:url('../img/actions/play-previous.svg?v=1'); } - -.ui-datepicker-prev,.ui-datepicker-next{ border:1px solid #ddd; background:#fff; } - -/* ---- DIALOGS ---- */ -#oc-dialog-filepicker-content .dirtree { - width:92%; - float: left; - margin-left: 15px; - overflow:hidden; -} -#oc-dialog-filepicker-content .dirtree div:first-child a { - background-image:url('../img/places/home.svg?v=1'); - background-repeat:no-repeat; - background-position: left center; -} -#oc-dialog-filepicker-content .dirtree span:not(:last-child) { cursor: pointer; } -#oc-dialog-filepicker-content .dirtree span:last-child { font-weight: bold; } -#oc-dialog-filepicker-content .dirtree span:not(:last-child)::after { content: '>'; padding: 3px;} -#oc-dialog-filepicker-content .filelist-container { - box-sizing: border-box; - display: inline-block; - overflow-y: auto; - height: 100%; /** overflow under the button row */ - width: 100%; -} - -#oc-dialog-filepicker-content .emptycontent { - color: #888; - text-align: center; - margin-top: 80px; - width: 100%; - display: none; -} -#oc-dialog-filepicker-content .filelist { - background-color:white; - width:100%; -} -#oc-dialog-filepicker-content #filestable.filelist { - /* prevent the filepicker to overflow */ - min-width: initial; - margin-bottom: 50px; -} - -#oc-dialog-filepicker-content .filelist td { - padding: 14px; - border-bottom: 1px solid #eee; -} -#oc-dialog-filepicker-content .filelist tr:last-child td { - border-bottom: none; -} -#oc-dialog-filepicker-content .filelist .filename { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - background-size: 32px; - background-repeat: no-repeat; - padding-left: 51px; - background-position: 7px 7px; - cursor: pointer; -} - -#oc-dialog-filepicker-content .filelist .filesize, -#oc-dialog-filepicker-content .filelist .date { - width: 80px; -} - -#oc-dialog-filepicker-content .filelist .filesize { - text-align: right; -} -#oc-dialog-filepicker-content .filepicker_element_selected { background-color:lightblue;} -.ui-dialog {position:fixed !important;} -span.ui-icon {float: left; margin: 3px 7px 30px 0;} - -.move2trash { /* decrease spinner size */ - width: 16px; - height: 16px; -} - -/* ---- TOOLTIPS ---- */ -.extra-data { - padding-right: 5px !important; -} -.tipsy-inner { - max-width: 400px !important; - overflow: hidden; - text-overflow: ellipsis; -} - -/* ---- TAGS ---- */ -#tagsdialog .content { - width: 100%; height: 280px; -} -#tagsdialog .scrollarea { - overflow:auto; border:1px solid #ddd; - width: 100%; height: 240px; -} -#tagsdialog .bottombuttons { - width: 100%; height: 30px; -} -#tagsdialog .bottombuttons * { float:left;} -#tagsdialog .taglist li { background:#f8f8f8; padding:.3em .8em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; -webkit-transition:background-color 500ms; transition:background-color 500ms; } -#tagsdialog .taglist li:hover, #tagsdialog .taglist li:active { background:#eee; } -#tagsdialog .addinput { width: 90%; clear: both; } - -/* ---- APP SETTINGS - LEGACY, DO NOT USE THE POPUP! ---- */ -.popup { - background-color: #fff; - border-radius: 3px; - box-shadow: 0 0 10px #aaa; - color: #333; - padding: 10px; - position: fixed !important; - z-index: 100; -} -.popup.topright { top:7em; right:1em; } -.popup.bottomleft { bottom:1em; left:33em; } -.popup .close { position:absolute; top:0.2em; right:0.2em; height:20px; width:20px; background:url('../img/actions/close.svg?v=1') no-repeat center; } -.popup h2 { font-size:20px; } -.arrow { border-bottom:10px solid white; border-left:10px solid transparent; border-right:10px solid transparent; display:block; height:0; position:absolute; width:0; z-index:201; } -.arrow.left { left:-13px; bottom:1.2em; -webkit-transform:rotate(270deg); -ms-transform:rotate(270deg); transform:rotate(270deg); } -.arrow.up { top:-8px; right:6px; } -.arrow.down { -webkit-transform:rotate(180deg); -ms-transform:rotate(180deg); transform:rotate(180deg); } - - -/* ---- BREADCRUMB ---- */ -div.crumb { - float: left; - display: block; - background-image: url('../img/breadcrumb.svg?v=1'); - background-repeat: no-repeat; - background-position: right center; - height: 44px; - background-size: auto 24px; -} -div.crumb.hidden { - display: none; -} -div.crumb a, -div.crumb > span { - position: relative; - top: 12px; - padding: 14px 24px 14px 17px; - color: #555; -} -div.crumb.last a { - padding-right: 0; -} -div.crumb:first-child a { - position: relative; - top: 13px; - padding-right: 14px; -} -div.crumb.last { - font-weight: 600; - margin-right: 10px; -} -div.crumb.ellipsized { - padding: 0 10px 0 5px; -} -div.crumb a.ellipsislink { - padding: 0 !important; - position: relative; - top: 8px !important; -} - -/* some feedback for hover/tap on breadcrumbs */ -div.crumb:hover, -div.crumb:focus, -div.crumb a:focus, -div.crumb:active { - -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; - opacity:.7; -} - -.appear { - opacity: 1; - -webkit-transition: opacity 500ms ease 0s; - -moz-transition: opacity 500ms ease 0s; - -ms-transition: opacity 500ms ease 0s; - -o-transition: opacity 500ms ease 0s; - transition: opacity 500ms ease 0s; -} -.appear.transparent { - opacity: 0; -} - - -/* public footer */ -#body-public footer { - position: relative; - text-align: center; -} - -#body-public footer .info { - color: #777; - text-align: center; - margin: 0 auto; - padding: 20px 0; -} - -#body-public footer .info a { - color: #777; - font-weight: 600; - padding: 13px; - margin: -13px; -} - - -/* LEGACY FIX only - do not use fieldsets for settings */ -fieldset.warning legend, fieldset.update legend { - top: 18px; - position: relative; -} -fieldset.warning legend + p, fieldset.update legend + p { - margin-top: 12px; -} - - -/* for IE10 */ -@-ms-viewport { - width: device-width; -} - -/* hidden input type=file field */ -.hiddenuploadfield { - width: 0; - height: 0; - opacity: 0; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; -} diff --git a/core/css/styles.scss b/core/css/styles.scss new file mode 100644 index 00000000000..affaec14342 --- /dev/null +++ b/core/css/styles.scss @@ -0,0 +1,1274 @@ +/* Copyright (c) 2011, Jan-Christoph Borchardt, http://jancborchardt.net + This file is licensed under the Affero General Public License version 3 or later. + See the COPYING-README file. */ + +html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, dialog, figure, footer, header, hgroup, nav, section { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-weight: inherit; + font-size: 100%; + font-family: inherit; + vertical-align: baseline; + cursor: default; +} + +html, body { + height: 100%; +} + +article, aside, dialog, figure, footer, header, hgroup, nav, section { + display: block; +} + +body { + line-height: 1.5; +} + +table { + border-collapse: separate; + border-spacing: 0; + white-space: nowrap; +} + +caption, th, td { + text-align: left; + font-weight: normal; +} + +table, td, th { + vertical-align: middle; +} + +a { + border: 0; + color: #000; + text-decoration: none; + cursor: pointer; + * { + cursor: pointer; + } +} + +input { + cursor: pointer; + * { + cursor: pointer; + } +} + +select, .button span, label { + cursor: pointer; +} + +ul { + list-style: none; +} + +body { + background-color: #ffffff; + font-weight: 400; + font-size: .8em; + line-height: 1.6em; + font-family: 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif; + color: #000; + height: auto; +} + +#body-login { + text-align: center; + background-color: #0082c9; + background-image: url('../img/background.jpg?v=1'); + background-position: 50% 50%; + background-repeat: no-repeat; + background-size: cover; +} + +.two-factor-header { + text-align: center; +} + +.two-factor-provider { + text-align: center; + width: 258px !important; + display: inline-block; + margin-bottom: 0 !important; + background-color: rgba(0, 0, 0, 0.3) !important; + border: none !important; +} + +.two-factor-link { + display: inline-block; + padding: 12px; + color: rgba(255, 255, 255, 0.75); +} + +.float-spinner { + height: 32px; + display: none; +} + +#body-login .float-spinner { + margin-top: -32px; + padding-top: 32px; +} + +#nojavascript { + position: fixed; + top: 0; + bottom: 0; + height: 100%; + width: 100%; + z-index: 9000; + text-align: center; + background-color: rgba(0, 0, 0, 0.5); + color: #fff; + line-height: 125%; + font-size: 24px; + div { + display: block; + position: relative; + width: 50%; + top: 35%; + margin: 0px auto; + } + a { + color: #fff; + border-bottom: 2px dotted #fff; + &:hover, &:focus { + color: #ddd; + } + } +} + +/* SCROLLING */ + +::-webkit-scrollbar { + width: 5px; +} + +::-webkit-scrollbar-track-piece { + background-color: transparent; +} + +::-webkit-scrollbar-thumb { + background: #ddd; + border-radius: 3px; +} + +/* Searchbox */ + +.searchbox input[type='search'] { + position: relative; + font-size: 1.2em; + padding: 3px; + padding-left: 25px; + background: transparent url('../img/actions/search-white.svg?v=1') no-repeat 6px center; + color: #fff; + border: 0; + border-radius: 3px; + margin-top: 9px; + float: right; + width: 0; + cursor: pointer; + -webkit-transition: all 100ms; + transition: all 100ms; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=70)'; + opacity: .7; + &:focus, &:active, &:valid { + color: #fff; + width: 155px; + max-width: 50%; + cursor: text; + background-color: #0082c9; + border: 1px solid rgba(255, 255, 255, 0.5); + } +} + +/* CONTENT ------------------------------------------------------------------ */ + +#controls { + box-sizing: border-box; + position: fixed; + top: 45px; + right: 0; + left: 0; + height: 44px; + width: 100%; + padding: 0; + margin: 0; + background-color: rgba(255, 255, 255, 0.95); + z-index: 50; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* position controls for apps with app-navigation */ + +#app-navigation + #app-content #controls { + left: 250px; +} + +.viewer-mode #app-navigation + #app-content #controls { + left: 0; +} + +#controls { + .button, button { + box-sizing: border-box; + display: inline-block; + height: 36px; + padding: 7px 10px; + } + input { + &[type='submit'], &[type='text'], &[type='password'] { + box-sizing: border-box; + display: inline-block; + height: 36px; + padding: 7px 10px; + } + } + select { + box-sizing: border-box; + display: inline-block; + height: 36px; + padding: 7px 10px; + } + .button.hidden { + display: none; + } +} + +#content { + position: relative; + height: 100%; + width: 100%; + .hascontrols { + margin-top: 45px; + } +} + +#content-wrapper { + position: absolute; + height: 100%; + width: 100%; + overflow-x: hidden; + /* prevent horizontal scrollbar */ + padding-top: 45px; + box-sizing: border-box; +} + +/* allow horizontal scrollbar for personal and admin settings */ + +#body-settings:not(.snapjs-left) .app-settings { + overflow-x: auto; +} + +#emptycontent, .emptycontent { + color: #888; + text-align: center; + margin-top: 30vh; + width: 100%; +} + +#emptycontent.emptycontent-search, .emptycontent.emptycontent-search { + position: static; +} + +#emptycontent h2, .emptycontent h2 { + margin-bottom: 10px; + line-height: 150%; +} + +#emptycontent [class^='icon-'], .emptycontent [class^='icon-'], #emptycontent [class*=' icon-'], .emptycontent [class*=' icon-'] { + background-size: 64px; + height: 64px; + width: 64px; + margin: 0 auto 15px; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=40)'; + opacity: .4; +} + +/* LOG IN & INSTALLATION ------------------------------------------------------------ */ + +/* Some whitespace to the top */ + +#body-login { + #header { + padding-top: 100px; + } + background-attachment: fixed; + /* fix background gradient */ + height: 100%; + /* fix sticky footer */ + p.info, form fieldset legend, #datadirContent label { + text-align: center; + color: #fff; + } + form { + fieldset .warning-info, input[type='checkbox'] + label { + text-align: center; + color: #fff; + } + .warning input[type='checkbox'] { + &:hover + label, &:focus + label, + label { + color: #fff !important; + } + } + } + .update { + h2 { + margin: 0 0 20px; + } + a { + color: #fff; + border-bottom: 1px solid #aaa; + } + } + .infogroup { + margin-bottom: 15px; + } + p#message img { + vertical-align: middle; + padding: 5px; + } + div.buttons { + text-align: center; + } + p.info { + margin: 0 auto; + padding-top: 20px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + a { + font-weight: 600; + padding: 13px; + margin: -13px; + } + } + form { + position: relative; + width: 280px; + margin: 16px auto; + padding: 0; + fieldset { + margin-bottom: 20px; + text-align: left; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + #sqliteInformation { + margin-top: -20px; + margin-bottom: 20px; + } + #adminaccount { + margin-bottom: 15px; + } + fieldset legend { + width: 100%; + } + } +} + +/* Dark subtle label text */ + +/* overrides another !important statement that sets this to unreadable black */ + +#datadirContent label { + width: 100%; +} + +#body-login { + #datadirContent label { + display: block; + margin: 0; + } + form #datadirField legend { + margin-bottom: 15px; + } + #showAdvanced { + padding: 13px; + /* increase clickable area of Advanced dropdown */ + img { + vertical-align: bottom; + /* adjust position of Advanced dropdown arrow */ + margin-left: -4px; + } + } + .icon-info-white { + padding: 10px; + } + .strengthify-wrapper { + display: inline-block; + position: relative; + left: 15px; + top: -23px; + width: 250px; + } + .tipsy-inner { + font-weight: bold; + color: #ccc; + } + input { + &[type='text'], &[type='password'], &[type='email'] { + border: none; + font-weight: 300; + } + } +} + +/* strengthify wrapper */ + +/* tipsy for the strengthify wrapper looks better with following font settings */ + +/* General new input field look */ + +/* Nicely grouping input field sets */ + +.grouptop, .groupmiddle, .groupbottom { + position: relative; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +#body-login .grouptop input, .grouptop input { + margin-bottom: 0 !important; + border-bottom: 0 !important; + border-bottom-left-radius: 0 !important; + border-bottom-right-radius: 0 !important; +} + +#body-login .groupmiddle input, .groupmiddle input { + margin-top: 0 !important; + margin-bottom: 0 !important; + border-top: 0 !important; + border-bottom: 0 !important; + border-radius: 0 !important; + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1) inset !important; +} + +#body-login .groupbottom input, .groupbottom input { + margin-top: 0 !important; + border-top: 0 !important; + border-top-right-radius: 0 !important; + border-top-left-radius: 0 !important; + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1) inset !important; +} + +#body-login .groupbottom input[type=submit] { + box-shadow: none !important; +} + +/* keep the labels for screen readers but hide them since we use placeholders */ + +label.infield { + display: none; +} + +#body-login { + form { + input[type='checkbox'] + label { + position: relative; + margin: 0; + padding: 14px; + vertical-align: middle; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + .errors { + background: #fed7d7; + border: 1px solid #f00; + list-style-indent: inside; + margin: 0 0 2em; + padding: 1em; + } + } + .success { + background: #d7fed7; + border: 1px solid #0f0; + width: 35%; + margin: 30px auto; + padding: 1em; + text-align: center; + } + #showAdvanced > img { + padding: 4px; + box-sizing: border-box; + } + p.info a, #showAdvanced { + color: #fff; + } + #remember_login { + &:hover + label, &:focus + label { + opacity: .6; + } + } + #forgot-password { + &:hover, &:focus { + opacity: .6; + } + } + p.info a { + &:hover, &:focus { + opacity: .6; + } + } +} + +/* Show password toggle */ + +#show, #dbpassword { + position: absolute; + right: 1em; + top: .8em; + float: right; +} + +#show, #dbpassword, #personal-show { + display: none; +} + +#show + label, #dbpassword + label { + right: 21px; + top: 15px !important; + margin: -14px !important; + padding: 14px !important; +} + +#show:checked + label, #dbpassword:checked + label, #personal-show:checked + label { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=80)'; + opacity: .8; +} + +#show + label, #dbpassword + label, #personal-show + label { + position: absolute !important; + height: 20px; + width: 24px; + background-image: url('../img/actions/toggle.svg?v=1'); + background-repeat: no-repeat; + background-position: center; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=30)'; + opacity: .3; +} + +#show + label:before, #dbpassword + label:before, #personal-show + label:before { + display: none; +} + +#pass2, input[name='personal-password-clone'] { + padding: .6em 2.5em .4em .4em; + width: 8em; +} + +#personal-show + label { + height: 14px; + margin-top: -25px; + left: 295px; + display: block; +} + +#passwordbutton { + margin-left: .5em; +} + +/* Database selector */ + +#body-login { + form #selectDbType { + text-align: center; + white-space: nowrap; + margin: 0; + .info { + white-space: normal; + } + label { + position: static; + margin: 0 -3px 5px; + font-size: 12px; + background: #f8f8f8; + color: #888; + cursor: pointer; + border: 1px solid #ddd; + span { + cursor: pointer; + padding: 10px 20px; + } + &.ui-state-hover, &.ui-state-active { + color: #000; + background-color: #e8e8e8; + } + } + } + .warning, .update, .error { + display: block; + padding: 10px; + background-color: rgba(0, 0, 0, 0.3); + color: #fff; + text-align: left; + border-radius: 3px; + cursor: default; + } + .update { + width: inherit; + text-align: center; + .appList { + list-style: disc; + text-align: left; + margin-left: 25px; + margin-right: 25px; + } + } + .v-align { + width: inherit; + } + .update img.float-spinner { + float: left; + } +} + +/* Warnings and errors are the same */ + +#body-user .warning, #body-settings .warning { + margin-top: 8px; + padding: 5px; + background: #fdd; + border-radius: 3px; +} + +.warning { + legend, a { + color: #fff !important; + font-weight: 600 !important; + } +} + +.error { + a { + color: #fff !important; + font-weight: 600 !important; + &.button { + color: #555 !important; + display: inline-block; + text-align: center; + } + } + pre { + white-space: pre-wrap; + text-align: left; + } +} + +.error-wide { + width: 700px; + margin-left: -200px !important; + .button { + color: black !important; + } +} + +.warning-input { + border-color: #ce3702 !important; +} + +/* Fixes for log in page, TODO should be removed some time */ + +#body-login { + ul.error-wide { + margin-top: 35px; + } + .warning { + margin: 0 7px 5px 4px; + legend { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } + } + a.warning { + cursor: pointer; + } + .updateProgress .error { + margin-top: 10px; + margin-bottom: 10px; + } +} + +/* fixes for update page TODO should be fixed some time in a proper way */ +/* this is just for an error while updating the ownCloud instance */ + +/* Alternative Logins */ + +#alternative-logins { + legend { + margin-bottom: 10px; + } + li { + height: 40px; + display: inline-block; + white-space: nowrap; + } +} + +/* Log in and install button */ + +#body-login input { + font-size: 20px; + margin: 5px; + padding: 11px 10px 9px; + &[type='text'], &[type='password'] { + width: 249px; + } + &.login { + width: 269px; + background-position: right 16px center; + } + &[type='submit'] { + padding: 10px 20px; + /* larger log in and installation buttons */ + } +} + +#remember_login { + margin: 18px 5px 0 16px !important; +} + +#body-login { + .remember-login-container { + display: inline-block; + margin: 10px 0; + text-align: center; + width: 100%; + } + #forgot-password { + padding: 11px; + float: right; + color: #fff; + } + .wrapper { + min-height: 100%; + margin: 0 auto -70px; + width: 300px; + } + footer, .push { + height: 70px; + } +} + +/* Sticky footer */ + +/* round profile photos */ + +.avatar, .avatardiv { + border-radius: 50%; + flex-shrink: 0; + img { + border-radius: 50%; + flex-shrink: 0; + } +} + +td.avatar { + border-radius: 0; +} + +#notification-container { + position: absolute; + top: 0; + width: 100%; + text-align: center; +} + +#notification { + margin: 0 auto; + max-width: 60%; + z-index: 8000; + background-color: #fff; + border: 0; + padding: 1px 8px; + display: none; + position: relative; + top: 0; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=90)'; + opacity: .9; + span { + cursor: pointer; + margin-left: 1em; + } + overflow-x: hidden; + overflow-y: auto; + max-height: 100px; + .row { + position: relative; + .close { + display: inline-block; + vertical-align: middle; + position: absolute; + right: 0; + top: 0; + margin-top: 2px; + } + &.closeable { + padding-right: 20px; + } + } +} + +tr .action:not(.permanent), .selectedActions a { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=0)'; + opacity: 0; +} + +tr { + &:hover .action, &:focus .action, .action.permanent { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)'; + opacity: .5; + } +} + +.selectedActions a { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)'; + opacity: .5; +} + +tr .action { + width: 16px; + height: 16px; +} + +.header-action { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=80)'; + opacity: .8; +} + +tr { + &:hover .action:hover, &:focus .action:focus { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } +} + +.selectedActions a { + &:hover, &:focus { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } +} + +.header-action { + &:hover, &:focus { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)'; + opacity: 1; + } +} + +tbody tr { + &:hover, &:focus, &:active { + background-color: #f8f8f8; + } +} + +code { + font-family: 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', monospace; +} + +#quota { + cursor: default; + margin: 30px !important; + position: relative; + padding: 0 !important; + div { + padding: 0; + background-color: rgb(220, 220, 220); + font-weight: normal; + white-space: nowrap; + border-bottom-left-radius: 3px; + border-top-left-radius: 3px; + min-width: 1%; + max-width: 100%; + } +} + +#quotatext { + padding: .6em 1em; +} + +#quota div.quota-warning { + background-color: #fc4; +} + +.pager { + list-style: none; + float: right; + display: inline; + margin: .7em 13em 0 0; + li { + display: inline-block; + } +} + +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { + overflow: hidden; + text-overflow: ellipsis; +} + +.separator { + display: inline; + border-left: 1px solid #d3d3d3; + border-right: 1px solid #fff; + height: 10px; + width: 0px; + margin: 4px; +} + +a.bookmarklet { + background-color: #ddd; + border: 1px solid #ccc; + padding: 5px; + padding-top: 0px; + padding-bottom: 2px; + text-decoration: none; + margin-top: 5px; +} + +.exception { + color: #000; + textarea { + width: 95%; + height: 200px; + background: #ffe; + border: 0; + } +} + +.ui-icon-circle-triangle-e { + background-image: url('../img/actions/play-next.svg?v=1'); +} + +.ui-icon-circle-triangle-w { + background-image: url('../img/actions/play-previous.svg?v=1'); +} + +.ui-datepicker-prev, .ui-datepicker-next { + border: 1px solid #ddd; + background: #fff; +} + +/* ---- DIALOGS ---- */ + +#oc-dialog-filepicker-content { + .dirtree { + width: 92%; + float: left; + margin-left: 15px; + overflow: hidden; + div:first-child a { + background-image: url('../img/places/home.svg?v=1'); + background-repeat: no-repeat; + background-position: left center; + } + span { + &:not(:last-child) { + cursor: pointer; + } + &:last-child { + font-weight: bold; + } + &:not(:last-child)::after { + content: '>'; + padding: 3px; + } + } + } + .filelist-container { + box-sizing: border-box; + display: inline-block; + overflow-y: auto; + height: 100%; + /** overflow under the button row */ + width: 100%; + } + .emptycontent { + color: #888; + text-align: center; + margin-top: 80px; + width: 100%; + display: none; + } + .filelist { + background-color: white; + width: 100%; + } + #filestable.filelist { + /* prevent the filepicker to overflow */ + min-width: initial; + margin-bottom: 50px; + } + .filelist { + td { + padding: 14px; + border-bottom: 1px solid #eee; + } + tr:last-child td { + border-bottom: none; + } + .filename { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + background-size: 32px; + background-repeat: no-repeat; + padding-left: 51px; + background-position: 7px 7px; + cursor: pointer; + } + .filesize, .date { + width: 80px; + } + .filesize { + text-align: right; + } + } + .filepicker_element_selected { + background-color: lightblue; + } +} + +.ui-dialog { + position: fixed !important; +} + +span.ui-icon { + float: left; + margin: 3px 7px 30px 0; +} + +.move2trash { + /* decrease spinner size */ + width: 16px; + height: 16px; +} + +/* ---- TOOLTIPS ---- */ + +.extra-data { + padding-right: 5px !important; +} + +.tipsy-inner { + max-width: 400px !important; + overflow: hidden; + text-overflow: ellipsis; +} + +/* ---- TAGS ---- */ + +#tagsdialog { + .content { + width: 100%; + height: 280px; + } + .scrollarea { + overflow: auto; + border: 1px solid #ddd; + width: 100%; + height: 240px; + } + .bottombuttons { + width: 100%; + height: 30px; + * { + float: left; + } + } + .taglist li { + background: #f8f8f8; + padding: .3em .8em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + -webkit-transition: background-color 500ms; + transition: background-color 500ms; + &:hover, &:active { + background: #eee; + } + } + .addinput { + width: 90%; + clear: both; + } +} + +/* ---- APP SETTINGS - LEGACY, DO NOT USE THE POPUP! ---- */ + +.popup { + background-color: #fff; + border-radius: 3px; + box-shadow: 0 0 10px #aaa; + color: #333; + padding: 10px; + position: fixed !important; + z-index: 100; + &.topright { + top: 7em; + right: 1em; + } + &.bottomleft { + bottom: 1em; + left: 33em; + } + .close { + position: absolute; + top: 0.2em; + right: 0.2em; + height: 20px; + width: 20px; + background: url('../img/actions/close.svg?v=1') no-repeat center; + } + h2 { + font-size: 20px; + } +} + +.arrow { + border-bottom: 10px solid white; + border-left: 10px solid transparent; + border-right: 10px solid transparent; + display: block; + height: 0; + position: absolute; + width: 0; + z-index: 201; + &.left { + left: -13px; + bottom: 1.2em; + -webkit-transform: rotate(270deg); + -ms-transform: rotate(270deg); + transform: rotate(270deg); + } + &.up { + top: -8px; + right: 6px; + } + &.down { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); + } +} + +/* ---- BREADCRUMB ---- */ + +div.crumb { + float: left; + display: block; + background-image: url('../img/breadcrumb.svg?v=1'); + background-repeat: no-repeat; + background-position: right center; + height: 44px; + background-size: auto 24px; + &.hidden { + display: none; + } + a, > span { + position: relative; + top: 12px; + padding: 14px 24px 14px 17px; + color: #555; + } + &.last a { + padding-right: 0; + } + &:first-child a { + position: relative; + top: 13px; + padding-right: 14px; + } + &.last { + font-weight: 600; + margin-right: 10px; + } + &.ellipsized { + padding: 0 10px 0 5px; + } + a.ellipsislink { + padding: 0 !important; + position: relative; + top: 8px !important; + } + &:hover, &:focus, a:focus, &:active { + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=70)'; + opacity: .7; + } +} + +/* some feedback for hover/tap on breadcrumbs */ + +.appear { + opacity: 1; + -webkit-transition: opacity 500ms ease 0s; + -moz-transition: opacity 500ms ease 0s; + -ms-transition: opacity 500ms ease 0s; + -o-transition: opacity 500ms ease 0s; + transition: opacity 500ms ease 0s; + &.transparent { + opacity: 0; + } +} + +/* public footer */ + +#body-public footer { + position: relative; + text-align: center; + .info { + color: #777; + text-align: center; + margin: 0 auto; + padding: 20px 0; + a { + color: #777; + font-weight: 600; + padding: 13px; + margin: -13px; + } + } +} + +/* LEGACY FIX only - do not use fieldsets for settings */ + +fieldset { + &.warning legend, &.update legend { + top: 18px; + position: relative; + } + &.warning legend + p, &.update legend + p { + margin-top: 12px; + } +} + +/* for IE10 */ +@-ms-viewport { + width: device-width; +} + + +/* hidden input type=file field */ + +.hiddenuploadfield { + width: 0; + height: 0; + opacity: 0; + -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=0)'; +} diff --git a/core/css/systemtags.css b/core/css/systemtags.css deleted file mode 100644 index d11dc741065..00000000000 --- a/core/css/systemtags.css +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2016 - * - * This file is licensed under the Affero General Public License version 3 - * or later. - * - * See the COPYING-README file. - * - */ - -.systemtags-select2-dropdown .select2-result-label .checkmark { - visibility: hidden; - margin-left: -5px; - margin-right: 5px; - padding: 4px; -} - -.systemtags-select2-dropdown .select2-result-label .new-item .systemtags-actions { - display: none; -} - -.systemtags-select2-dropdown .select2-selected .select2-result-label .checkmark { - visibility: visible; -} - -.systemtags-select2-dropdown .select2-result-label .icon { - display: inline-block; - opacity: .5; -} -.systemtags-select2-dropdown .select2-result-label .icon.rename { - padding: 4px; -} - -.systemtags-select2-dropdown .systemtags-actions { - position: absolute; - right: 5px; -} - -.systemtags-select2-dropdown .systemtags-rename-form { - display: inline-block; - width: calc(100% - 20px); - top: -6px; - position: relative; -} - -.systemtags-select2-dropdown .systemtags-rename-form input { - display: inline-block; - width: calc(100% - 40px); -} - -.systemtags-select2-container { - width: 100%; -} - -#select2-drop.systemtags-select2-dropdown .select2-results li.select2-result { - padding: 5px; -} - -.systemtags-select2-dropdown span { - line-height: 25px; -} - -.systemtags-select2-dropdown .systemtags-item { - display: inline-block; - height: 25px; - width: 100%; -} - -.systemtags-select2-dropdown .select2-result-label { - height: 25px; -} - -.systemtags-select2-container .select2-choices .select2-search-choice.select2-locked .label { - opacity: 0.5; -} - -.systemtags-select2-dropdown .label { - width:85%; - display:-moz-inline-box; - display:inline-block; - overflow:hidden; - text-overflow:ellipsis; -} -.systemtags-select2-dropdown .label.hidden { - display: none; -} diff --git a/core/css/systemtags.scss b/core/css/systemtags.scss new file mode 100644 index 00000000000..adbd36ebab1 --- /dev/null +++ b/core/css/systemtags.scss @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +.systemtags-select2-dropdown { + .select2-result-label { + .checkmark { + visibility: hidden; + margin-left: -5px; + margin-right: 5px; + padding: 4px; + } + .new-item .systemtags-actions { + display: none; + } + } + .select2-selected .select2-result-label .checkmark { + visibility: visible; + } + .select2-result-label .icon { + display: inline-block; + opacity: .5; + &.rename { + padding: 4px; + } + } + .systemtags-actions { + position: absolute; + right: 5px; + } + .systemtags-rename-form { + display: inline-block; + width: calc(100% - 20px); + top: -6px; + position: relative; + input { + display: inline-block; + width: calc(100% - 40px); + } + } +} + +.systemtags-select2-container { + width: 100%; +} + +#select2-drop.systemtags-select2-dropdown .select2-results li.select2-result { + padding: 5px; +} + +.systemtags-select2-dropdown { + span { + line-height: 25px; + } + .systemtags-item { + display: inline-block; + height: 25px; + width: 100%; + } + .select2-result-label { + height: 25px; + } +} + +.systemtags-select2-container .select2-choices .select2-search-choice.select2-locked .label { + opacity: 0.5; +} + +.systemtags-select2-dropdown .label { + width: 85%; + display: -moz-inline-box; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + &.hidden { + display: none; + } +} diff --git a/core/css/tooltip.css b/core/css/tooltip.css deleted file mode 100644 index e5a2f7b2145..00000000000 --- a/core/css/tooltip.css +++ /dev/null @@ -1,119 +0,0 @@ -/*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -.tooltip { - position: absolute; - display: block; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-style: normal; - font-weight: normal; - letter-spacing: normal; - line-break: auto; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - white-space: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - font-size: 12px; - opacity: 0; - z-index: 100000; - filter: alpha(opacity=0); -} -.tooltip.in { - opacity: 0.9; - filter: alpha(opacity=90); -} -.tooltip.top { - margin-top: -3px; - padding: 5px 0; -} -.tooltip.right { - margin-left: 3px; - padding: 0 5px; -} -.tooltip.bottom { - margin-top: 3px; - padding: 5px 0; -} -.tooltip.left { - margin-left: -3px; - padding: 0 5px; -} -.tooltip-inner { - max-width: 350px; - padding: 3px 8px; - color: #ffffff; - text-align: center; - background-color: #000000; - border-radius: 4px; -} -.tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.tooltip.top .tooltip-arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-width: 5px 5px 0; - border-top-color: #000000; -} -.tooltip.top-left .tooltip-arrow { - bottom: 0; - right: 5px; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000000; -} -.tooltip.top-right .tooltip-arrow { - bottom: 0; - left: 5px; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000000; -} -.tooltip.right .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-width: 5px 5px 5px 0; - border-right-color: #000000; -} -.tooltip.left .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-width: 5px 0 5px 5px; - border-left-color: #000000; -} -.tooltip.bottom .tooltip-arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000000; -} -.tooltip.bottom-left .tooltip-arrow { - top: 0; - right: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000000; -} -.tooltip.bottom-right .tooltip-arrow { - top: 0; - left: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000000; -} diff --git a/core/css/tooltip.scss b/core/css/tooltip.scss new file mode 100644 index 00000000000..42289787e8d --- /dev/null +++ b/core/css/tooltip.scss @@ -0,0 +1,125 @@ +/*! + * Bootstrap v3.3.5 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +.tooltip { + position: absolute; + display: block; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-style: normal; + font-weight: normal; + letter-spacing: normal; + line-break: auto; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + white-space: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + font-size: 12px; + opacity: 0; + z-index: 100000; + filter: alpha(opacity = 0); + &.in { + opacity: 0.9; + filter: alpha(opacity = 90); + } + &.top { + margin-top: -3px; + padding: 5px 0; + } + &.right { + margin-left: 3px; + padding: 0 5px; + } + &.bottom { + margin-top: 3px; + padding: 5px 0; + } + &.left { + margin-left: -3px; + padding: 0 5px; + } +} + +.tooltip-inner { + max-width: 350px; + padding: 3px 8px; + color: #ffffff; + text-align: center; + background-color: #000000; + border-radius: 4px; +} + +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.tooltip { + &.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000000; + } + &.top-left .tooltip-arrow { + bottom: 0; + right: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000000; + } + &.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000000; + } + &.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000000; + } + &.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000000; + } + &.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; + } + &.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; + } + &.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; + } +} diff --git a/core/css/update.css b/core/css/update.css index 12cda3d5bbd..0ae72fd04a0 100644 --- a/core/css/update.css +++ b/core/css/update.css @@ -31,3 +31,419 @@ #body-login .warning.hidden { display: none; } + +/** + * Below this is a copy of the original CSS because we moved to on-the-fly + * generated CSS from SCSS which doesn't work during update + */ + +/** HEADER **/ + +/* prevent ugly selection effect on accidental selection */ +#header { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +/* removed until content-focusing issue is fixed */ +#skip-to-content a { + position: absolute; + left: -10000px; + top: auto; + width: 1px; + height: 1px; + overflow: hidden; +} +#skip-to-content a:focus { + left: 76px; + top: -9px; + color: #fff; + width: auto; + height: auto; +} + +/* HEADERS ------------------------------------------------------------------ */ + +#header .logo { + background-image: url('../img/logo-icon.svg?v=1'); + background-repeat: no-repeat; + background-size: 175px; + background-position: center; + width: 252px; + height: 120px; + margin: 0 auto; +} + +/** STYLES **/ + +/* Copyright (c) 2011, Jan-Christoph Borchardt, http://jancborchardt.net + This file is licensed under the Affero General Public License version 3 or later. + See the COPYING-README file. */ + +html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, dialog, figure, footer, header, hgroup, nav, section { margin:0; padding:0; border:0; outline:0; font-weight:inherit; font-size:100%; font-family:inherit; vertical-align:baseline; cursor:default; } +html, body { height:100%; } +article, aside, dialog, figure, footer, header, hgroup, nav, section { display:block; } +body { line-height:1.5; } +table { border-collapse:separate; border-spacing:0; white-space:nowrap; } +caption, th, td { text-align:left; font-weight:normal; } +table, td, th { vertical-align:middle; } +a { border:0; color:#000; text-decoration:none;} +a, a *, input, input *, select, .button span, label { cursor:pointer; } +ul { list-style:none; } + +body { + background-color: #ffffff; + font-weight: 400; + font-size: .8em; + line-height: 1.6em; + font-family: 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif; + color: #000; + height: auto; +} + +#body-login { + text-align: center; + background-color: #0082c9; + background-image: url('../img/background.jpg?v=1'); + background-position: 50% 50%; + background-repeat: no-repeat; + background-size: cover; +} + +#nojavascript { + position: fixed; + top: 0; + bottom: 0; + height: 100%; + width: 100%; + z-index: 9000; + text-align: center; + background-color: rgba(0,0,0,0.5); + color: #fff; + line-height: 125%; + font-size: 24px; +} +#nojavascript div { + display: block; + position: relative; + width: 50%; + top: 35%; + margin: 0px auto; +} +#nojavascript a { + color: #fff; + border-bottom: 2px dotted #fff; +} +#nojavascript a:hover, +#nojavascript a:focus { + color: #ddd; +} + +/* SCROLLING */ +::-webkit-scrollbar { + width: 5px; +} +::-webkit-scrollbar-track-piece { + background-color: transparent; +} +::-webkit-scrollbar-thumb { + background: #ddd; + border-radius: 3px; +} + +/* LOG IN & INSTALLATION ------------------------------------------------------------ */ + +/* Some whitespace to the top */ +#body-login #header { + padding-top: 100px; +} +#body-login { + background-attachment: fixed; /* fix background gradient */ + height: 100%; /* fix sticky footer */ +} + +/* Dark subtle label text */ +#body-login p.info { + text-align: center; + color: #fff; +} + +#body-login .update h2 { + margin: 0 0 20px; +} + +#body-login .update a { + color: #fff; + border-bottom: 1px solid #aaa; +} + +#body-login .infogroup { + margin-bottom: 15px; +} + +#body-login p#message img { + vertical-align: middle; + padding: 5px; +} + +#body-login p.info { + margin: 0 auto; + padding-top: 20px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +#body-login p.info a { + font-weight: 600; + padding: 13px; + margin: -13px; + color: #fff; +} + +#body-login .success { + background:#d7fed7; + border:1px solid #0f0; + width: 35%; + margin: 30px auto; + padding:1em; + text-align: center; +} + +#body-login p.info a:hover, +#body-login p.info a:focus { + opacity: .6; +} + +/* Warnings and errors are the same */ +#body-login .warning, +#body-login .update, +#body-login .error { + display: block; + padding: 10px; + background-color: rgba(0,0,0,.3); + color: #fff; + text-align: left; + border-radius: 3px; + cursor: default; +} + +#body-login .update { + width: inherit; + text-align: center; +} + +#body-login .update .appList { + list-style: disc; + text-align: left; + margin-left: 25px; + margin-right: 25px; +} + +#body-login .v-align { + width: inherit; +} + +.error a { + color: #fff !important; + font-weight: 600 !important; +} +.error a.button { + color: #555 !important; + display: inline-block; + text-align: center; +} +.error pre { + white-space: pre-wrap; + text-align: left; +} + +/* fixes for update page TODO should be fixed some time in a proper way */ +/* this is just for an error while updating the ownCloud instance */ +#body-login .updateProgress .error { + margin-top: 10px; + margin-bottom: 10px; +} + +/* Log in and install button */ +#body-login input { + font-size: 20px; + margin: 5px; + padding: 11px 10px 9px; +} +#body-login input.login { + width: 269px; + background-position: right 16px center; +} + +/* Sticky footer */ +#body-login .wrapper { + min-height: 100%; + margin: 0 auto -70px; + width: 300px; +} +#body-login footer, #body-login .push { + height: 70px; +} + +code { font-family:"Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", monospace; } + +/* for IE10 */ +@-ms-viewport { + width: device-width; +} + +/** APPS **/ + +/* buttons */ +button.loading { + background-image: url('../img/loading.gif'); + background-position: right 10px center; background-repeat: no-repeat; + background-size: 16px; + padding-right: 30px; +} + +/* heading styles */ +h2 { + font-size: 20px; + font-weight: 300; + margin-bottom: 12px; + line-height: 140%; +} +h3 { + font-size: 15px; + font-weight: 300; + margin: 12px 0; +} + +/* do not use italic typeface style, instead lighter color */ +em { + font-style: normal; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + opacity: .5; +} + +/** ICONS **/ + +[class^="icon-"], [class*=" icon-"] { + background-repeat: no-repeat; + background-position: center; + min-width: 16px; + min-height: 16px; +} + +/* general assets */ + +.icon-loading-dark { + position: relative; +} +.icon-loading-dark:after { + z-index: 2; + content: ""; + height: 30px; + width: 30px; + margin: -16px 0 0 -16px; + position: absolute; + top: 50%; + left: 50%; + border-radius: 100%; + -webkit-animation: rotate .8s infinite linear; + animation: rotate .8s infinite linear; + -webkit-transform-origin: center; + -ms-transform-origin: center; + transform-origin: center; +} +.icon-loading-dark:after { + border: 2px solid rgba(150, 150, 150, .5); + border-top-color: rgb(100, 100, 100); +} + +.icon-loading-dark:after, +.icon-loading-small-dark:after { + border: 2px solid rgba(187, 187, 187, .5); + border-top-color: #bbb; +} + +/* Css replaced elements don't have ::after nor ::before */ +img.icon-loading-dark, object.icon-loading-dark, video.icon-loading-dark, button.icon-loading-dark, textarea.icon-loading-dark, input.icon-loading-dark, select.icon-loading-dark { + background-image: url("../img/loading-dark.gif"); +} + +@-webkit-keyframes rotate { + from { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes rotate { + from { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +.icon-32 { + background-size: 32px !important; +} + +.icon-checkmark-white { + background-image: url('../img/actions/checkmark-white.svg?v=1'); +} + +.icon-error-white { + background-image: url('../img/actions/error-white.svg?v=1'); +} + +/* INPUTS */ + +/* specifically override browser styles */ +input { + font-family: 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif; +} + +input[type="button"] { + width: 130px; + margin: 3px 3px 3px 0; + padding: 7px 6px 5px; + font-size: 13px; + background-color: #fff; + color: #333; + border: 1px solid #ddd; + outline: none; + border-radius: 3px; +} + +/* correctly align images inside of buttons */ +input img { + vertical-align: text-bottom; +} + +/* BUTTONS */ +input[type="button"] { + width: auto; + min-width: 25px; + padding: 5px; + background-color: rgba(240,240,240,.9); + font-weight: 600; + color: #555; + border: 1px solid rgba(240,240,240,.9); + cursor: pointer; +} + +input[type="button"]:hover, input[type="button"]:focus { + background-color: rgba(255, 255, 255, .95); + color: #111; +} +input[type="button"] img { + border: none; + box-shadow: none; +} diff --git a/core/js/mimetypelist.js b/core/js/mimetypelist.js index e1b9dba14af..89558e21086 100644 --- a/core/js/mimetypelist.js +++ b/core/js/mimetypelist.js @@ -86,6 +86,7 @@ OC.MimeTypeList={ "text/x-c": "text/code", "text/x-c++src": "text/code", "text/x-h": "text/code", + "text/x-ldif": "text/code", "text/x-java-source": "text/code", "text/x-python": "text/code", "text/x-shellscript": "text/code", diff --git a/core/js/systemtags/systemtagsinputfield.js b/core/js/systemtags/systemtagsinputfield.js index 690525c0ebb..d5f6bd5f97e 100644 --- a/core/js/systemtags/systemtagsinputfield.js +++ b/core/js/systemtags/systemtagsinputfield.js @@ -40,7 +40,9 @@ '<form class="systemtags-rename-form">' + ' <label class="hidden-visually" for="{{cid}}-rename-input">{{renameLabel}}</label>' + ' <input id="{{cid}}-rename-input" type="text" value="{{name}}">' + - ' <a href="#" class="delete icon icon-delete" title="{{deleteTooltip}}"></a>' + + ' {{#if isAdmin}}' + + ' <a href="#" class="delete icon icon-delete" title="{{deleteTooltip}}"></a>' + + ' {{/if}}' + '</form>'; /** @@ -148,7 +150,8 @@ cid: this.cid, name: oldName, deleteTooltip: t('core', 'Delete'), - renameLabel: t('core', 'Rename') + renameLabel: t('core', 'Rename'), + isAdmin: this._isAdmin })); $item.find('.label').after($renameForm); $item.find('.label, .systemtags-actions').addClass('hidden'); diff --git a/core/js/tests/specs/systemtags/systemtagsinputfieldSpec.js b/core/js/tests/specs/systemtags/systemtagsinputfieldSpec.js index 503bef7cf2b..8d3f67bfa0d 100644 --- a/core/js/tests/specs/systemtags/systemtagsinputfieldSpec.js +++ b/core/js/tests/specs/systemtags/systemtagsinputfieldSpec.js @@ -267,20 +267,6 @@ describe('OC.SystemTags.SystemTagsInputField tests', function() { saveStub.restore(); }); - it('deletes model and submits change when clicking delete', function() { - var destroyStub = sinon.stub(OC.SystemTags.SystemTagModel.prototype, 'destroy'); - - expect($dropdown.find('.delete').length).toEqual(0); - $dropdown.find('.rename').mouseup(); - // delete button appears - expect($dropdown.find('.delete').length).toEqual(1); - $dropdown.find('.delete').mouseup(); - - expect(destroyStub.calledOnce).toEqual(true); - expect(destroyStub.calledOn(view.collection.get('1'))); - - destroyStub.restore(); - }); }); describe('setting data', function() { it('sets value when calling setValues', function() { @@ -299,12 +285,18 @@ describe('OC.SystemTags.SystemTagsInputField tests', function() { }); describe('as admin', function() { + var $dropdown; + beforeEach(function() { view = new OC.SystemTags.SystemTagsInputField({ isAdmin: true }); - view.render(); $('.testInputContainer').append(view.$el); + $dropdown = $('<div class="select2-dropdown"></div>'); + select2Stub.withArgs('dropdown').returns($dropdown); + $('#testArea').append($dropdown); + + view.render(); }); it('formatResult renders tag name with visibility', function() { var opts = select2Stub.getCall(0).args[0]; @@ -431,15 +423,50 @@ describe('OC.SystemTags.SystemTagsInputField tests', function() { ]); }); }); + describe('tag actions', function() { + var opts; + + beforeEach(function() { + + opts = select2Stub.getCall(0).args[0]; + + view.collection.add([ + new OC.SystemTags.SystemTagModel({id: '1', name: 'abc'}), + ]); + + $dropdown.append(opts.formatResult(view.collection.get('1').toJSON())); + + }); + it('deletes model and submits change when clicking delete', function() { + var destroyStub = sinon.stub(OC.SystemTags.SystemTagModel.prototype, 'destroy'); + + expect($dropdown.find('.delete').length).toEqual(0); + $dropdown.find('.rename').mouseup(); + // delete button appears + expect($dropdown.find('.delete').length).toEqual(1); + $dropdown.find('.delete').mouseup(); + + expect(destroyStub.calledOnce).toEqual(true); + expect(destroyStub.calledOn(view.collection.get('1'))); + + destroyStub.restore(); + }); + }); }); describe('as user', function() { + var $dropdown; + beforeEach(function() { view = new OC.SystemTags.SystemTagsInputField({ isAdmin: false }); - view.render(); $('.testInputContainer').append(view.$el); + $dropdown = $('<div class="select2-dropdown"></div>'); + select2Stub.withArgs('dropdown').returns($dropdown); + $('#testArea').append($dropdown); + + view.render(); }); it('formatResult renders tag name only', function() { var opts = select2Stub.getCall(0).args[0]; @@ -570,5 +597,33 @@ describe('OC.SystemTags.SystemTagsInputField tests', function() { ]); }); }); + describe('tag actions', function() { + var opts; + + beforeEach(function() { + + opts = select2Stub.getCall(0).args[0]; + + view.collection.add([ + new OC.SystemTags.SystemTagModel({id: '1', name: 'abc'}), + ]); + + $dropdown.append(opts.formatResult(view.collection.get('1').toJSON())); + + }); + it('deletes model and submits change when clicking delete', function() { + var destroyStub = sinon.stub(OC.SystemTags.SystemTagModel.prototype, 'destroy'); + + expect($dropdown.find('.delete').length).toEqual(0); + $dropdown.find('.rename').mouseup(); + // delete button appears only for admins + expect($dropdown.find('.delete').length).toEqual(0); + $dropdown.find('.delete').mouseup(); + + expect(destroyStub.notCalled).toEqual(true); + + destroyStub.restore(); + }); + }); }); }); diff --git a/core/l10n/fr.js b/core/l10n/fr.js index 1d8b24af00b..ab1004d7d02 100644 --- a/core/l10n/fr.js +++ b/core/l10n/fr.js @@ -89,25 +89,25 @@ OC.L10N.register( "Strong password" : "Mot de passe fort", "Your web server is not yet set up properly to allow file synchronization because the WebDAV interface seems to be broken." : "Votre serveur web n'est pas correctement configuré pour la synchronisation de fichiers : l'interface WebDAV semble ne pas fonctionner.", "Your web server is not set up properly to resolve \"{url}\". Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "La configuration du serveur web ne permet pas d'atteindre \"{url}\". Consultez la <a target=\"_blank\" ref=\"noreferrer\" href=\"{docLink}\">documentation</a> pour avoir plus d'informations à ce sujet.", - "This server has no working Internet connection: Multiple endpoints could not be reached. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "Ce serveur ne peut se connecter à Internet : plusieurs point finaux ne peuvent être atteins. Cela signifie que certaines fonctionnalités, telles que le montage de supports de stockage distants, les notifications de mises à jour ou l'installation d'applications tierces ne fonctionneront pas. L'accès aux fichiers à distance, ainsi que l'envoie de notifications par mail peuvent aussi être indisponibles. Il est recommandé d'activer la connexion internet pour ce serveur si vous souhaitez disposer de l'ensemble des fonctionnalités offertes.", - "No memory cache has been configured. To enhance your performance please configure a memcache if available. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "Aucun cache mémoire n'est configuré. Si possible, configurez un \"memcache\" pour augmenter les performances. Pour plus d'information consultez la <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>.", + "This server has no working Internet connection: Multiple endpoints could not be reached. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "Ce serveur ne peut se connecter à Internet : plusieurs point finaux ne peuvent être atteints. Cela signifie que certaines fonctionnalités, telles que le montage de supports de stockage distants, les notifications de mises à jour ou l'installation d'applications tierces ne fonctionneront pas. L'accès aux fichiers à distance, ainsi que l'envoi de notifications par mail peuvent aussi être indisponibles. Il est recommandé d'activer la connexion internet pour ce serveur si vous souhaitez disposer de l'ensemble des fonctionnalités offertes.", + "No memory cache has been configured. To enhance your performance please configure a memcache if available. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "Aucun cache mémoire n'est configuré. Si possible, configurez un \"memcache\" pour améliorer les performances. Pour plus d'informations consultez la <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>.", "/dev/urandom is not readable by PHP which is highly discouraged for security reasons. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "/dev/urandom n'est pas lisible par PHP, ce qui est fortement déconseillé pour des raisons de sécurité. Consultez la <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a> pour avoir plus d'informations à ce sujet.", - "You are currently running PHP {version}. We encourage you to upgrade your PHP version to take advantage of <a target=\"_blank\" rel=\"noreferrer\" href=\"{phpLink}\">performance and security updates provided by the PHP Group</a> as soon as your distribution supports it." : "Vous utilisez actuellement PHP {version}. Nous vous encourageons à mettre à jour votre version de PHP afin de tirer avantage des amélioration liées à <a target=\"_blank\" rel=\"noreferrer\" href=\"{phpLink}\">la performance et la sécurité fournies par le PHP Group</a>, dès que votre distribution le supportera.", + "You are currently running PHP {version}. We encourage you to upgrade your PHP version to take advantage of <a target=\"_blank\" rel=\"noreferrer\" href=\"{phpLink}\">performance and security updates provided by the PHP Group</a> as soon as your distribution supports it." : "Vous utilisez actuellement PHP {version}. Nous vous encourageons à mettre à jour votre version de PHP afin de tirer avantage des améliorations liées à <a target=\"_blank\" rel=\"noreferrer\" href=\"{phpLink}\">la performance et la sécurité fournies par le PHP Group</a>, dès que votre distribution le supportera.", "The reverse proxy headers configuration is incorrect, or you are accessing Nextcloud from a trusted proxy. If you are not accessing Nextcloud from a trusted proxy, this is a security issue and can allow an attacker to spoof their IP address as visible to Nextcloud. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "La configuration des entêtes du proxy inverse est incorrecte, ou vous accédez à Nextcloud depuis un proxy de confiance. Si vous n'êtes pas en train d’accéder à Nextcloud depuis un proxy de confiance, ceci est un problème de sécurité qui peut permettre à un attaquant d'usurper l'adresse IP affichée à Nextcloud. Consultez la <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a> pour avoir plus d'informations à ce sujet.", - "Memcached is configured as distributed cache, but the wrong PHP module \"memcache\" is installed. \\OC\\Memcache\\Memcached only supports \"memcached\" and not \"memcache\". See the <a target=\"_blank\" rel=\"noreferrer\" href=\"{wikiLink}\">memcached wiki about both modules</a>." : "\"memcached\" est configuré comme cache distribué, mais le module installé est \"memcache\". \\OC\\Memcache\\Memcached ne prend en charge que \"memcached\" et non \"memcache\". <a target=\"_blank\" rel=\"noreferrer\" href=\"{wikiLink}\">Consulter le wiki de memcached à propos de ces deux modules.</a>", - "Some files have not passed the integrity check. Further information on how to resolve this issue can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>. (<a href=\"{codeIntegrityDownloadEndpoint}\">List of invalid files…</a> / <a href=\"{rescanEndpoint}\">Rescan…</a>)" : "Des fichiers n'ont pas réussi à passer la vérification d’intégrité. Plus d'information sur comment résoudre ce problème dans notre <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>. (<a href=\"{codeIntegrityDownloadEndpoint}\">Liste des fichiers invalides…</a> / <a href=\"{rescanEndpoint}\">Relancer…</a>)", + "Memcached is configured as distributed cache, but the wrong PHP module \"memcache\" is installed. \\OC\\Memcache\\Memcached only supports \"memcached\" and not \"memcache\". See the <a target=\"_blank\" rel=\"noreferrer\" href=\"{wikiLink}\">memcached wiki about both modules</a>." : "\"memcached\" est configuré comme cache distribué, mais le mauvais module PHP est installé. \\OC\\Memcache\\Memcached ne prend en charge que \"memcached\" et non \"memcache\". <a target=\"_blank\" rel=\"noreferrer\" href=\"{wikiLink}\">Consulter le wiki de memcached à propos de ces deux modules.</a>", + "Some files have not passed the integrity check. Further information on how to resolve this issue can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>. (<a href=\"{codeIntegrityDownloadEndpoint}\">List of invalid files…</a> / <a href=\"{rescanEndpoint}\">Rescan…</a>)" : "Des fichiers n'ont pas passé la vérification d’intégrité. Plus d'information sur comment résoudre ce problème dans notre <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>. (<a href=\"{codeIntegrityDownloadEndpoint}\">Liste des fichiers invalides…</a> / <a href=\"{rescanEndpoint}\">Relancer…</a>)", "Error occurred while checking server setup" : "Une erreur s'est produite lors de la vérification de la configuration du serveur", "Your data directory and your files are probably accessible from the Internet. The .htaccess file is not working. We strongly suggest that you configure your web server in a way that the data directory is no longer accessible or you move the data directory outside the web server document root." : "Votre dossier de données et vos fichiers sont probablement accessibles depuis internet. Le fichier .htaccess ne fonctionne pas. Nous vous recommandons vivement de configurer votre serveur web de façon à ce que ce dossier de données ne soit plus accessible, ou de le déplacer hors de la racine du serveur web.", "The \"{header}\" HTTP header is not configured to equal to \"{expected}\". This is a potential security or privacy risk and we recommend adjusting this setting." : "L'en-tête HTTP \"{header}\" n'est pas configurée pour être égale à \"{expected}\" créant potentiellement un risque relié à la sécurité et à la vie privée. Il est recommandé d'ajuster ce paramètre.", - "The \"Strict-Transport-Security\" HTTP header is not configured to at least \"{seconds}\" seconds. For enhanced security we recommend enabling HSTS as described in our <a href=\"{docUrl}\" rel=\"noreferrer\">security tips</a>." : "L'en-tête HTTP \"Strict-Transport-Security\" n'est pas configurée à \"{seconds}\" secondes. Pour renforcer la sécurité nous recommandons d'activer HSTS comme décrit dans notre <a href=\"{docUrl} rel=\"noreferrer\">Guide pour le renforcement et la sécurité</a>.", - "You are accessing this site via HTTP. We strongly suggest you configure your server to require using HTTPS instead as described in our <a href=\"{docUrl}\">security tips</a>." : "Vous accédez à ce site via HTTP. Nous vous recommandons fortement de configurer votre serveur pour forcer l'utilisation de HTTPS, comme expliqué dans notre <a href=\"{docUrl}\">Guide pour le renforcement et la sécurité</a>.", + "The \"Strict-Transport-Security\" HTTP header is not configured to at least \"{seconds}\" seconds. For enhanced security we recommend enabling HSTS as described in our <a href=\"{docUrl}\" rel=\"noreferrer\">security tips</a>." : "L'en-tête HTTP \"Strict-Transport-Security\" n'est pas configurée à au moins \"{seconds}\" secondes. Pour renforcer la sécurité nous recommandons d'activer HSTS comme décrit dans nos <a href=\"{docUrl} rel=\"noreferrer\">conseils de sécurisation</a>.", + "You are accessing this site via HTTP. We strongly suggest you configure your server to require using HTTPS instead as described in our <a href=\"{docUrl}\">security tips</a>." : "Vous accédez à ce site via HTTP. Nous vous recommandons fortement de configurer votre serveur pour forcer l'utilisation de HTTPS, comme expliqué dans nos <a href=\"{docUrl}\">conseils de sécurisation</a>.", "Shared" : "Partagé", "Shared with {recipients}" : "Partagé avec {recipients}", "Error" : "Erreur", "Error while sharing" : "Erreur lors de la mise en partage", "Error while unsharing" : "Erreur lors de l'annulation du partage", - "Error setting expiration date" : "Erreur lors de la spécification de la date d'expiration", - "The public link will expire no later than {days} days after it is created" : "Ce lien public expirera au plus tard {days} jours après sa création.", + "Error setting expiration date" : "Erreur lors de la configuration de la date d'expiration", + "The public link will expire no later than {days} days after it is created" : "Ce lien public expirera dans {days} jours après sa création.", "Set expiration date" : "Spécifier une date d'expiration", "Expiration" : "Expiration", "Expiration date" : "Date d'expiration", @@ -122,7 +122,7 @@ OC.L10N.register( "Link" : "Lien", "Password protect" : "Protéger par un mot de passe", "Allow upload and editing" : "Autoriser l'envoi et l'édition", - "File drop (upload only)" : "Dépôt de fichier (uniquement pour le téléversement)", + "File drop (upload only)" : "Dépôt de fichier (téléversement uniquement)", "Email link to person" : "Envoyer le lien par courriel", "Send" : "Envoyer", "Shared with you and the group {group} by {owner}" : "Partagé avec vous et le groupe {group} par {owner}", @@ -130,7 +130,7 @@ OC.L10N.register( "{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} a partagé via un lien", "group" : "groupe", "remote" : "distant", - "email" : "Adresse e-mail", + "email" : "Adresse de courriel", "Unshare" : "Ne plus partager", "can reshare" : "peut repartager", "can edit" : "peut modifier", @@ -147,14 +147,14 @@ OC.L10N.register( "{sharee} (remote)" : "{sharee} (distant)", "{sharee} (email)" : "{sharee} (email)", "Share" : "Partager", - "Share with people on other servers using their Federated Cloud ID username@example.com/nextcloud" : "Partager avec des personnes sur d'autres serveurs en utilisant leur identifiant du Cloud Fédéré utilisateur@exemple.com/nextcloud", - "Share with users or by mail..." : "Partager avec des utilisateurs ou par mail…", + "Share with people on other servers using their Federated Cloud ID username@example.com/nextcloud" : "Partager avec des personnes sur d'autres serveurs en utilisant leur identifiant du Cloud Fédéré (utilisateur@exemple.com/nextcloud)", + "Share with users or by mail..." : "Partager avec des utilisateurs ou par courriel…", "Share with users or remote users..." : "Partager avec des utilisateurs ou des utilisateurs distants...", - "Share with users, remote users or by mail..." : "Partager avec des utilisateurs, des utilisateurs distants ou par mail…", + "Share with users, remote users or by mail..." : "Partager avec des utilisateurs, des utilisateurs distants ou par courriel…", "Share with users or groups..." : "Partager avec des utilisateurs ou des groupes...", - "Share with users, groups or by mail..." : "Partager avec des utilisateurs, des groupes ou par mail…", + "Share with users, groups or by mail..." : "Partager avec des utilisateurs, des groupes ou par courriel…", "Share with users, groups or remote users..." : "Partager avec des utilisateurs, groupes ou utilisateurs distants...", - "Share with users, groups, remote users or by mail..." : "Partager avec des utilisateurs, des groupes, des utilisateurs distants ou par mail…", + "Share with users, groups, remote users or by mail..." : "Partager avec des utilisateurs, des groupes, des utilisateurs distants ou par courriel…", "Share with users..." : "Partager avec des utilisateurs...", "Error removing share" : "Erreur lors de l'arrêt du partage", "Non-existing tag #{tag}" : "Étiquette #{tag} inexistante", @@ -166,7 +166,7 @@ OC.L10N.register( "Collaborative tags" : "Étiquettes collaboratives ", "No tags found" : "Aucune étiquette n'a été trouvée", "The object type is not specified." : "Le type d'objet n'est pas spécifié.", - "Enter new" : "Saisir un nouveau", + "Enter new" : "Nouvelle étiquette", "Add" : "Ajouter", "Edit tags" : "Modifier les étiquettes", "Error loading dialog template: {error}" : "Erreur lors du chargement du modèle de dialogue : {error}", @@ -228,13 +228,13 @@ OC.L10N.register( "Database user" : "Utilisateur de la base de données", "Database password" : "Mot de passe de la base de données", "Database name" : "Nom de la base de données", - "Database tablespace" : "Tablespace de la base de données", + "Database tablespace" : "Espace de stockage de la base de données", "Database host" : "Hôte de la base de données", "Please specify the port number along with the host name (e.g., localhost:5432)." : "Veuillez spécifier le numéro du port avec le nom de l'hôte (ex: localhost:5432).", "Performance warning" : "Avertissement à propos des performances", "SQLite will be used as database." : "SQLite sera utilisé comme gestionnaire de base de données.", "For larger installations we recommend to choose a different database backend." : "Pour des installations plus volumineuses, nous vous conseillons d'utiliser un autre gestionnaire de base de données.", - "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "En particulier si vous utilisez le client de bureau pour synchroniser vos données : l'utilisation de SQLite est alors déconseillée.", + "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "En particulier si vous utilisez le client de bureau pour synchroniser vos données, l'utilisation de SQLite est déconseillée.", "Finish setup" : "Terminer l'installation", "Finishing …" : "Finalisation …", "Need help?" : "Besoin d'aide ?", @@ -248,7 +248,7 @@ OC.L10N.register( "Please contact your administrator." : "Veuillez contacter votre administrateur.", "An internal error occurred." : "Une erreur interne est survenue.", "Please try again or contact your administrator." : "Veuillez réessayer ou contactez votre administrateur.", - "Username or email" : "Nom d'utilisateur ou e-mail", + "Username or email" : "Nom d'utilisateur ou adresse de courriel", "Wrong password. Reset it?" : "Mot de passe incorrect. Réinitialiser ?", "Wrong password." : "Mot de passe incorrect.", "Log in" : "Se connecter", @@ -282,7 +282,7 @@ OC.L10N.register( "To avoid timeouts with larger installations, you can instead run the following command from your installation directory:" : "Afin d'éviter les timeouts avec les installations de volume conséquent, vous pouvez exécuter la commande suivante depuis le répertoire d'installation :", "Detailed logs" : "Journaux détaillés", "Update needed" : "Mise à jour nécessaire", - "Please use the command line updater because you have a big instance." : "Veuillez utiliser la mise à jour en ligne de commande, votre instance est trop volumineuse.", + "Please use the command line updater because you have a big instance." : "Veuillez utiliser la mise à jour en ligne de commande car votre instance est volumineuse.", "For help, see the <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">documentation</a>." : "Pour obtenir de l'aide, lisez la <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">documentation</a>.", "This %s instance is currently in maintenance mode, which may take a while." : "Cette instance de %s est en cours de maintenance, cela peut prendre du temps.", "This page will refresh itself when the %s instance is available again." : "Cette page se rafraîchira d'elle-même lorsque l'instance %s sera à nouveau disponible.", @@ -339,7 +339,7 @@ OC.L10N.register( "Oct." : "Oct.", "Nov." : "Nov.", "Dec." : "Déc.", - "This server has no working Internet connection. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "Ce serveur ne peut se connecter à internet. Cela signifie que certaines fonctionnalités, telles que le montage de supports de stockage distants, les notifications de mises à jour ou l'installation d'applications tierces ne fonctionneront pas. L'accès aux fichiers à distance, ainsi que les notifications par mail peuvent aussi être indisponibles. Il est recommandé d'activer la connexion internet pour ce serveur si vous souhaitez disposer de l'ensemble des fonctionnalités offertes.", + "This server has no working Internet connection. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "Ce serveur ne peut se connecter à internet. Cela signifie que certaines fonctionnalités, telles que le montage de supports de stockage distants, les notifications de mises à jour ou l'installation d'applications tierces ne fonctionneront pas. L'accès aux fichiers à distance, ainsi que les notifications par courriel peuvent aussi être indisponibles. Il est recommandé d'activer la connexion internet pour ce serveur si vous souhaitez disposer de l'ensemble des fonctionnalités offertes.", "The reverse proxy headers configuration is incorrect, or you are accessing ownCloud from a trusted proxy. If you are not accessing ownCloud from a trusted proxy, this is a security issue and can allow an attacker to spoof their IP address as visible to ownCloud. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "La configuration des entêtes du proxy inverse est incorrecte, ou vous accédez à ownCloud depuis un proxy de confiance. Si vous n'êtes pas en train d’accéder à ownCloud depuis un proxy de confiance, ceci est un problème de sécurité qui peut permettre à un attaquant de masquer sa véritable adresse IP. Consultez la <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a> pour avoir plus d'informations à ce sujet.", "Allow editing" : "Permettre la modification", "Hide file listing" : "Cacher la liste des fichiers", @@ -358,7 +358,7 @@ OC.L10N.register( "Share with users or remote users…" : "Partager avec des utilisateurs ou des utilisateurs distants...", "Warning" : "Attention", "Error while sending notification" : "Erreur lors de l'envoi de la notification", - "The upgrade is in progress, leaving this page might interrupt the process in some environments." : "La mise à jour est en cours, quitter la page peux interrompre le processus dans beaucoup d'environnements.", + "The upgrade is in progress, leaving this page might interrupt the process in some environments." : "La mise à jour est en cours, quitter la page peux interrompre le processus dans certains d'environnements.", "Updating to {version}" : "Mise à jour vers {version}", "The update was successful. There were warnings." : "La mise à jour a été faite avec succès. Il y a eu des avertissements.", "No search results in other folders" : "Aucun résultat dans d'autres dossiers", diff --git a/core/l10n/fr.json b/core/l10n/fr.json index 64db993fa58..4933a1993fb 100644 --- a/core/l10n/fr.json +++ b/core/l10n/fr.json @@ -87,25 +87,25 @@ "Strong password" : "Mot de passe fort", "Your web server is not yet set up properly to allow file synchronization because the WebDAV interface seems to be broken." : "Votre serveur web n'est pas correctement configuré pour la synchronisation de fichiers : l'interface WebDAV semble ne pas fonctionner.", "Your web server is not set up properly to resolve \"{url}\". Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "La configuration du serveur web ne permet pas d'atteindre \"{url}\". Consultez la <a target=\"_blank\" ref=\"noreferrer\" href=\"{docLink}\">documentation</a> pour avoir plus d'informations à ce sujet.", - "This server has no working Internet connection: Multiple endpoints could not be reached. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "Ce serveur ne peut se connecter à Internet : plusieurs point finaux ne peuvent être atteins. Cela signifie que certaines fonctionnalités, telles que le montage de supports de stockage distants, les notifications de mises à jour ou l'installation d'applications tierces ne fonctionneront pas. L'accès aux fichiers à distance, ainsi que l'envoie de notifications par mail peuvent aussi être indisponibles. Il est recommandé d'activer la connexion internet pour ce serveur si vous souhaitez disposer de l'ensemble des fonctionnalités offertes.", - "No memory cache has been configured. To enhance your performance please configure a memcache if available. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "Aucun cache mémoire n'est configuré. Si possible, configurez un \"memcache\" pour augmenter les performances. Pour plus d'information consultez la <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>.", + "This server has no working Internet connection: Multiple endpoints could not be reached. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "Ce serveur ne peut se connecter à Internet : plusieurs point finaux ne peuvent être atteints. Cela signifie que certaines fonctionnalités, telles que le montage de supports de stockage distants, les notifications de mises à jour ou l'installation d'applications tierces ne fonctionneront pas. L'accès aux fichiers à distance, ainsi que l'envoi de notifications par mail peuvent aussi être indisponibles. Il est recommandé d'activer la connexion internet pour ce serveur si vous souhaitez disposer de l'ensemble des fonctionnalités offertes.", + "No memory cache has been configured. To enhance your performance please configure a memcache if available. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "Aucun cache mémoire n'est configuré. Si possible, configurez un \"memcache\" pour améliorer les performances. Pour plus d'informations consultez la <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>.", "/dev/urandom is not readable by PHP which is highly discouraged for security reasons. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "/dev/urandom n'est pas lisible par PHP, ce qui est fortement déconseillé pour des raisons de sécurité. Consultez la <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a> pour avoir plus d'informations à ce sujet.", - "You are currently running PHP {version}. We encourage you to upgrade your PHP version to take advantage of <a target=\"_blank\" rel=\"noreferrer\" href=\"{phpLink}\">performance and security updates provided by the PHP Group</a> as soon as your distribution supports it." : "Vous utilisez actuellement PHP {version}. Nous vous encourageons à mettre à jour votre version de PHP afin de tirer avantage des amélioration liées à <a target=\"_blank\" rel=\"noreferrer\" href=\"{phpLink}\">la performance et la sécurité fournies par le PHP Group</a>, dès que votre distribution le supportera.", + "You are currently running PHP {version}. We encourage you to upgrade your PHP version to take advantage of <a target=\"_blank\" rel=\"noreferrer\" href=\"{phpLink}\">performance and security updates provided by the PHP Group</a> as soon as your distribution supports it." : "Vous utilisez actuellement PHP {version}. Nous vous encourageons à mettre à jour votre version de PHP afin de tirer avantage des améliorations liées à <a target=\"_blank\" rel=\"noreferrer\" href=\"{phpLink}\">la performance et la sécurité fournies par le PHP Group</a>, dès que votre distribution le supportera.", "The reverse proxy headers configuration is incorrect, or you are accessing Nextcloud from a trusted proxy. If you are not accessing Nextcloud from a trusted proxy, this is a security issue and can allow an attacker to spoof their IP address as visible to Nextcloud. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "La configuration des entêtes du proxy inverse est incorrecte, ou vous accédez à Nextcloud depuis un proxy de confiance. Si vous n'êtes pas en train d’accéder à Nextcloud depuis un proxy de confiance, ceci est un problème de sécurité qui peut permettre à un attaquant d'usurper l'adresse IP affichée à Nextcloud. Consultez la <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a> pour avoir plus d'informations à ce sujet.", - "Memcached is configured as distributed cache, but the wrong PHP module \"memcache\" is installed. \\OC\\Memcache\\Memcached only supports \"memcached\" and not \"memcache\". See the <a target=\"_blank\" rel=\"noreferrer\" href=\"{wikiLink}\">memcached wiki about both modules</a>." : "\"memcached\" est configuré comme cache distribué, mais le module installé est \"memcache\". \\OC\\Memcache\\Memcached ne prend en charge que \"memcached\" et non \"memcache\". <a target=\"_blank\" rel=\"noreferrer\" href=\"{wikiLink}\">Consulter le wiki de memcached à propos de ces deux modules.</a>", - "Some files have not passed the integrity check. Further information on how to resolve this issue can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>. (<a href=\"{codeIntegrityDownloadEndpoint}\">List of invalid files…</a> / <a href=\"{rescanEndpoint}\">Rescan…</a>)" : "Des fichiers n'ont pas réussi à passer la vérification d’intégrité. Plus d'information sur comment résoudre ce problème dans notre <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>. (<a href=\"{codeIntegrityDownloadEndpoint}\">Liste des fichiers invalides…</a> / <a href=\"{rescanEndpoint}\">Relancer…</a>)", + "Memcached is configured as distributed cache, but the wrong PHP module \"memcache\" is installed. \\OC\\Memcache\\Memcached only supports \"memcached\" and not \"memcache\". See the <a target=\"_blank\" rel=\"noreferrer\" href=\"{wikiLink}\">memcached wiki about both modules</a>." : "\"memcached\" est configuré comme cache distribué, mais le mauvais module PHP est installé. \\OC\\Memcache\\Memcached ne prend en charge que \"memcached\" et non \"memcache\". <a target=\"_blank\" rel=\"noreferrer\" href=\"{wikiLink}\">Consulter le wiki de memcached à propos de ces deux modules.</a>", + "Some files have not passed the integrity check. Further information on how to resolve this issue can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>. (<a href=\"{codeIntegrityDownloadEndpoint}\">List of invalid files…</a> / <a href=\"{rescanEndpoint}\">Rescan…</a>)" : "Des fichiers n'ont pas passé la vérification d’intégrité. Plus d'information sur comment résoudre ce problème dans notre <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>. (<a href=\"{codeIntegrityDownloadEndpoint}\">Liste des fichiers invalides…</a> / <a href=\"{rescanEndpoint}\">Relancer…</a>)", "Error occurred while checking server setup" : "Une erreur s'est produite lors de la vérification de la configuration du serveur", "Your data directory and your files are probably accessible from the Internet. The .htaccess file is not working. We strongly suggest that you configure your web server in a way that the data directory is no longer accessible or you move the data directory outside the web server document root." : "Votre dossier de données et vos fichiers sont probablement accessibles depuis internet. Le fichier .htaccess ne fonctionne pas. Nous vous recommandons vivement de configurer votre serveur web de façon à ce que ce dossier de données ne soit plus accessible, ou de le déplacer hors de la racine du serveur web.", "The \"{header}\" HTTP header is not configured to equal to \"{expected}\". This is a potential security or privacy risk and we recommend adjusting this setting." : "L'en-tête HTTP \"{header}\" n'est pas configurée pour être égale à \"{expected}\" créant potentiellement un risque relié à la sécurité et à la vie privée. Il est recommandé d'ajuster ce paramètre.", - "The \"Strict-Transport-Security\" HTTP header is not configured to at least \"{seconds}\" seconds. For enhanced security we recommend enabling HSTS as described in our <a href=\"{docUrl}\" rel=\"noreferrer\">security tips</a>." : "L'en-tête HTTP \"Strict-Transport-Security\" n'est pas configurée à \"{seconds}\" secondes. Pour renforcer la sécurité nous recommandons d'activer HSTS comme décrit dans notre <a href=\"{docUrl} rel=\"noreferrer\">Guide pour le renforcement et la sécurité</a>.", - "You are accessing this site via HTTP. We strongly suggest you configure your server to require using HTTPS instead as described in our <a href=\"{docUrl}\">security tips</a>." : "Vous accédez à ce site via HTTP. Nous vous recommandons fortement de configurer votre serveur pour forcer l'utilisation de HTTPS, comme expliqué dans notre <a href=\"{docUrl}\">Guide pour le renforcement et la sécurité</a>.", + "The \"Strict-Transport-Security\" HTTP header is not configured to at least \"{seconds}\" seconds. For enhanced security we recommend enabling HSTS as described in our <a href=\"{docUrl}\" rel=\"noreferrer\">security tips</a>." : "L'en-tête HTTP \"Strict-Transport-Security\" n'est pas configurée à au moins \"{seconds}\" secondes. Pour renforcer la sécurité nous recommandons d'activer HSTS comme décrit dans nos <a href=\"{docUrl} rel=\"noreferrer\">conseils de sécurisation</a>.", + "You are accessing this site via HTTP. We strongly suggest you configure your server to require using HTTPS instead as described in our <a href=\"{docUrl}\">security tips</a>." : "Vous accédez à ce site via HTTP. Nous vous recommandons fortement de configurer votre serveur pour forcer l'utilisation de HTTPS, comme expliqué dans nos <a href=\"{docUrl}\">conseils de sécurisation</a>.", "Shared" : "Partagé", "Shared with {recipients}" : "Partagé avec {recipients}", "Error" : "Erreur", "Error while sharing" : "Erreur lors de la mise en partage", "Error while unsharing" : "Erreur lors de l'annulation du partage", - "Error setting expiration date" : "Erreur lors de la spécification de la date d'expiration", - "The public link will expire no later than {days} days after it is created" : "Ce lien public expirera au plus tard {days} jours après sa création.", + "Error setting expiration date" : "Erreur lors de la configuration de la date d'expiration", + "The public link will expire no later than {days} days after it is created" : "Ce lien public expirera dans {days} jours après sa création.", "Set expiration date" : "Spécifier une date d'expiration", "Expiration" : "Expiration", "Expiration date" : "Date d'expiration", @@ -120,7 +120,7 @@ "Link" : "Lien", "Password protect" : "Protéger par un mot de passe", "Allow upload and editing" : "Autoriser l'envoi et l'édition", - "File drop (upload only)" : "Dépôt de fichier (uniquement pour le téléversement)", + "File drop (upload only)" : "Dépôt de fichier (téléversement uniquement)", "Email link to person" : "Envoyer le lien par courriel", "Send" : "Envoyer", "Shared with you and the group {group} by {owner}" : "Partagé avec vous et le groupe {group} par {owner}", @@ -128,7 +128,7 @@ "{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} a partagé via un lien", "group" : "groupe", "remote" : "distant", - "email" : "Adresse e-mail", + "email" : "Adresse de courriel", "Unshare" : "Ne plus partager", "can reshare" : "peut repartager", "can edit" : "peut modifier", @@ -145,14 +145,14 @@ "{sharee} (remote)" : "{sharee} (distant)", "{sharee} (email)" : "{sharee} (email)", "Share" : "Partager", - "Share with people on other servers using their Federated Cloud ID username@example.com/nextcloud" : "Partager avec des personnes sur d'autres serveurs en utilisant leur identifiant du Cloud Fédéré utilisateur@exemple.com/nextcloud", - "Share with users or by mail..." : "Partager avec des utilisateurs ou par mail…", + "Share with people on other servers using their Federated Cloud ID username@example.com/nextcloud" : "Partager avec des personnes sur d'autres serveurs en utilisant leur identifiant du Cloud Fédéré (utilisateur@exemple.com/nextcloud)", + "Share with users or by mail..." : "Partager avec des utilisateurs ou par courriel…", "Share with users or remote users..." : "Partager avec des utilisateurs ou des utilisateurs distants...", - "Share with users, remote users or by mail..." : "Partager avec des utilisateurs, des utilisateurs distants ou par mail…", + "Share with users, remote users or by mail..." : "Partager avec des utilisateurs, des utilisateurs distants ou par courriel…", "Share with users or groups..." : "Partager avec des utilisateurs ou des groupes...", - "Share with users, groups or by mail..." : "Partager avec des utilisateurs, des groupes ou par mail…", + "Share with users, groups or by mail..." : "Partager avec des utilisateurs, des groupes ou par courriel…", "Share with users, groups or remote users..." : "Partager avec des utilisateurs, groupes ou utilisateurs distants...", - "Share with users, groups, remote users or by mail..." : "Partager avec des utilisateurs, des groupes, des utilisateurs distants ou par mail…", + "Share with users, groups, remote users or by mail..." : "Partager avec des utilisateurs, des groupes, des utilisateurs distants ou par courriel…", "Share with users..." : "Partager avec des utilisateurs...", "Error removing share" : "Erreur lors de l'arrêt du partage", "Non-existing tag #{tag}" : "Étiquette #{tag} inexistante", @@ -164,7 +164,7 @@ "Collaborative tags" : "Étiquettes collaboratives ", "No tags found" : "Aucune étiquette n'a été trouvée", "The object type is not specified." : "Le type d'objet n'est pas spécifié.", - "Enter new" : "Saisir un nouveau", + "Enter new" : "Nouvelle étiquette", "Add" : "Ajouter", "Edit tags" : "Modifier les étiquettes", "Error loading dialog template: {error}" : "Erreur lors du chargement du modèle de dialogue : {error}", @@ -226,13 +226,13 @@ "Database user" : "Utilisateur de la base de données", "Database password" : "Mot de passe de la base de données", "Database name" : "Nom de la base de données", - "Database tablespace" : "Tablespace de la base de données", + "Database tablespace" : "Espace de stockage de la base de données", "Database host" : "Hôte de la base de données", "Please specify the port number along with the host name (e.g., localhost:5432)." : "Veuillez spécifier le numéro du port avec le nom de l'hôte (ex: localhost:5432).", "Performance warning" : "Avertissement à propos des performances", "SQLite will be used as database." : "SQLite sera utilisé comme gestionnaire de base de données.", "For larger installations we recommend to choose a different database backend." : "Pour des installations plus volumineuses, nous vous conseillons d'utiliser un autre gestionnaire de base de données.", - "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "En particulier si vous utilisez le client de bureau pour synchroniser vos données : l'utilisation de SQLite est alors déconseillée.", + "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "En particulier si vous utilisez le client de bureau pour synchroniser vos données, l'utilisation de SQLite est déconseillée.", "Finish setup" : "Terminer l'installation", "Finishing …" : "Finalisation …", "Need help?" : "Besoin d'aide ?", @@ -246,7 +246,7 @@ "Please contact your administrator." : "Veuillez contacter votre administrateur.", "An internal error occurred." : "Une erreur interne est survenue.", "Please try again or contact your administrator." : "Veuillez réessayer ou contactez votre administrateur.", - "Username or email" : "Nom d'utilisateur ou e-mail", + "Username or email" : "Nom d'utilisateur ou adresse de courriel", "Wrong password. Reset it?" : "Mot de passe incorrect. Réinitialiser ?", "Wrong password." : "Mot de passe incorrect.", "Log in" : "Se connecter", @@ -280,7 +280,7 @@ "To avoid timeouts with larger installations, you can instead run the following command from your installation directory:" : "Afin d'éviter les timeouts avec les installations de volume conséquent, vous pouvez exécuter la commande suivante depuis le répertoire d'installation :", "Detailed logs" : "Journaux détaillés", "Update needed" : "Mise à jour nécessaire", - "Please use the command line updater because you have a big instance." : "Veuillez utiliser la mise à jour en ligne de commande, votre instance est trop volumineuse.", + "Please use the command line updater because you have a big instance." : "Veuillez utiliser la mise à jour en ligne de commande car votre instance est volumineuse.", "For help, see the <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">documentation</a>." : "Pour obtenir de l'aide, lisez la <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">documentation</a>.", "This %s instance is currently in maintenance mode, which may take a while." : "Cette instance de %s est en cours de maintenance, cela peut prendre du temps.", "This page will refresh itself when the %s instance is available again." : "Cette page se rafraîchira d'elle-même lorsque l'instance %s sera à nouveau disponible.", @@ -337,7 +337,7 @@ "Oct." : "Oct.", "Nov." : "Nov.", "Dec." : "Déc.", - "This server has no working Internet connection. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "Ce serveur ne peut se connecter à internet. Cela signifie que certaines fonctionnalités, telles que le montage de supports de stockage distants, les notifications de mises à jour ou l'installation d'applications tierces ne fonctionneront pas. L'accès aux fichiers à distance, ainsi que les notifications par mail peuvent aussi être indisponibles. Il est recommandé d'activer la connexion internet pour ce serveur si vous souhaitez disposer de l'ensemble des fonctionnalités offertes.", + "This server has no working Internet connection. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "Ce serveur ne peut se connecter à internet. Cela signifie que certaines fonctionnalités, telles que le montage de supports de stockage distants, les notifications de mises à jour ou l'installation d'applications tierces ne fonctionneront pas. L'accès aux fichiers à distance, ainsi que les notifications par courriel peuvent aussi être indisponibles. Il est recommandé d'activer la connexion internet pour ce serveur si vous souhaitez disposer de l'ensemble des fonctionnalités offertes.", "The reverse proxy headers configuration is incorrect, or you are accessing ownCloud from a trusted proxy. If you are not accessing ownCloud from a trusted proxy, this is a security issue and can allow an attacker to spoof their IP address as visible to ownCloud. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "La configuration des entêtes du proxy inverse est incorrecte, ou vous accédez à ownCloud depuis un proxy de confiance. Si vous n'êtes pas en train d’accéder à ownCloud depuis un proxy de confiance, ceci est un problème de sécurité qui peut permettre à un attaquant de masquer sa véritable adresse IP. Consultez la <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a> pour avoir plus d'informations à ce sujet.", "Allow editing" : "Permettre la modification", "Hide file listing" : "Cacher la liste des fichiers", @@ -356,7 +356,7 @@ "Share with users or remote users…" : "Partager avec des utilisateurs ou des utilisateurs distants...", "Warning" : "Attention", "Error while sending notification" : "Erreur lors de l'envoi de la notification", - "The upgrade is in progress, leaving this page might interrupt the process in some environments." : "La mise à jour est en cours, quitter la page peux interrompre le processus dans beaucoup d'environnements.", + "The upgrade is in progress, leaving this page might interrupt the process in some environments." : "La mise à jour est en cours, quitter la page peux interrompre le processus dans certains d'environnements.", "Updating to {version}" : "Mise à jour vers {version}", "The update was successful. There were warnings." : "La mise à jour a été faite avec succès. Il y a eu des avertissements.", "No search results in other folders" : "Aucun résultat dans d'autres dossiers", diff --git a/core/l10n/ru.js b/core/l10n/ru.js index b0f9243447d..79f75a564d6 100644 --- a/core/l10n/ru.js +++ b/core/l10n/ru.js @@ -71,7 +71,7 @@ OC.L10N.register( "Ok" : "Ок", "Error loading message template: {error}" : "Ошибка загрузки шаблона сообщений: {error}", "read-only" : "только для чтения", - "_{count} file conflict_::_{count} file conflicts_" : ["{count} конфликт в файлах","{count} конфликта в файлах","{count} конфликтов в файлах","{count} конфликтов в файлах"], + "_{count} file conflict_::_{count} file conflicts_" : ["{count} конфликт в файлах","{count} конфликта в файлах","{count} конфликтов в файлах","{count} конфликтов файлов"], "One file conflict" : "Один конфликт в файлах", "New Files" : "Новые файлы", "Already existing files" : "Существующие файлы", @@ -133,7 +133,7 @@ OC.L10N.register( "email" : "email", "Unshare" : "Закрыть доступ", "can reshare" : "можно опубликовать", - "can edit" : "может редактировать", + "can edit" : "можно редактировать", "can create" : "можно создавать", "can change" : "можно изменять", "can delete" : "можно удалять", diff --git a/core/l10n/ru.json b/core/l10n/ru.json index edcad18ba9a..5a353dc5e69 100644 --- a/core/l10n/ru.json +++ b/core/l10n/ru.json @@ -69,7 +69,7 @@ "Ok" : "Ок", "Error loading message template: {error}" : "Ошибка загрузки шаблона сообщений: {error}", "read-only" : "только для чтения", - "_{count} file conflict_::_{count} file conflicts_" : ["{count} конфликт в файлах","{count} конфликта в файлах","{count} конфликтов в файлах","{count} конфликтов в файлах"], + "_{count} file conflict_::_{count} file conflicts_" : ["{count} конфликт в файлах","{count} конфликта в файлах","{count} конфликтов в файлах","{count} конфликтов файлов"], "One file conflict" : "Один конфликт в файлах", "New Files" : "Новые файлы", "Already existing files" : "Существующие файлы", @@ -131,7 +131,7 @@ "email" : "email", "Unshare" : "Закрыть доступ", "can reshare" : "можно опубликовать", - "can edit" : "может редактировать", + "can edit" : "можно редактировать", "can create" : "можно создавать", "can change" : "можно изменять", "can delete" : "можно удалять", diff --git a/core/l10n/sv.js b/core/l10n/sv.js index f92b2923ec7..dea783f3b12 100644 --- a/core/l10n/sv.js +++ b/core/l10n/sv.js @@ -1,7 +1,7 @@ OC.L10N.register( "core", { - "Please select a file." : "Vänligen välj en fil.", + "Please select a file." : "Välj en fil.", "File is too big" : "Filen är för stor", "The selected file is not an image." : "Den valda filen är ingen bild.", "The selected file cannot be read." : "Den valda filen kan inte läsas.", @@ -16,10 +16,10 @@ OC.L10N.register( "Crop is not square" : "Beskärning är inte kvadratisk", "Couldn't reset password because the token is invalid" : "Kunde inte återställa lösenordet på grund av felaktig token", "Couldn't reset password because the token is expired" : "Lösenord kunde inte återställas eftersom \"token\" har utgått", - "Couldn't send reset email. Please make sure your username is correct." : "Kunde inte skicka återställningsmail. Vänligen kontrollera att ditt användarnamn är korrekt.", - "Could not send reset email because there is no email address for this username. Please contact your administrator." : "Kunde inte skicka återställningsmail eftersom det saknas epost-adress för denna användaren. Kontakta din administratör", + "Couldn't send reset email. Please make sure your username is correct." : "Kunde inte skicka återställningsmejl. Vänligen kontrollera att ditt användarnamn är korrekt.", + "Could not send reset email because there is no email address for this username. Please contact your administrator." : "Kunde inte skicka återställningsmejl eftersom det saknas e-postadress för denna användare. Kontakta din administratör", "%s password reset" : "%s återställ lösenord", - "Couldn't send reset email. Please contact your administrator." : "Kunde inte skicka återställningsmail. Vänligen kontakta din administratör.", + "Couldn't send reset email. Please contact your administrator." : "Kunde inte skicka återställningsmejl. Vänligen kontakta din administratör.", "Preparing update" : "Förbereder uppdatering", "[%d / %d]: %s" : "[%d / %d]: %s", "Repair warning: " : "Reperationsvarning:", @@ -27,8 +27,8 @@ OC.L10N.register( "Please use the command line updater because automatic updating is disabled in the config.php." : "Vänligen använd den kommandotolksbaserade uppdateringen då automatisk uppdatering är inaktiverat i config.php.", "[%d / %d]: Checking table %s" : "[%d / %d]: Kontrollerar tabell %s", "Turned on maintenance mode" : "Aktiverade underhållsläge", - "Turned off maintenance mode" : "Deaktiverade underhållsläge", - "Maintenance mode is kept active" : "Underhållsläget förblir aktivt", + "Turned off maintenance mode" : "Inaktivera underhållsläge", + "Maintenance mode is kept active" : "Underhållsläget förblir aktiverat", "Updating database schema" : "Uppdaterar databasschema", "Updated database" : "Uppdaterade databasen", "Checking whether the database schema can be updated (this can take a long time depending on the database size)" : "Kontrollerar om databasschema kan uppdateras (detta kan ta lång tid beroende på databasens storlek)", @@ -75,7 +75,7 @@ OC.L10N.register( "One file conflict" : "En filkonflikt", "New Files" : "Nya filer", "Already existing files" : "Filer som redan existerar", - "Which files do you want to keep?" : "Vilken fil vill du behålla?", + "Which files do you want to keep?" : "Vilka filer vill du behålla?", "If you select both versions, the copied file will have a number added to its name." : "Om du väljer båda versionerna kommer de kopierade filerna ha nummer tillagda i filnamnet.", "Continue" : "Fortsätt", "(all selected)" : "(Alla valda)", @@ -106,12 +106,12 @@ OC.L10N.register( "Error" : "Fel", "Error while sharing" : "Fel vid delning", "Error while unsharing" : "Fel när delning skulle avslutas", - "Error setting expiration date" : "Fel vid sättning av utgångsdatum", - "The public link will expire no later than {days} days after it is created" : "Den publika länken kommer sluta gälla inte senare än {days} dagar efter att den skapades", - "Set expiration date" : "Sätt utgångsdatum", + "Error setting expiration date" : "Fel vid val av utgångsdatum", + "The public link will expire no later than {days} days after it is created" : "Den offentliga länken kommer sluta gälla inte senare än {days} dagar efter att den skapades", + "Set expiration date" : "Välj utgångsdatum", "Expiration" : "Upphör", "Expiration date" : "Utgångsdatum", - "Choose a password for the public link" : "Välj ett lösenord för den publika länken", + "Choose a password for the public link" : "Välj ett lösenord för den offentliga länken", "Copied!" : "Kopierad!", "Copy" : "Kopiera", "Not supported!" : "Stöds ej!", @@ -123,14 +123,14 @@ OC.L10N.register( "Password protect" : "Lösenordsskydda", "Allow upload and editing" : "Tillåt uppladdning och redigering", "File drop (upload only)" : "Göm fillista (endast uppladdning)", - "Email link to person" : "E-posta länk till person", + "Email link to person" : "Skicka länken som e-postmeddelande", "Send" : "Skicka", "Shared with you and the group {group} by {owner}" : "Delad med dig och gruppen {group} av {owner}", "Shared with you by {owner}" : "Delad med dig av {owner}", "{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} delad via länk", "group" : "Grupp", - "remote" : "fjärr", - "email" : "epost", + "remote" : "extern", + "email" : "e-post", "Unshare" : "Sluta dela", "can reshare" : "kan dela vidare", "can edit" : "kan redigera", @@ -138,23 +138,23 @@ OC.L10N.register( "can change" : "kan ändra", "can delete" : "kan radera", "access control" : "åtkomstkontroll", - "Could not unshare" : "Kunde inte odela", + "Could not unshare" : "Kunde inte ta bort delning", "Share details could not be loaded for this item." : "Delningsdetaljer kunde inte laddas för detta objekt.", "No users or groups found for {search}" : "Inga användare eller grupper funna för {search}", "No users found for {search}" : "Inga användare funna för {search}", "An error occurred. Please try again" : "Ett fel uppstod. Vänligen försök igen", - "{sharee} (group)" : "{sharee} (group)", - "{sharee} (remote)" : "{sharee} (remote)", - "{sharee} (email)" : "{sharee} (email)", + "{sharee} (group)" : "{sharee} (grupp)", + "{sharee} (remote)" : "{sharee} (externt)", + "{sharee} (email)" : "{sharee} (e-post)", "Share" : "Dela", - "Share with people on other servers using their Federated Cloud ID username@example.com/nextcloud" : "Dela med andra personer på andra servrar genom att använda deras Federerade Cloud ID användarnamn@example.com/nextcloud", - "Share with users or by mail..." : "Dela med användare via epost...", - "Share with users or remote users..." : "Dela med användare eller fjärranvändare...", - "Share with users, remote users or by mail..." : "Dela med användare, fjärranvändare eller via epost...", + "Share with people on other servers using their Federated Cloud ID username@example.com/nextcloud" : "Dela med andra personer på andra servrar genom att använda deras Federerade Moln-ID användarnamn@example.com/nextcloud", + "Share with users or by mail..." : "Dela med användare eller via e-post...", + "Share with users or remote users..." : "Dela med användare eller externanvändare...", + "Share with users, remote users or by mail..." : "Dela med användare, externanvändare eller via e-post...", "Share with users or groups..." : "Dela med användare eller grupper...", "Share with users, groups or by mail..." : "Dela med användare, grupper eller via epost", - "Share with users, groups or remote users..." : "Dela med användare, grupper eller fjärranvändare...", - "Share with users, groups, remote users or by mail..." : "Dela med användare, grupper, fjärranvändare eller via epost...", + "Share with users, groups or remote users..." : "Dela med användare, grupper eller externanvändare...", + "Share with users, groups, remote users or by mail..." : "Dela med användare, grupper, externanvändare eller via e-post...", "Share with users..." : "Dela med användare...", "Error removing share" : "Fel uppstod när delning försökte tas bort", "Non-existing tag #{tag}" : "Icke-existerande tag #{tag}", @@ -163,13 +163,13 @@ OC.L10N.register( "({scope})" : "({scope})", "Delete" : "Radera", "Rename" : "Byt namn", - "Collaborative tags" : "Sammarbets taggar", + "Collaborative tags" : "Samverkanstaggar", "No tags found" : "Hittade inga taggar", "The object type is not specified." : "Objekttypen är inte specificerad.", "Enter new" : "Skriv nytt", "Add" : "Lägg till", "Edit tags" : "Redigera taggar", - "Error loading dialog template: {error}" : "Fel vid inläsning av dialogmall: {fel}", + "Error loading dialog template: {error}" : "Fel vid inläsning av dialogmall: {error}", "No tags selected for deletion." : "Inga taggar valda för borttagning.", "unknown text" : "okänd text", "Hello world!" : "Hej världen!", @@ -177,7 +177,7 @@ OC.L10N.register( "Hello {name}, the weather is {weather}" : "Hej {name}, vädret är {weather}", "Hello {name}" : "Hej {name}", "new" : "ny", - "_download %n file_::_download %n files_" : ["Ladda ner %n fil","Ladda ner %n filer"], + "_download %n file_::_download %n files_" : ["Ladda ner %n fil","Ladda ned %n filer"], "The update is in progress, leaving this page might interrupt the process in some environments." : "Uppdateringen pågår, om sidan lämnas kan uppdateringen misslyckas. ", "Update to {version}" : "Uppdatera till {version}", "An error occurred." : "Ett fel inträffade.", @@ -189,9 +189,9 @@ OC.L10N.register( "Searching other places" : "Söker på andra platser", "No search results in other folders for '{tag}{filter}{endtag}'" : "Inga sökresultat i andra mappar för '{tag}{filter}{endtag}'", "_{count} search result in another folder_::_{count} search results in other folders_" : ["{count} sökresultat i en annan mapp","{count} sökresultat i andra mappar"], - "Personal" : "Personligt", + "Personal" : "Personliga Inställningar", "Users" : "Användare", - "Apps" : "Program", + "Apps" : "Appar", "Admin" : "Admin", "Help" : "Hjälp", "Access forbidden" : "Åtkomst förbjuden", @@ -199,14 +199,14 @@ OC.L10N.register( "The specified document has not been found on the server." : "Det angivna dokumentet hittades inte på servern.", "You can click here to return to %s." : "Du kan klicka här för att återvända till %s.", "Hey there,\n\njust letting you know that %s shared %s with you.\nView it: %s\n\n" : "Hej där,\n\nVi vill bara meddela att %s delade %s med dig.\nTitta på den här: %s\n\n", - "The share will expire on %s." : "Utdelningen kommer att upphöra %s.", + "The share will expire on %s." : "Delningen kommer att upphöra %s.", "Cheers!" : "Ha de fint!", "Internal Server Error" : "Internt serverfel", "The server encountered an internal error and was unable to complete your request." : "Servern påträffade ett internt fel och lmisslyckades att slutföra din begäran.", "Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report." : "Vänligen kontakta serveradministratören om detta fel återkommer flera gånger, vänligen inkludera nedanstående tekniska detaljeri din felrapport.", "More details can be found in the server log." : "Mer detaljer återfinns i serverns logg.", "Technical details" : "Tekniska detaljer", - "Remote Address: %s" : "Fjärradress: %s", + "Remote Address: %s" : "Extern adress: %s", "Request ID: %s" : "Begärd ID: %s", "Type: %s" : "Typ: %s", "Code: %s" : "Kod: %s", @@ -231,14 +231,14 @@ OC.L10N.register( "Database tablespace" : "Databas tabellutrymme", "Database host" : "Databasserver", "Please specify the port number along with the host name (e.g., localhost:5432)." : "Vänligen ange portnumret tillsammans med hostnamnet (t.ex. localhost:5432).", - "Performance warning" : "Prestanda varning", + "Performance warning" : "Prestandavarning", "SQLite will be used as database." : "SQLite kommer att användas som databas", - "For larger installations we recommend to choose a different database backend." : "För större installationer rekommenderar vi at man väljer en annan databasmotor.", - "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Speciellt när desktop klienten för filsynkronisering används så avråds användande av SQLite.", - "Finish setup" : "Avsluta installation", + "For larger installations we recommend to choose a different database backend." : "För större installationer rekommenderar vi att man väljer en annan databasmotor.", + "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Speciellt när skrivbordsklienten för filsynkronisering används så avråds användande av SQLite.", + "Finish setup" : "Slutför installationen", "Finishing …" : "Avslutar ...", "Need help?" : "Behöver du hjälp?", - "See the documentation" : "Kolla dokumentationen", + "See the documentation" : "Läs dokumentationen", "This application requires JavaScript for correct operation. Please {linkstart}enable JavaScript{linkend} and reload the page." : "Denna applikationen kräver JavaScript för att fungera korrekt. Var god {linkstart}aktivera JavaScript{linkend} och ladda om sidan.", "Log out" : "Logga ut", "Search" : "Sök", @@ -249,7 +249,7 @@ OC.L10N.register( "An internal error occurred." : "Ett internt fel uppstod.", "Please try again or contact your administrator." : "Vänligen försök igen eller kontakta din administratör.", "Username or email" : "Användarnamn eller e-post", - "Wrong password. Reset it?" : "Fel lösenord. Vill du återställa?", + "Wrong password. Reset it?" : "Fel lösenord. Vill du återställa lösenordet?", "Wrong password." : "Fel lösenord.", "Log in" : "Logga in", "Stay logged in" : "Fortsätt vara inloggad.", @@ -257,19 +257,19 @@ OC.L10N.register( "Use the following link to reset your password: {link}" : "Använd följande länk för att återställa lösenordet: {link}", "New password" : "Nytt lösenord", "New Password" : "Nytt lösenord", - "Reset password" : "Återställ lösenordet", - "Hey there,<br><br>just letting you know that %s shared <strong>%s</strong> with you.<br><a href=\"%s\">View it!</a><br><br>" : "Hej där,<br><br>Tänkte bara informera dig om att %s delade <strong>%s</strong> med dig.<br><a href=\"%s\">Visa den!</a><br><br>", + "Reset password" : "Återställ lösenord", + "Hey there,<br><br>just letting you know that %s shared <strong>%s</strong> with you.<br><a href=\"%s\">View it!</a><br><br>" : "Hej där,<br><br>Tänkte bara informera dig om att %s delade <strong>%s</strong> med dig.<br><a href=\"%s\">Klicka här för att se!</a><br><br>", "This Nextcloud instance is currently in single user mode." : "Denna Nextcloud instans är för närvarande i enanvändarläge", "This means only administrators can use the instance." : "Detta betyder att endast administartörer kan använda instansen.", - "Contact your system administrator if this message persists or appeared unexpectedly." : "Hör av dig till din systemadministratör ifall detta meddelande fortsätter eller visas oväntat.", + "Contact your system administrator if this message persists or appeared unexpectedly." : "Hör av dig till din systemadministratör om detta meddelande fortsätter eller visas oväntat.", "Thank you for your patience." : "Tack för ditt tålamod.", "Two-factor authentication" : "Tvåfaktorsautentisering", - "Enhanced security is enabled for your account. Please authenticate using a second factor." : "Utökad säkerhet är aktiverat för ditt konto. Var vänlig verifiera med en andra faktor. ", + "Enhanced security is enabled for your account. Please authenticate using a second factor." : "Utökad säkerhet är aktiverat för ditt konto. Var vänlig verifiera med tvåfaktorsautentisering.", "Cancel log in" : "Avbryt inloggning", - "Use backup code" : "Använd backupkod", - "Error while validating your second factor" : "Fel vid verifiering av din andra faktor.", - "You are accessing the server from an untrusted domain." : "Du ansluter till servern från en osäker domän.", - "Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domains\" setting in config/config.php. An example configuration is provided in config/config.sample.php." : "Var god kontakta din administratör. Om du är en administratör för denna installationen konfigurera \"trusted_domains\" i inställnings i config/config.php. En exempelkonfiguration återfinns i confg/config.sample.php.", + "Use backup code" : "Använd reservkod", + "Error while validating your second factor" : "Fel vid verifiering av tvåfaktorsautentisering.", + "You are accessing the server from an untrusted domain." : "Du försöker ansluta från en icke tillåten domän.", + "Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domains\" setting in config/config.php. An example configuration is provided in config/config.sample.php." : "Var god kontakta din administratör. Om du är en administratör för denna installation så behöver du konfigurera \"trusted_domains\" i config/config.php. En exempelkonfiguration återfinns i confg/config.sample.php.", "Depending on your configuration, as an administrator you might also be able to use the button below to trust this domain." : "Beroende på din konfiguartion, så finns det möjlighet att du som administratör kan använda knappen nedan för att verifiera på denna domän.", "Add \"%s\" as trusted domain" : "Lägg till \"%s\" som en pålitlig domän", "App update required" : "Appen behöver uppdateras", @@ -278,7 +278,7 @@ OC.L10N.register( "These incompatible apps will be disabled:" : "Dessa inkompatibla appar kommer att inaktiveras", "The theme %s has been disabled." : "Temat %s har blivit inaktiverat.", "Please make sure that the database, the config folder and the data folder have been backed up before proceeding." : "Vänligen säkerställ att en säkerhetskopia har gjorts av databasen, konfigurations- och datamappen innan du fortsätter.", - "Start update" : "Starta uppdateringen", + "Start update" : "Påbörja uppdateringen", "To avoid timeouts with larger installations, you can instead run the following command from your installation directory:" : "För att undvika timeout vid större installationer kan du istället köra följande kommando från din installationskatalog:", "Detailed logs" : "Detaljerade loggar", "Update needed" : "Uppdatering krävs", @@ -286,13 +286,13 @@ OC.L10N.register( "For help, see the <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">documentation</a>." : "För hjälp, se <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">dokumentationen</a>.", "This %s instance is currently in maintenance mode, which may take a while." : "Denna %s instans befinner sig för närvarande i underhållsläge, vilket kan ta ett tag.", "This page will refresh itself when the %s instance is available again." : "Denna sida uppdaterar sig själv när %s instansen är tillgänglig igen.", - "Error loading tags" : "Fel vid inläsning utav taggar", - "Tag already exists" : "Tagg existerar redan", + "Error loading tags" : "Fel vid inläsning av taggar", + "Tag already exists" : "Taggen finns redan", "Error deleting tag(s)" : "Fel vid borttagning utav tagg(ar)", "Error tagging" : "Fel vid taggning", - "Error untagging" : "Fel vid avtaggning", + "Error untagging" : "Fel vid borttagning av tagg", "Error favoriting" : "Fel vid favorisering", - "Error unfavoriting" : "Fel vid avfavorisering ", + "Error unfavoriting" : "Fel vid borttagning av favorisering ", "Couldn't send mail to following users: %s " : "Gick inte att skicka e-post till följande användare: %s", "Sunday" : "Söndag", "Monday" : "Måndag", @@ -342,20 +342,20 @@ OC.L10N.register( "This server has no working Internet connection. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "Servern har ingen fungerande internetuppkoppling. Detta betyder att vissa funktioner så som extern lagring, notifikationer om uppdateringar eller installationer utav tredjepartsapplikationer inte kommer fungera. Åtkomst av filer utifrån och att skicka notifieringar via epost kanske inte fungerar heller. Vi föreslår att internetanslutningen aktiveras för denna server om man vill använda samtliga funktioner.", "The reverse proxy headers configuration is incorrect, or you are accessing ownCloud from a trusted proxy. If you are not accessing ownCloud from a trusted proxy, this is a security issue and can allow an attacker to spoof their IP address as visible to ownCloud. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "Konfiguration för \"reverse proxy headers\" är felaktig eller så försöker du nå Owncloud från en betrodd proxy. Om du inte försöker nå Owncloud från en betrodd proxy, detta är en säkerhetsrisk och kan möjliggöra att en hacker att förfalska sin IP adress som är synlig för Owncloud. Vidare information kan finnas i vår <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">dokumentation</a>.", "Allow editing" : "Tillåt redigering", - "Hide file listing" : "Göm fillista", + "Hide file listing" : "Dölj filer i mappen", "Sending ..." : "Skickar ...", "Email sent" : "E-post skickat", "Send link via email" : "Skicka länk via e-post", "notify by email" : "informera via e-post", "can share" : "får dela", "create" : "skapa", - "change" : "ändra", + "change" : "redigera", "delete" : "radera", - "{sharee} (at {server})" : "{sharee} (at {server})", + "{sharee} (at {server})" : "{sharee} (på {server})", "Share with users…" : "Dela med användare...", - "Share with users, groups or remote users…" : "Dela med användare, grupper och fjärranvändare...", + "Share with users, groups or remote users…" : "Dela med användare, grupper eller externanvändare...", "Share with users or groups…" : "Dela med användare eller grupper...", - "Share with users or remote users…" : "Dela med användare eller fjärranvändare...", + "Share with users or remote users…" : "Dela med användare eller externanvändare...", "Warning" : "Varning", "Error while sending notification" : "Fel när notifikation skulle skickas", "The upgrade is in progress, leaving this page might interrupt the process in some environments." : "Uppgradering pågår, att lämna denna sidan kan störa processen i vissa miljöer", diff --git a/core/l10n/sv.json b/core/l10n/sv.json index 1b07ef13113..4deb658786c 100644 --- a/core/l10n/sv.json +++ b/core/l10n/sv.json @@ -1,5 +1,5 @@ { "translations": { - "Please select a file." : "Vänligen välj en fil.", + "Please select a file." : "Välj en fil.", "File is too big" : "Filen är för stor", "The selected file is not an image." : "Den valda filen är ingen bild.", "The selected file cannot be read." : "Den valda filen kan inte läsas.", @@ -14,10 +14,10 @@ "Crop is not square" : "Beskärning är inte kvadratisk", "Couldn't reset password because the token is invalid" : "Kunde inte återställa lösenordet på grund av felaktig token", "Couldn't reset password because the token is expired" : "Lösenord kunde inte återställas eftersom \"token\" har utgått", - "Couldn't send reset email. Please make sure your username is correct." : "Kunde inte skicka återställningsmail. Vänligen kontrollera att ditt användarnamn är korrekt.", - "Could not send reset email because there is no email address for this username. Please contact your administrator." : "Kunde inte skicka återställningsmail eftersom det saknas epost-adress för denna användaren. Kontakta din administratör", + "Couldn't send reset email. Please make sure your username is correct." : "Kunde inte skicka återställningsmejl. Vänligen kontrollera att ditt användarnamn är korrekt.", + "Could not send reset email because there is no email address for this username. Please contact your administrator." : "Kunde inte skicka återställningsmejl eftersom det saknas e-postadress för denna användare. Kontakta din administratör", "%s password reset" : "%s återställ lösenord", - "Couldn't send reset email. Please contact your administrator." : "Kunde inte skicka återställningsmail. Vänligen kontakta din administratör.", + "Couldn't send reset email. Please contact your administrator." : "Kunde inte skicka återställningsmejl. Vänligen kontakta din administratör.", "Preparing update" : "Förbereder uppdatering", "[%d / %d]: %s" : "[%d / %d]: %s", "Repair warning: " : "Reperationsvarning:", @@ -25,8 +25,8 @@ "Please use the command line updater because automatic updating is disabled in the config.php." : "Vänligen använd den kommandotolksbaserade uppdateringen då automatisk uppdatering är inaktiverat i config.php.", "[%d / %d]: Checking table %s" : "[%d / %d]: Kontrollerar tabell %s", "Turned on maintenance mode" : "Aktiverade underhållsläge", - "Turned off maintenance mode" : "Deaktiverade underhållsläge", - "Maintenance mode is kept active" : "Underhållsläget förblir aktivt", + "Turned off maintenance mode" : "Inaktivera underhållsläge", + "Maintenance mode is kept active" : "Underhållsläget förblir aktiverat", "Updating database schema" : "Uppdaterar databasschema", "Updated database" : "Uppdaterade databasen", "Checking whether the database schema can be updated (this can take a long time depending on the database size)" : "Kontrollerar om databasschema kan uppdateras (detta kan ta lång tid beroende på databasens storlek)", @@ -73,7 +73,7 @@ "One file conflict" : "En filkonflikt", "New Files" : "Nya filer", "Already existing files" : "Filer som redan existerar", - "Which files do you want to keep?" : "Vilken fil vill du behålla?", + "Which files do you want to keep?" : "Vilka filer vill du behålla?", "If you select both versions, the copied file will have a number added to its name." : "Om du väljer båda versionerna kommer de kopierade filerna ha nummer tillagda i filnamnet.", "Continue" : "Fortsätt", "(all selected)" : "(Alla valda)", @@ -104,12 +104,12 @@ "Error" : "Fel", "Error while sharing" : "Fel vid delning", "Error while unsharing" : "Fel när delning skulle avslutas", - "Error setting expiration date" : "Fel vid sättning av utgångsdatum", - "The public link will expire no later than {days} days after it is created" : "Den publika länken kommer sluta gälla inte senare än {days} dagar efter att den skapades", - "Set expiration date" : "Sätt utgångsdatum", + "Error setting expiration date" : "Fel vid val av utgångsdatum", + "The public link will expire no later than {days} days after it is created" : "Den offentliga länken kommer sluta gälla inte senare än {days} dagar efter att den skapades", + "Set expiration date" : "Välj utgångsdatum", "Expiration" : "Upphör", "Expiration date" : "Utgångsdatum", - "Choose a password for the public link" : "Välj ett lösenord för den publika länken", + "Choose a password for the public link" : "Välj ett lösenord för den offentliga länken", "Copied!" : "Kopierad!", "Copy" : "Kopiera", "Not supported!" : "Stöds ej!", @@ -121,14 +121,14 @@ "Password protect" : "Lösenordsskydda", "Allow upload and editing" : "Tillåt uppladdning och redigering", "File drop (upload only)" : "Göm fillista (endast uppladdning)", - "Email link to person" : "E-posta länk till person", + "Email link to person" : "Skicka länken som e-postmeddelande", "Send" : "Skicka", "Shared with you and the group {group} by {owner}" : "Delad med dig och gruppen {group} av {owner}", "Shared with you by {owner}" : "Delad med dig av {owner}", "{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} delad via länk", "group" : "Grupp", - "remote" : "fjärr", - "email" : "epost", + "remote" : "extern", + "email" : "e-post", "Unshare" : "Sluta dela", "can reshare" : "kan dela vidare", "can edit" : "kan redigera", @@ -136,23 +136,23 @@ "can change" : "kan ändra", "can delete" : "kan radera", "access control" : "åtkomstkontroll", - "Could not unshare" : "Kunde inte odela", + "Could not unshare" : "Kunde inte ta bort delning", "Share details could not be loaded for this item." : "Delningsdetaljer kunde inte laddas för detta objekt.", "No users or groups found for {search}" : "Inga användare eller grupper funna för {search}", "No users found for {search}" : "Inga användare funna för {search}", "An error occurred. Please try again" : "Ett fel uppstod. Vänligen försök igen", - "{sharee} (group)" : "{sharee} (group)", - "{sharee} (remote)" : "{sharee} (remote)", - "{sharee} (email)" : "{sharee} (email)", + "{sharee} (group)" : "{sharee} (grupp)", + "{sharee} (remote)" : "{sharee} (externt)", + "{sharee} (email)" : "{sharee} (e-post)", "Share" : "Dela", - "Share with people on other servers using their Federated Cloud ID username@example.com/nextcloud" : "Dela med andra personer på andra servrar genom att använda deras Federerade Cloud ID användarnamn@example.com/nextcloud", - "Share with users or by mail..." : "Dela med användare via epost...", - "Share with users or remote users..." : "Dela med användare eller fjärranvändare...", - "Share with users, remote users or by mail..." : "Dela med användare, fjärranvändare eller via epost...", + "Share with people on other servers using their Federated Cloud ID username@example.com/nextcloud" : "Dela med andra personer på andra servrar genom att använda deras Federerade Moln-ID användarnamn@example.com/nextcloud", + "Share with users or by mail..." : "Dela med användare eller via e-post...", + "Share with users or remote users..." : "Dela med användare eller externanvändare...", + "Share with users, remote users or by mail..." : "Dela med användare, externanvändare eller via e-post...", "Share with users or groups..." : "Dela med användare eller grupper...", "Share with users, groups or by mail..." : "Dela med användare, grupper eller via epost", - "Share with users, groups or remote users..." : "Dela med användare, grupper eller fjärranvändare...", - "Share with users, groups, remote users or by mail..." : "Dela med användare, grupper, fjärranvändare eller via epost...", + "Share with users, groups or remote users..." : "Dela med användare, grupper eller externanvändare...", + "Share with users, groups, remote users or by mail..." : "Dela med användare, grupper, externanvändare eller via e-post...", "Share with users..." : "Dela med användare...", "Error removing share" : "Fel uppstod när delning försökte tas bort", "Non-existing tag #{tag}" : "Icke-existerande tag #{tag}", @@ -161,13 +161,13 @@ "({scope})" : "({scope})", "Delete" : "Radera", "Rename" : "Byt namn", - "Collaborative tags" : "Sammarbets taggar", + "Collaborative tags" : "Samverkanstaggar", "No tags found" : "Hittade inga taggar", "The object type is not specified." : "Objekttypen är inte specificerad.", "Enter new" : "Skriv nytt", "Add" : "Lägg till", "Edit tags" : "Redigera taggar", - "Error loading dialog template: {error}" : "Fel vid inläsning av dialogmall: {fel}", + "Error loading dialog template: {error}" : "Fel vid inläsning av dialogmall: {error}", "No tags selected for deletion." : "Inga taggar valda för borttagning.", "unknown text" : "okänd text", "Hello world!" : "Hej världen!", @@ -175,7 +175,7 @@ "Hello {name}, the weather is {weather}" : "Hej {name}, vädret är {weather}", "Hello {name}" : "Hej {name}", "new" : "ny", - "_download %n file_::_download %n files_" : ["Ladda ner %n fil","Ladda ner %n filer"], + "_download %n file_::_download %n files_" : ["Ladda ner %n fil","Ladda ned %n filer"], "The update is in progress, leaving this page might interrupt the process in some environments." : "Uppdateringen pågår, om sidan lämnas kan uppdateringen misslyckas. ", "Update to {version}" : "Uppdatera till {version}", "An error occurred." : "Ett fel inträffade.", @@ -187,9 +187,9 @@ "Searching other places" : "Söker på andra platser", "No search results in other folders for '{tag}{filter}{endtag}'" : "Inga sökresultat i andra mappar för '{tag}{filter}{endtag}'", "_{count} search result in another folder_::_{count} search results in other folders_" : ["{count} sökresultat i en annan mapp","{count} sökresultat i andra mappar"], - "Personal" : "Personligt", + "Personal" : "Personliga Inställningar", "Users" : "Användare", - "Apps" : "Program", + "Apps" : "Appar", "Admin" : "Admin", "Help" : "Hjälp", "Access forbidden" : "Åtkomst förbjuden", @@ -197,14 +197,14 @@ "The specified document has not been found on the server." : "Det angivna dokumentet hittades inte på servern.", "You can click here to return to %s." : "Du kan klicka här för att återvända till %s.", "Hey there,\n\njust letting you know that %s shared %s with you.\nView it: %s\n\n" : "Hej där,\n\nVi vill bara meddela att %s delade %s med dig.\nTitta på den här: %s\n\n", - "The share will expire on %s." : "Utdelningen kommer att upphöra %s.", + "The share will expire on %s." : "Delningen kommer att upphöra %s.", "Cheers!" : "Ha de fint!", "Internal Server Error" : "Internt serverfel", "The server encountered an internal error and was unable to complete your request." : "Servern påträffade ett internt fel och lmisslyckades att slutföra din begäran.", "Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report." : "Vänligen kontakta serveradministratören om detta fel återkommer flera gånger, vänligen inkludera nedanstående tekniska detaljeri din felrapport.", "More details can be found in the server log." : "Mer detaljer återfinns i serverns logg.", "Technical details" : "Tekniska detaljer", - "Remote Address: %s" : "Fjärradress: %s", + "Remote Address: %s" : "Extern adress: %s", "Request ID: %s" : "Begärd ID: %s", "Type: %s" : "Typ: %s", "Code: %s" : "Kod: %s", @@ -229,14 +229,14 @@ "Database tablespace" : "Databas tabellutrymme", "Database host" : "Databasserver", "Please specify the port number along with the host name (e.g., localhost:5432)." : "Vänligen ange portnumret tillsammans med hostnamnet (t.ex. localhost:5432).", - "Performance warning" : "Prestanda varning", + "Performance warning" : "Prestandavarning", "SQLite will be used as database." : "SQLite kommer att användas som databas", - "For larger installations we recommend to choose a different database backend." : "För större installationer rekommenderar vi at man väljer en annan databasmotor.", - "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Speciellt när desktop klienten för filsynkronisering används så avråds användande av SQLite.", - "Finish setup" : "Avsluta installation", + "For larger installations we recommend to choose a different database backend." : "För större installationer rekommenderar vi att man väljer en annan databasmotor.", + "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Speciellt när skrivbordsklienten för filsynkronisering används så avråds användande av SQLite.", + "Finish setup" : "Slutför installationen", "Finishing …" : "Avslutar ...", "Need help?" : "Behöver du hjälp?", - "See the documentation" : "Kolla dokumentationen", + "See the documentation" : "Läs dokumentationen", "This application requires JavaScript for correct operation. Please {linkstart}enable JavaScript{linkend} and reload the page." : "Denna applikationen kräver JavaScript för att fungera korrekt. Var god {linkstart}aktivera JavaScript{linkend} och ladda om sidan.", "Log out" : "Logga ut", "Search" : "Sök", @@ -247,7 +247,7 @@ "An internal error occurred." : "Ett internt fel uppstod.", "Please try again or contact your administrator." : "Vänligen försök igen eller kontakta din administratör.", "Username or email" : "Användarnamn eller e-post", - "Wrong password. Reset it?" : "Fel lösenord. Vill du återställa?", + "Wrong password. Reset it?" : "Fel lösenord. Vill du återställa lösenordet?", "Wrong password." : "Fel lösenord.", "Log in" : "Logga in", "Stay logged in" : "Fortsätt vara inloggad.", @@ -255,19 +255,19 @@ "Use the following link to reset your password: {link}" : "Använd följande länk för att återställa lösenordet: {link}", "New password" : "Nytt lösenord", "New Password" : "Nytt lösenord", - "Reset password" : "Återställ lösenordet", - "Hey there,<br><br>just letting you know that %s shared <strong>%s</strong> with you.<br><a href=\"%s\">View it!</a><br><br>" : "Hej där,<br><br>Tänkte bara informera dig om att %s delade <strong>%s</strong> med dig.<br><a href=\"%s\">Visa den!</a><br><br>", + "Reset password" : "Återställ lösenord", + "Hey there,<br><br>just letting you know that %s shared <strong>%s</strong> with you.<br><a href=\"%s\">View it!</a><br><br>" : "Hej där,<br><br>Tänkte bara informera dig om att %s delade <strong>%s</strong> med dig.<br><a href=\"%s\">Klicka här för att se!</a><br><br>", "This Nextcloud instance is currently in single user mode." : "Denna Nextcloud instans är för närvarande i enanvändarläge", "This means only administrators can use the instance." : "Detta betyder att endast administartörer kan använda instansen.", - "Contact your system administrator if this message persists or appeared unexpectedly." : "Hör av dig till din systemadministratör ifall detta meddelande fortsätter eller visas oväntat.", + "Contact your system administrator if this message persists or appeared unexpectedly." : "Hör av dig till din systemadministratör om detta meddelande fortsätter eller visas oväntat.", "Thank you for your patience." : "Tack för ditt tålamod.", "Two-factor authentication" : "Tvåfaktorsautentisering", - "Enhanced security is enabled for your account. Please authenticate using a second factor." : "Utökad säkerhet är aktiverat för ditt konto. Var vänlig verifiera med en andra faktor. ", + "Enhanced security is enabled for your account. Please authenticate using a second factor." : "Utökad säkerhet är aktiverat för ditt konto. Var vänlig verifiera med tvåfaktorsautentisering.", "Cancel log in" : "Avbryt inloggning", - "Use backup code" : "Använd backupkod", - "Error while validating your second factor" : "Fel vid verifiering av din andra faktor.", - "You are accessing the server from an untrusted domain." : "Du ansluter till servern från en osäker domän.", - "Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domains\" setting in config/config.php. An example configuration is provided in config/config.sample.php." : "Var god kontakta din administratör. Om du är en administratör för denna installationen konfigurera \"trusted_domains\" i inställnings i config/config.php. En exempelkonfiguration återfinns i confg/config.sample.php.", + "Use backup code" : "Använd reservkod", + "Error while validating your second factor" : "Fel vid verifiering av tvåfaktorsautentisering.", + "You are accessing the server from an untrusted domain." : "Du försöker ansluta från en icke tillåten domän.", + "Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domains\" setting in config/config.php. An example configuration is provided in config/config.sample.php." : "Var god kontakta din administratör. Om du är en administratör för denna installation så behöver du konfigurera \"trusted_domains\" i config/config.php. En exempelkonfiguration återfinns i confg/config.sample.php.", "Depending on your configuration, as an administrator you might also be able to use the button below to trust this domain." : "Beroende på din konfiguartion, så finns det möjlighet att du som administratör kan använda knappen nedan för att verifiera på denna domän.", "Add \"%s\" as trusted domain" : "Lägg till \"%s\" som en pålitlig domän", "App update required" : "Appen behöver uppdateras", @@ -276,7 +276,7 @@ "These incompatible apps will be disabled:" : "Dessa inkompatibla appar kommer att inaktiveras", "The theme %s has been disabled." : "Temat %s har blivit inaktiverat.", "Please make sure that the database, the config folder and the data folder have been backed up before proceeding." : "Vänligen säkerställ att en säkerhetskopia har gjorts av databasen, konfigurations- och datamappen innan du fortsätter.", - "Start update" : "Starta uppdateringen", + "Start update" : "Påbörja uppdateringen", "To avoid timeouts with larger installations, you can instead run the following command from your installation directory:" : "För att undvika timeout vid större installationer kan du istället köra följande kommando från din installationskatalog:", "Detailed logs" : "Detaljerade loggar", "Update needed" : "Uppdatering krävs", @@ -284,13 +284,13 @@ "For help, see the <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">documentation</a>." : "För hjälp, se <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">dokumentationen</a>.", "This %s instance is currently in maintenance mode, which may take a while." : "Denna %s instans befinner sig för närvarande i underhållsläge, vilket kan ta ett tag.", "This page will refresh itself when the %s instance is available again." : "Denna sida uppdaterar sig själv när %s instansen är tillgänglig igen.", - "Error loading tags" : "Fel vid inläsning utav taggar", - "Tag already exists" : "Tagg existerar redan", + "Error loading tags" : "Fel vid inläsning av taggar", + "Tag already exists" : "Taggen finns redan", "Error deleting tag(s)" : "Fel vid borttagning utav tagg(ar)", "Error tagging" : "Fel vid taggning", - "Error untagging" : "Fel vid avtaggning", + "Error untagging" : "Fel vid borttagning av tagg", "Error favoriting" : "Fel vid favorisering", - "Error unfavoriting" : "Fel vid avfavorisering ", + "Error unfavoriting" : "Fel vid borttagning av favorisering ", "Couldn't send mail to following users: %s " : "Gick inte att skicka e-post till följande användare: %s", "Sunday" : "Söndag", "Monday" : "Måndag", @@ -340,20 +340,20 @@ "This server has no working Internet connection. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "Servern har ingen fungerande internetuppkoppling. Detta betyder att vissa funktioner så som extern lagring, notifikationer om uppdateringar eller installationer utav tredjepartsapplikationer inte kommer fungera. Åtkomst av filer utifrån och att skicka notifieringar via epost kanske inte fungerar heller. Vi föreslår att internetanslutningen aktiveras för denna server om man vill använda samtliga funktioner.", "The reverse proxy headers configuration is incorrect, or you are accessing ownCloud from a trusted proxy. If you are not accessing ownCloud from a trusted proxy, this is a security issue and can allow an attacker to spoof their IP address as visible to ownCloud. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "Konfiguration för \"reverse proxy headers\" är felaktig eller så försöker du nå Owncloud från en betrodd proxy. Om du inte försöker nå Owncloud från en betrodd proxy, detta är en säkerhetsrisk och kan möjliggöra att en hacker att förfalska sin IP adress som är synlig för Owncloud. Vidare information kan finnas i vår <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">dokumentation</a>.", "Allow editing" : "Tillåt redigering", - "Hide file listing" : "Göm fillista", + "Hide file listing" : "Dölj filer i mappen", "Sending ..." : "Skickar ...", "Email sent" : "E-post skickat", "Send link via email" : "Skicka länk via e-post", "notify by email" : "informera via e-post", "can share" : "får dela", "create" : "skapa", - "change" : "ändra", + "change" : "redigera", "delete" : "radera", - "{sharee} (at {server})" : "{sharee} (at {server})", + "{sharee} (at {server})" : "{sharee} (på {server})", "Share with users…" : "Dela med användare...", - "Share with users, groups or remote users…" : "Dela med användare, grupper och fjärranvändare...", + "Share with users, groups or remote users…" : "Dela med användare, grupper eller externanvändare...", "Share with users or groups…" : "Dela med användare eller grupper...", - "Share with users or remote users…" : "Dela med användare eller fjärranvändare...", + "Share with users or remote users…" : "Dela med användare eller externanvändare...", "Warning" : "Varning", "Error while sending notification" : "Fel när notifikation skulle skickas", "The upgrade is in progress, leaving this page might interrupt the process in some environments." : "Uppgradering pågår, att lämna denna sidan kan störa processen i vissa miljöer", diff --git a/core/l10n/zh_CN.js b/core/l10n/zh_CN.js index ac362ea3e8d..1e3c8e717bb 100644 --- a/core/l10n/zh_CN.js +++ b/core/l10n/zh_CN.js @@ -3,6 +3,8 @@ OC.L10N.register( { "Please select a file." : "请选择一个文件", "File is too big" : "文件太大", + "The selected file is not an image." : "所选文件不是一张图片", + "The selected file cannot be read." : "无法读取所选文件", "Invalid file provided" : "提供了无效文件", "No image or file provided" : "没有提供图片或文件", "Unknown filetype" : "未知的文件类型", @@ -45,18 +47,25 @@ OC.L10N.register( "Already up to date" : "已经是最新", "<a href=\"{docUrl}\">There were problems with the code integrity check. More information…</a>" : "<a href=\"{docUrl}\">代码完整性检查出现异常,点击查看详细信息...</a>", "Settings" : "设置", + "Connection to server lost" : "与服务器的连接断开", "Problem loading page, reloading in 5 seconds" : "加载页面出现问题,在 5 秒内重新载入", "Saving..." : "保存中...", "Dismiss" : "忽略", + "This action requires you to confirm your password" : "请您确认您的密码", + "Authentication required" : "授权请求", "Password" : "密码", "Cancel" : "取消", + "Confirm" : "确认", + "Failed to authenticate, try again" : "授权失败,请重试", "seconds ago" : "几秒前", + "Logging in …" : "正在登陆...", "The link to reset your password has been sent to your email. If you do not receive it within a reasonable amount of time, check your spam/junk folders.<br>If it is not there ask your local administrator." : "密码重置邮件已经发送到您的电子邮箱中。如果您长时间没能收到邮件,请检查您的垃圾/广告邮件箱。<br>如果未能收到邮件请联系管理员。", "Your files are encrypted. If you haven't enabled the recovery key, there will be no way to get your data back after your password is reset.<br />If you are not sure what to do, please contact your administrator before you continue. <br />Do you really want to continue?" : "您的文件已被加密。如果您没有启用恢复密钥,密码重置后您将无法取回您的文件。<br />在继续之前,如果有疑问请联系您的管理员。<br />确认继续?", "I know what I'm doing" : "我知道我在做什么", "Password can not be changed. Please contact your administrator." : "无法修改密码,请联系管理员。", "No" : "否", "Yes" : "是", + "No files in here" : "未找到文件", "Choose" : "选择", "Error loading file picker template: {error}" : "加载文件分拣模板出错: {error}", "Ok" : "确定", @@ -72,6 +81,7 @@ OC.L10N.register( "(all selected)" : "(选中全部)", "({count} selected)" : "(选择了{count}个)", "Error loading file exists template" : "加载文件存在性模板失败", + "Pending" : "挂起", "Very weak password" : "非常弱的密码", "Weak password" : "弱密码", "So-so password" : "一般强度的密码", @@ -79,9 +89,11 @@ OC.L10N.register( "Strong password" : "强密码", "Your web server is not yet set up properly to allow file synchronization because the WebDAV interface seems to be broken." : "由于 WebDAV 接口似乎被破坏,因此你的网页服务器没有正确地设置来允许文件同步。", "Your web server is not set up properly to resolve \"{url}\". Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "您的web服务器未正确设置以解析 \"{url}\"。您可以在我们的<a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">文档</a>中找到更多可用信息。", + "This server has no working Internet connection: Multiple endpoints could not be reached. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "此服务器没有有效的英特网连接:无法访问多个节点。这意味着某些功能比如加载外部存储器,更新或者安装第三方应用程序的通知程序无法工作。访问本地文件和发送通知邮件可能也不工作。我们建议您如果想使用所有功能,请启用此服务器的英特网连接服务。", "No memory cache has been configured. To enhance your performance please configure a memcache if available. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "内存缓存未配置。如果可用,请配置 memcache 来增强性能。更多信息请查看我们的<a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">文档</a> 。", "/dev/urandom is not readable by PHP which is highly discouraged for security reasons. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "/dev/urandom 无法被 PHP 读取,出于安全原因,这是强烈不推荐的。请查看<a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">文档</a>了解详情。", "You are currently running PHP {version}. We encourage you to upgrade your PHP version to take advantage of <a target=\"_blank\" rel=\"noreferrer\" href=\"{phpLink}\">performance and security updates provided by the PHP Group</a> as soon as your distribution supports it." : "你的 PHP 版本 ({version}) 不再被 <a target=\"_blank\" rel=\"noreferrer\" href=\"{phpLink}\"> PHP </a>支持。我们建议您升级您的PHP版本,以便获得 PHP 性能和安全提升。", + "The reverse proxy headers configuration is incorrect, or you are accessing Nextcloud from a trusted proxy. If you are not accessing Nextcloud from a trusted proxy, this is a security issue and can allow an attacker to spoof their IP address as visible to Nextcloud. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "方向代理配置不正确,或者您正在通过可信的代理访问 Nextcloud 。如果您不是通过可信代理访问 Nextcloud ,这将会是一个安全问题,将允许攻击者通过伪装IP地址访问 Nextcloud。要获得更进一步的信息,请访问 <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">文档</a>.", "Memcached is configured as distributed cache, but the wrong PHP module \"memcache\" is installed. \\OC\\Memcache\\Memcached only supports \"memcached\" and not \"memcache\". See the <a target=\"_blank\" rel=\"noreferrer\" href=\"{wikiLink}\">memcached wiki about both modules</a>." : "Memcached 配置为分布式缓存,但是已经安装的 PHP 模块是 \"memcache\" 。 \\OC\\Memcache\\Memcached 仅支持 \"memcached\" 而不是 \"memcache\"。点击 <a target=\"_blank\" rel=\"noreferrer\" href=\"{wikiLink}\"> memcached wiki 了解两个模块的不同</a>.", "Some files have not passed the integrity check. Further information on how to resolve this issue can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>. (<a href=\"{codeIntegrityDownloadEndpoint}\">List of invalid files…</a> / <a href=\"{rescanEndpoint}\">Rescan…</a>)" : "一些文件没有通过完整性检查。如何解决此问题的详细信息可以查看我们的 <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">文档</a>. (<a href=\"{codeIntegrityDownloadEndpoint}\">无效文件列表…</a> / <a href=\"{rescanEndpoint}\">重新扫描…</a>)", "Error occurred while checking server setup" : "当检查服务器启动时出错", @@ -100,18 +112,31 @@ OC.L10N.register( "Expiration" : "过期", "Expiration date" : "过期日期", "Choose a password for the public link" : "为共享链接设置密码", + "Copied!" : "已经复制!", + "Copy" : "复制", + "Not supported!" : "不被支持!", + "Press ⌘-C to copy." : "按 ⌘ + C 进行复制。", + "Press Ctrl-C to copy." : "按 Ctrl + C 进行复制。", "Resharing is not allowed" : "不允许二次共享", "Share link" : "分享链接", "Link" : "链接", "Password protect" : "密码保护", + "Allow upload and editing" : "允许上传和编辑", + "File drop (upload only)" : "文件访问(只允许上传)", "Email link to person" : "发送链接到个人", "Send" : "发送", "Shared with you and the group {group} by {owner}" : "{owner} 共享给您及 {group} 组", "Shared with you by {owner}" : "{owner} 与您共享", + "{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} 通过链接分享", "group" : "群组", "remote" : "远程", + "email" : "邮件", "Unshare" : "取消共享", + "can reshare" : "允许重新分享", "can edit" : "可以修改", + "can create" : "允许创建", + "can change" : "允许改变", + "can delete" : "允许删除", "access control" : "访问控制", "Could not unshare" : "无法共享", "Share details could not be loaded for this item." : "无法加载这个项目的分享详情", @@ -120,7 +145,17 @@ OC.L10N.register( "An error occurred. Please try again" : "发生错误。请重试。", "{sharee} (group)" : "{sharee} (组)", "{sharee} (remote)" : "{sharee} (远程)", + "{sharee} (email)" : "{sharee} (邮件)", "Share" : "分享", + "Share with people on other servers using their Federated Cloud ID username@example.com/nextcloud" : "使用云ID格式与其他服务器的用户分享,如 用户名@example.com/nextcloud", + "Share with users or by mail..." : "通过邮件分享...", + "Share with users or remote users..." : "和用户或者远程用户分享...", + "Share with users, remote users or by mail..." : "和用户或者远程用户通过邮件分享...", + "Share with users or groups..." : "和用户或者组分享...", + "Share with users, groups or by mail..." : "和用户或者组通过邮件分享...", + "Share with users, groups or remote users..." : "和用户、组群或者远程用户分享...", + "Share with users, groups, remote users or by mail..." : "和用户、组群或者远程用户通过邮件分享...", + "Share with users..." : "和用户分享...", "Error removing share" : "移除分享时出错", "Non-existing tag #{tag}" : "标签 #{tag} 不存在", "restricted" : "受限", @@ -129,6 +164,7 @@ OC.L10N.register( "Delete" : "删除", "Rename" : "重命名", "Collaborative tags" : "协作标签", + "No tags found" : "标签未找到", "The object type is not specified." : "未指定对象类型。", "Enter new" : "输入新...", "Add" : "增加", @@ -142,10 +178,16 @@ OC.L10N.register( "Hello {name}" : "你好 {name}", "new" : "新建", "_download %n file_::_download %n files_" : ["下载 %n 个文件"], + "The update is in progress, leaving this page might interrupt the process in some environments." : "正在更新升级,离开当前页面可能导致某些环境中断。", + "Update to {version}" : "升级到 {version}", "An error occurred." : "发生了一个错误", "Please reload the page." : "请重新加载页面。", "The update was unsuccessful. For more information <a href=\"{url}\">check our forum post</a> covering this issue." : "更新不成功。有关此问题的更多信息请<a href=\"{url}\">查看我们的论坛帖子</a>。", + "The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/nextcloud/server/issues\" target=\"_blank\">Nextcloud community</a>." : "升级成功。请此问题报告给 <a href=\"https://github.com/nextcloud/server/issues\" target=\"_blank\">Nextcloud 社区</a>.", + "Continue to Nextcloud" : "继续访问 Nextcloud", + "The update was successful. Redirecting you to Nextcloud now." : "升级成功。将重新打开Nextcloud。", "Searching other places" : "搜索其他地方", + "No search results in other folders for '{tag}{filter}{endtag}'" : "在其他文件夹内未找到含有 '{tag}{filter}{endtag}'的结果", "_{count} search result in another folder_::_{count} search results in other folders_" : ["在其他文件夹中找到 {count} 条搜索结果"], "Personal" : "个人", "Users" : "用户", @@ -188,6 +230,7 @@ OC.L10N.register( "Database name" : "数据库名", "Database tablespace" : "数据库表空间", "Database host" : "数据库主机", + "Please specify the port number along with the host name (e.g., localhost:5432)." : "请填写主机名称和端口号(示例, localhost:5432)。", "Performance warning" : "性能警告", "SQLite will be used as database." : "SQLite 将被作为数据库使用。", "For larger installations we recommend to choose a different database backend." : "对于更大的安装,我们建议选择一个不同的数据库后端。", @@ -199,6 +242,8 @@ OC.L10N.register( "This application requires JavaScript for correct operation. Please {linkstart}enable JavaScript{linkend} and reload the page." : "对于正确的操作,该应用要求 JavaScript 。请 {linkstart} 打开 JavaScript {linkend} ,然后重新载入页面。", "Log out" : "注销", "Search" : "搜索", + "This action requires you to confirm your password:" : "需要您确认您的密码:", + "Confirm your password" : "确认您的密码", "Server side authentication failed!" : "服务端验证失败!", "Please contact your administrator." : "请联系你的管理员。", "An internal error occurred." : "发生了内部错误。", @@ -218,6 +263,11 @@ OC.L10N.register( "This means only administrators can use the instance." : "这意味着只有管理员才能在实例上操作。", "Contact your system administrator if this message persists or appeared unexpectedly." : "如果这个消息一直存在或不停出现,请联系你的系统管理员。", "Thank you for your patience." : "感谢让你久等了。", + "Two-factor authentication" : "双项认证", + "Enhanced security is enabled for your account. Please authenticate using a second factor." : "已为您的帐户启用增强的安全性。 请使用附加项目进行验证。", + "Cancel log in" : "取消登陆", + "Use backup code" : "使用备份口令", + "Error while validating your second factor" : "验证您的第二项时出错", "You are accessing the server from an untrusted domain." : "您正在访问来自不信任域名的服务器。", "Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domains\" setting in config/config.php. An example configuration is provided in config/config.sample.php." : "请联系你的系统管理员。如果你是系统管理员,配置 config/config.php 文件中参数 \"trusted_domain\" 设置。可以在 config/config.sample.php 文件中找到例子。", "Depending on your configuration, as an administrator you might also be able to use the button below to trust this domain." : "基于你的配置,作为系统管理员,你可能还能点击下面的按钮来信任这个域。", @@ -251,20 +301,20 @@ OC.L10N.register( "Thursday" : "星期四", "Friday" : "星期五", "Saturday" : "星期六", - "Sun." : "日", + "Sun." : "周日", "Mon." : "周一", "Tue." : "周二", "Wed." : "周三", "Thu." : "周四", "Fri." : "周五", "Sat." : "周六", - "Su" : "Su", - "Mo" : "Mo", - "Tu" : "Tu", - "We" : "We", - "Th" : "Th", - "Fr" : "Fr", - "Sa" : "Sa", + "Su" : "日", + "Mo" : "一", + "Tu" : "二", + "We" : "三", + "Th" : "四", + "Fr" : "五", + "Sa" : "六", "January" : "一月", "February" : "二月", "March" : "三月", @@ -289,8 +339,10 @@ OC.L10N.register( "Oct." : "十月", "Nov." : "十一月", "Dec." : "十二月", + "This server has no working Internet connection. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "此服务器没有有效的英特网连接。这意味着某些功能比如加载外部存储器,更新或者安装第三方应用程序的通知程序无法工作。访问本地文件和发送通知邮件可能也不工作。我们建议您如果想使用所有功能,请启用此服务器的英特网连接服务。", "The reverse proxy headers configuration is incorrect, or you are accessing ownCloud from a trusted proxy. If you are not accessing ownCloud from a trusted proxy, this is a security issue and can allow an attacker to spoof their IP address as visible to ownCloud. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "反向代理头配置不正确,或者您正从一个受信任的代理访问ownCloud。如果你不是通过受信任的代理访问 ownCloud,这将引发一个安全问题,可能由于 ownCloud IP 地址可见导致欺骗攻击。更多信息可以查看我们的 <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">文档</a>。", "Allow editing" : "允许编辑", + "Hide file listing" : "隐藏列出的文件", "Sending ..." : "正在发送...", "Email sent" : "邮件已发送", "Send link via email" : "通过邮件发送链接", @@ -306,6 +358,9 @@ OC.L10N.register( "Share with users or remote users…" : "与用户或远程用户分享...", "Warning" : "警告", "Error while sending notification" : "发送通知时出现错误", + "The upgrade is in progress, leaving this page might interrupt the process in some environments." : "正在更新,如果离开当前页面可能会中断当前环境。", + "Updating to {version}" : "升级到 {version}", + "The update was successful. There were warnings." : "更新成功。更新过程中出现一些警告。", "No search results in other folders" : "在其他文件夹中没有得到任何搜索结果", "Two-step verification" : "两步验证", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "您的帐户已启用增强安全性,请使用第二因子验证。", diff --git a/core/l10n/zh_CN.json b/core/l10n/zh_CN.json index aae88c6f9ba..87c7f85b3fb 100644 --- a/core/l10n/zh_CN.json +++ b/core/l10n/zh_CN.json @@ -1,6 +1,8 @@ { "translations": { "Please select a file." : "请选择一个文件", "File is too big" : "文件太大", + "The selected file is not an image." : "所选文件不是一张图片", + "The selected file cannot be read." : "无法读取所选文件", "Invalid file provided" : "提供了无效文件", "No image or file provided" : "没有提供图片或文件", "Unknown filetype" : "未知的文件类型", @@ -43,18 +45,25 @@ "Already up to date" : "已经是最新", "<a href=\"{docUrl}\">There were problems with the code integrity check. More information…</a>" : "<a href=\"{docUrl}\">代码完整性检查出现异常,点击查看详细信息...</a>", "Settings" : "设置", + "Connection to server lost" : "与服务器的连接断开", "Problem loading page, reloading in 5 seconds" : "加载页面出现问题,在 5 秒内重新载入", "Saving..." : "保存中...", "Dismiss" : "忽略", + "This action requires you to confirm your password" : "请您确认您的密码", + "Authentication required" : "授权请求", "Password" : "密码", "Cancel" : "取消", + "Confirm" : "确认", + "Failed to authenticate, try again" : "授权失败,请重试", "seconds ago" : "几秒前", + "Logging in …" : "正在登陆...", "The link to reset your password has been sent to your email. If you do not receive it within a reasonable amount of time, check your spam/junk folders.<br>If it is not there ask your local administrator." : "密码重置邮件已经发送到您的电子邮箱中。如果您长时间没能收到邮件,请检查您的垃圾/广告邮件箱。<br>如果未能收到邮件请联系管理员。", "Your files are encrypted. If you haven't enabled the recovery key, there will be no way to get your data back after your password is reset.<br />If you are not sure what to do, please contact your administrator before you continue. <br />Do you really want to continue?" : "您的文件已被加密。如果您没有启用恢复密钥,密码重置后您将无法取回您的文件。<br />在继续之前,如果有疑问请联系您的管理员。<br />确认继续?", "I know what I'm doing" : "我知道我在做什么", "Password can not be changed. Please contact your administrator." : "无法修改密码,请联系管理员。", "No" : "否", "Yes" : "是", + "No files in here" : "未找到文件", "Choose" : "选择", "Error loading file picker template: {error}" : "加载文件分拣模板出错: {error}", "Ok" : "确定", @@ -70,6 +79,7 @@ "(all selected)" : "(选中全部)", "({count} selected)" : "(选择了{count}个)", "Error loading file exists template" : "加载文件存在性模板失败", + "Pending" : "挂起", "Very weak password" : "非常弱的密码", "Weak password" : "弱密码", "So-so password" : "一般强度的密码", @@ -77,9 +87,11 @@ "Strong password" : "强密码", "Your web server is not yet set up properly to allow file synchronization because the WebDAV interface seems to be broken." : "由于 WebDAV 接口似乎被破坏,因此你的网页服务器没有正确地设置来允许文件同步。", "Your web server is not set up properly to resolve \"{url}\". Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "您的web服务器未正确设置以解析 \"{url}\"。您可以在我们的<a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">文档</a>中找到更多可用信息。", + "This server has no working Internet connection: Multiple endpoints could not be reached. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "此服务器没有有效的英特网连接:无法访问多个节点。这意味着某些功能比如加载外部存储器,更新或者安装第三方应用程序的通知程序无法工作。访问本地文件和发送通知邮件可能也不工作。我们建议您如果想使用所有功能,请启用此服务器的英特网连接服务。", "No memory cache has been configured. To enhance your performance please configure a memcache if available. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "内存缓存未配置。如果可用,请配置 memcache 来增强性能。更多信息请查看我们的<a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">文档</a> 。", "/dev/urandom is not readable by PHP which is highly discouraged for security reasons. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "/dev/urandom 无法被 PHP 读取,出于安全原因,这是强烈不推荐的。请查看<a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">文档</a>了解详情。", "You are currently running PHP {version}. We encourage you to upgrade your PHP version to take advantage of <a target=\"_blank\" rel=\"noreferrer\" href=\"{phpLink}\">performance and security updates provided by the PHP Group</a> as soon as your distribution supports it." : "你的 PHP 版本 ({version}) 不再被 <a target=\"_blank\" rel=\"noreferrer\" href=\"{phpLink}\"> PHP </a>支持。我们建议您升级您的PHP版本,以便获得 PHP 性能和安全提升。", + "The reverse proxy headers configuration is incorrect, or you are accessing Nextcloud from a trusted proxy. If you are not accessing Nextcloud from a trusted proxy, this is a security issue and can allow an attacker to spoof their IP address as visible to Nextcloud. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "方向代理配置不正确,或者您正在通过可信的代理访问 Nextcloud 。如果您不是通过可信代理访问 Nextcloud ,这将会是一个安全问题,将允许攻击者通过伪装IP地址访问 Nextcloud。要获得更进一步的信息,请访问 <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">文档</a>.", "Memcached is configured as distributed cache, but the wrong PHP module \"memcache\" is installed. \\OC\\Memcache\\Memcached only supports \"memcached\" and not \"memcache\". See the <a target=\"_blank\" rel=\"noreferrer\" href=\"{wikiLink}\">memcached wiki about both modules</a>." : "Memcached 配置为分布式缓存,但是已经安装的 PHP 模块是 \"memcache\" 。 \\OC\\Memcache\\Memcached 仅支持 \"memcached\" 而不是 \"memcache\"。点击 <a target=\"_blank\" rel=\"noreferrer\" href=\"{wikiLink}\"> memcached wiki 了解两个模块的不同</a>.", "Some files have not passed the integrity check. Further information on how to resolve this issue can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>. (<a href=\"{codeIntegrityDownloadEndpoint}\">List of invalid files…</a> / <a href=\"{rescanEndpoint}\">Rescan…</a>)" : "一些文件没有通过完整性检查。如何解决此问题的详细信息可以查看我们的 <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">文档</a>. (<a href=\"{codeIntegrityDownloadEndpoint}\">无效文件列表…</a> / <a href=\"{rescanEndpoint}\">重新扫描…</a>)", "Error occurred while checking server setup" : "当检查服务器启动时出错", @@ -98,18 +110,31 @@ "Expiration" : "过期", "Expiration date" : "过期日期", "Choose a password for the public link" : "为共享链接设置密码", + "Copied!" : "已经复制!", + "Copy" : "复制", + "Not supported!" : "不被支持!", + "Press ⌘-C to copy." : "按 ⌘ + C 进行复制。", + "Press Ctrl-C to copy." : "按 Ctrl + C 进行复制。", "Resharing is not allowed" : "不允许二次共享", "Share link" : "分享链接", "Link" : "链接", "Password protect" : "密码保护", + "Allow upload and editing" : "允许上传和编辑", + "File drop (upload only)" : "文件访问(只允许上传)", "Email link to person" : "发送链接到个人", "Send" : "发送", "Shared with you and the group {group} by {owner}" : "{owner} 共享给您及 {group} 组", "Shared with you by {owner}" : "{owner} 与您共享", + "{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} 通过链接分享", "group" : "群组", "remote" : "远程", + "email" : "邮件", "Unshare" : "取消共享", + "can reshare" : "允许重新分享", "can edit" : "可以修改", + "can create" : "允许创建", + "can change" : "允许改变", + "can delete" : "允许删除", "access control" : "访问控制", "Could not unshare" : "无法共享", "Share details could not be loaded for this item." : "无法加载这个项目的分享详情", @@ -118,7 +143,17 @@ "An error occurred. Please try again" : "发生错误。请重试。", "{sharee} (group)" : "{sharee} (组)", "{sharee} (remote)" : "{sharee} (远程)", + "{sharee} (email)" : "{sharee} (邮件)", "Share" : "分享", + "Share with people on other servers using their Federated Cloud ID username@example.com/nextcloud" : "使用云ID格式与其他服务器的用户分享,如 用户名@example.com/nextcloud", + "Share with users or by mail..." : "通过邮件分享...", + "Share with users or remote users..." : "和用户或者远程用户分享...", + "Share with users, remote users or by mail..." : "和用户或者远程用户通过邮件分享...", + "Share with users or groups..." : "和用户或者组分享...", + "Share with users, groups or by mail..." : "和用户或者组通过邮件分享...", + "Share with users, groups or remote users..." : "和用户、组群或者远程用户分享...", + "Share with users, groups, remote users or by mail..." : "和用户、组群或者远程用户通过邮件分享...", + "Share with users..." : "和用户分享...", "Error removing share" : "移除分享时出错", "Non-existing tag #{tag}" : "标签 #{tag} 不存在", "restricted" : "受限", @@ -127,6 +162,7 @@ "Delete" : "删除", "Rename" : "重命名", "Collaborative tags" : "协作标签", + "No tags found" : "标签未找到", "The object type is not specified." : "未指定对象类型。", "Enter new" : "输入新...", "Add" : "增加", @@ -140,10 +176,16 @@ "Hello {name}" : "你好 {name}", "new" : "新建", "_download %n file_::_download %n files_" : ["下载 %n 个文件"], + "The update is in progress, leaving this page might interrupt the process in some environments." : "正在更新升级,离开当前页面可能导致某些环境中断。", + "Update to {version}" : "升级到 {version}", "An error occurred." : "发生了一个错误", "Please reload the page." : "请重新加载页面。", "The update was unsuccessful. For more information <a href=\"{url}\">check our forum post</a> covering this issue." : "更新不成功。有关此问题的更多信息请<a href=\"{url}\">查看我们的论坛帖子</a>。", + "The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/nextcloud/server/issues\" target=\"_blank\">Nextcloud community</a>." : "升级成功。请此问题报告给 <a href=\"https://github.com/nextcloud/server/issues\" target=\"_blank\">Nextcloud 社区</a>.", + "Continue to Nextcloud" : "继续访问 Nextcloud", + "The update was successful. Redirecting you to Nextcloud now." : "升级成功。将重新打开Nextcloud。", "Searching other places" : "搜索其他地方", + "No search results in other folders for '{tag}{filter}{endtag}'" : "在其他文件夹内未找到含有 '{tag}{filter}{endtag}'的结果", "_{count} search result in another folder_::_{count} search results in other folders_" : ["在其他文件夹中找到 {count} 条搜索结果"], "Personal" : "个人", "Users" : "用户", @@ -186,6 +228,7 @@ "Database name" : "数据库名", "Database tablespace" : "数据库表空间", "Database host" : "数据库主机", + "Please specify the port number along with the host name (e.g., localhost:5432)." : "请填写主机名称和端口号(示例, localhost:5432)。", "Performance warning" : "性能警告", "SQLite will be used as database." : "SQLite 将被作为数据库使用。", "For larger installations we recommend to choose a different database backend." : "对于更大的安装,我们建议选择一个不同的数据库后端。", @@ -197,6 +240,8 @@ "This application requires JavaScript for correct operation. Please {linkstart}enable JavaScript{linkend} and reload the page." : "对于正确的操作,该应用要求 JavaScript 。请 {linkstart} 打开 JavaScript {linkend} ,然后重新载入页面。", "Log out" : "注销", "Search" : "搜索", + "This action requires you to confirm your password:" : "需要您确认您的密码:", + "Confirm your password" : "确认您的密码", "Server side authentication failed!" : "服务端验证失败!", "Please contact your administrator." : "请联系你的管理员。", "An internal error occurred." : "发生了内部错误。", @@ -216,6 +261,11 @@ "This means only administrators can use the instance." : "这意味着只有管理员才能在实例上操作。", "Contact your system administrator if this message persists or appeared unexpectedly." : "如果这个消息一直存在或不停出现,请联系你的系统管理员。", "Thank you for your patience." : "感谢让你久等了。", + "Two-factor authentication" : "双项认证", + "Enhanced security is enabled for your account. Please authenticate using a second factor." : "已为您的帐户启用增强的安全性。 请使用附加项目进行验证。", + "Cancel log in" : "取消登陆", + "Use backup code" : "使用备份口令", + "Error while validating your second factor" : "验证您的第二项时出错", "You are accessing the server from an untrusted domain." : "您正在访问来自不信任域名的服务器。", "Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domains\" setting in config/config.php. An example configuration is provided in config/config.sample.php." : "请联系你的系统管理员。如果你是系统管理员,配置 config/config.php 文件中参数 \"trusted_domain\" 设置。可以在 config/config.sample.php 文件中找到例子。", "Depending on your configuration, as an administrator you might also be able to use the button below to trust this domain." : "基于你的配置,作为系统管理员,你可能还能点击下面的按钮来信任这个域。", @@ -249,20 +299,20 @@ "Thursday" : "星期四", "Friday" : "星期五", "Saturday" : "星期六", - "Sun." : "日", + "Sun." : "周日", "Mon." : "周一", "Tue." : "周二", "Wed." : "周三", "Thu." : "周四", "Fri." : "周五", "Sat." : "周六", - "Su" : "Su", - "Mo" : "Mo", - "Tu" : "Tu", - "We" : "We", - "Th" : "Th", - "Fr" : "Fr", - "Sa" : "Sa", + "Su" : "日", + "Mo" : "一", + "Tu" : "二", + "We" : "三", + "Th" : "四", + "Fr" : "五", + "Sa" : "六", "January" : "一月", "February" : "二月", "March" : "三月", @@ -287,8 +337,10 @@ "Oct." : "十月", "Nov." : "十一月", "Dec." : "十二月", + "This server has no working Internet connection. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "此服务器没有有效的英特网连接。这意味着某些功能比如加载外部存储器,更新或者安装第三方应用程序的通知程序无法工作。访问本地文件和发送通知邮件可能也不工作。我们建议您如果想使用所有功能,请启用此服务器的英特网连接服务。", "The reverse proxy headers configuration is incorrect, or you are accessing ownCloud from a trusted proxy. If you are not accessing ownCloud from a trusted proxy, this is a security issue and can allow an attacker to spoof their IP address as visible to ownCloud. Further information can be found in our <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">documentation</a>." : "反向代理头配置不正确,或者您正从一个受信任的代理访问ownCloud。如果你不是通过受信任的代理访问 ownCloud,这将引发一个安全问题,可能由于 ownCloud IP 地址可见导致欺骗攻击。更多信息可以查看我们的 <a target=\"_blank\" rel=\"noreferrer\" href=\"{docLink}\">文档</a>。", "Allow editing" : "允许编辑", + "Hide file listing" : "隐藏列出的文件", "Sending ..." : "正在发送...", "Email sent" : "邮件已发送", "Send link via email" : "通过邮件发送链接", @@ -304,6 +356,9 @@ "Share with users or remote users…" : "与用户或远程用户分享...", "Warning" : "警告", "Error while sending notification" : "发送通知时出现错误", + "The upgrade is in progress, leaving this page might interrupt the process in some environments." : "正在更新,如果离开当前页面可能会中断当前环境。", + "Updating to {version}" : "升级到 {version}", + "The update was successful. There were warnings." : "更新成功。更新过程中出现一些警告。", "No search results in other folders" : "在其他文件夹中没有得到任何搜索结果", "Two-step verification" : "两步验证", "Enhanced security has been enabled for your account. Please authenticate using a second factor." : "您的帐户已启用增强安全性,请使用第二因子验证。", diff --git a/core/routes.php b/core/routes.php index 2b8080a3b7b..6f1892d19ac 100644 --- a/core/routes.php +++ b/core/routes.php @@ -55,6 +55,7 @@ $application->registerRoutes($this, [ ['name' => 'OCJS#getConfig', 'url' => '/core/js/oc.js', 'verb' => 'GET'], ['name' => 'Preview#getPreview', 'url' => '/core/preview', 'verb' => 'GET'], ['name' => 'Preview#getPreview', 'url' => '/core/preview.png', 'verb' => 'GET'], + ['name' => 'Css#getCss', 'url' => '/css/{appName}/{fileName}', 'verb' => 'GET'], ], 'ocs' => [ ['root' => '/cloud', 'name' => 'OCS#getCapabilities', 'url' => '/capabilities', 'verb' => 'GET'], diff --git a/lib/base.php b/lib/base.php index f235629120a..23eda212f03 100644 --- a/lib/base.php +++ b/lib/base.php @@ -281,6 +281,7 @@ class OC { // render error page $template = new OC_Template('', 'update.user', 'guest'); OC_Util::addScript('maintenance-check'); + OC_Util::addStyle('update'); $template->printPage(); die(); } @@ -354,6 +355,8 @@ class OC { header('Status: 503 Service Temporarily Unavailable'); header('Retry-After: 120'); + \OCP\Util::addStyle('update'); + // render error page $template = new OC_Template('', 'update.use-cli', 'guest'); $template->assign('productName', 'nextcloud'); // for now @@ -666,10 +669,6 @@ class OC { OC\Log\ErrorHandler::register($debug); } - // register the stream wrappers - stream_wrapper_register('close', 'OC\Files\Stream\Close'); - stream_wrapper_register('quota', 'OC\Files\Stream\Quota'); - \OC::$server->getEventLogger()->start('init_session', 'Initialize session'); OC_App::loadApps(array('session')); if (!self::$CLI) { @@ -748,9 +747,6 @@ class OC { self::registerCacheHooks(); self::registerFilesystemHooks(); - if ($systemConfig->getValue('enable_previews', true)) { - self::registerPreviewHooks(); - } self::registerShareHooks(); self::registerLogRotate(); self::registerEncryptionWrapper(); @@ -789,23 +785,31 @@ class OC { && !\OC::$server->getTrustedDomainHelper()->isTrustedDomain($host) && self::$server->getConfig()->getSystemValue('installed', false) ) { - header('HTTP/1.1 400 Bad Request'); - header('Status: 400 Bad Request'); + // Allow access to CSS resources + $isScssRequest = false; + if(strpos($request->getPathInfo(), '/css/') === 0) { + $isScssRequest = true; + } - \OC::$server->getLogger()->warning( + if (!$isScssRequest) { + header('HTTP/1.1 400 Bad Request'); + header('Status: 400 Bad Request'); + + \OC::$server->getLogger()->warning( 'Trusted domain error. "{remoteAddress}" tried to access using "{host}" as host.', [ 'app' => 'core', 'remoteAddress' => $request->getRemoteAddress(), 'host' => $host, ] - ); + ); - $tmpl = new OCP\Template('core', 'untrustedDomain', 'guest'); - $tmpl->assign('domain', $host); - $tmpl->printPage(); + $tmpl = new OCP\Template('core', 'untrustedDomain', 'guest'); + $tmpl->assign('domain', $host); + $tmpl->printPage(); - exit(); + exit(); + } } \OC::$server->getEventLogger()->end('boot'); } @@ -879,7 +883,7 @@ class OC { if ($systemConfig->getValue('installed', false) && $systemConfig->getValue('log_rotate_size', false) && !self::checkUpgrade(false)) { //don't try to do this before we are properly setup //use custom logfile path if defined, otherwise use default of nextcloud.log in data directory - \OCP\BackgroundJob::registerJob('OC\Log\Rotate', $systemConfig->getValue('logfile', $systemConfig->getValue('datadirectory', OC::$SERVERROOT . '/data') . '/nextcloud.log')); + \OC::$server->getJobList()->add('OC\Log\Rotate'); } } @@ -893,20 +897,6 @@ class OC { } /** - * register hooks for previews - */ - public static function registerPreviewHooks() { - OC_Hook::connect('OC_Filesystem', 'post_write', 'OC\Preview', 'post_write'); - OC_Hook::connect('OC_Filesystem', 'delete', 'OC\Preview', 'prepare_delete_files'); - OC_Hook::connect('\OCP\Versions', 'preDelete', 'OC\Preview', 'prepare_delete'); - OC_Hook::connect('\OCP\Trashbin', 'preDelete', 'OC\Preview', 'prepare_delete'); - OC_Hook::connect('OC_Filesystem', 'post_delete', 'OC\Preview', 'post_delete_files'); - OC_Hook::connect('\OCP\Versions', 'delete', 'OC\Preview', 'post_delete_versions'); - OC_Hook::connect('\OCP\Trashbin', 'delete', 'OC\Preview', 'post_delete'); - OC_Hook::connect('\OCP\Versions', 'rollback', 'OC\Preview', 'post_delete_versions'); - } - - /** * register hooks for sharing */ public static function registerShareHooks() { diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 78a3fb183df..2e5fc685e88 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -420,6 +420,7 @@ return array( 'OC\\Core\\Command\\User\\ResetPassword' => $baseDir . '/core/Command/User/ResetPassword.php', 'OC\\Core\\Command\\User\\Setting' => $baseDir . '/core/Command/User/Setting.php', 'OC\\Core\\Controller\\AvatarController' => $baseDir . '/core/Controller/AvatarController.php', + 'OC\\Core\\Controller\\CssController' => $baseDir . '/core/Controller/CssController.php', 'OC\\Core\\Controller\\LoginController' => $baseDir . '/core/Controller/LoginController.php', 'OC\\Core\\Controller\\LostController' => $baseDir . '/core/Controller/LostController.php', 'OC\\Core\\Controller\\OCJSController' => $baseDir . '/core/Controller/OCJSController.php', @@ -553,7 +554,6 @@ return array( 'OC\\Files\\Storage\\Wrapper\\PermissionsMask' => $baseDir . '/lib/private/Files/Storage/Wrapper/PermissionsMask.php', 'OC\\Files\\Storage\\Wrapper\\Quota' => $baseDir . '/lib/private/Files/Storage/Wrapper/Quota.php', 'OC\\Files\\Storage\\Wrapper\\Wrapper' => $baseDir . '/lib/private/Files/Storage/Wrapper/Wrapper.php', - 'OC\\Files\\Stream\\Close' => $baseDir . '/lib/private/Files/Stream/Close.php', 'OC\\Files\\Stream\\Encryption' => $baseDir . '/lib/private/Files/Stream/Encryption.php', 'OC\\Files\\Stream\\Quota' => $baseDir . '/lib/private/Files/Stream/Quota.php', 'OC\\Files\\Type\\Detection' => $baseDir . '/lib/private/Files/Type/Detection.php', @@ -605,7 +605,6 @@ return array( 'OC\\Log\\Syslog' => $baseDir . '/lib/private/Log/Syslog.php', 'OC\\Mail\\Mailer' => $baseDir . '/lib/private/Mail/Mailer.php', 'OC\\Mail\\Message' => $baseDir . '/lib/private/Mail/Message.php', - 'OC\\Memcache\\APC' => $baseDir . '/lib/private/Memcache/APC.php', 'OC\\Memcache\\APCu' => $baseDir . '/lib/private/Memcache/APCu.php', 'OC\\Memcache\\ArrayCache' => $baseDir . '/lib/private/Memcache/ArrayCache.php', 'OC\\Memcache\\CADTrait' => $baseDir . '/lib/private/Memcache/CADTrait.php', @@ -630,7 +629,6 @@ return array( 'OC\\OCS\\PrivateData' => $baseDir . '/lib/private/OCS/PrivateData.php', 'OC\\OCS\\Provider' => $baseDir . '/lib/private/OCS/Provider.php', 'OC\\OCS\\Result' => $baseDir . '/lib/private/OCS/Result.php', - 'OC\\Preview' => $baseDir . '/lib/private/Preview.php', 'OC\\PreviewManager' => $baseDir . '/lib/private/PreviewManager.php', 'OC\\PreviewNotAvailableException' => $baseDir . '/lib/private/PreviewNotAvailableException.php', 'OC\\Preview\\BMP' => $baseDir . '/lib/private/Preview/BMP.php', @@ -792,6 +790,7 @@ return array( 'OC\\Template\\JSResourceLocator' => $baseDir . '/lib/private/Template/JSResourceLocator.php', 'OC\\Template\\ResourceLocator' => $baseDir . '/lib/private/Template/ResourceLocator.php', 'OC\\Template\\ResourceNotFoundException' => $baseDir . '/lib/private/Template/ResourceNotFoundException.php', + 'OC\\Template\\SCSSCacher' => $baseDir . '/lib/private/Template/SCSSCacher.php', 'OC\\Template\\TemplateFileLocator' => $baseDir . '/lib/private/Template/TemplateFileLocator.php', 'OC\\URLGenerator' => $baseDir . '/lib/private/URLGenerator.php', 'OC\\Updater' => $baseDir . '/lib/private/Updater.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index cec3bd18f15..2b8233f6569 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -450,6 +450,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Core\\Command\\User\\ResetPassword' => __DIR__ . '/../../..' . '/core/Command/User/ResetPassword.php', 'OC\\Core\\Command\\User\\Setting' => __DIR__ . '/../../..' . '/core/Command/User/Setting.php', 'OC\\Core\\Controller\\AvatarController' => __DIR__ . '/../../..' . '/core/Controller/AvatarController.php', + 'OC\\Core\\Controller\\CssController' => __DIR__ . '/../../..' . '/core/Controller/CssController.php', 'OC\\Core\\Controller\\LoginController' => __DIR__ . '/../../..' . '/core/Controller/LoginController.php', 'OC\\Core\\Controller\\LostController' => __DIR__ . '/../../..' . '/core/Controller/LostController.php', 'OC\\Core\\Controller\\OCJSController' => __DIR__ . '/../../..' . '/core/Controller/OCJSController.php', @@ -583,7 +584,6 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Files\\Storage\\Wrapper\\PermissionsMask' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/PermissionsMask.php', 'OC\\Files\\Storage\\Wrapper\\Quota' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Quota.php', 'OC\\Files\\Storage\\Wrapper\\Wrapper' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Wrapper.php', - 'OC\\Files\\Stream\\Close' => __DIR__ . '/../../..' . '/lib/private/Files/Stream/Close.php', 'OC\\Files\\Stream\\Encryption' => __DIR__ . '/../../..' . '/lib/private/Files/Stream/Encryption.php', 'OC\\Files\\Stream\\Quota' => __DIR__ . '/../../..' . '/lib/private/Files/Stream/Quota.php', 'OC\\Files\\Type\\Detection' => __DIR__ . '/../../..' . '/lib/private/Files/Type/Detection.php', @@ -635,7 +635,6 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Log\\Syslog' => __DIR__ . '/../../..' . '/lib/private/Log/Syslog.php', 'OC\\Mail\\Mailer' => __DIR__ . '/../../..' . '/lib/private/Mail/Mailer.php', 'OC\\Mail\\Message' => __DIR__ . '/../../..' . '/lib/private/Mail/Message.php', - 'OC\\Memcache\\APC' => __DIR__ . '/../../..' . '/lib/private/Memcache/APC.php', 'OC\\Memcache\\APCu' => __DIR__ . '/../../..' . '/lib/private/Memcache/APCu.php', 'OC\\Memcache\\ArrayCache' => __DIR__ . '/../../..' . '/lib/private/Memcache/ArrayCache.php', 'OC\\Memcache\\CADTrait' => __DIR__ . '/../../..' . '/lib/private/Memcache/CADTrait.php', @@ -660,7 +659,6 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\OCS\\PrivateData' => __DIR__ . '/../../..' . '/lib/private/OCS/PrivateData.php', 'OC\\OCS\\Provider' => __DIR__ . '/../../..' . '/lib/private/OCS/Provider.php', 'OC\\OCS\\Result' => __DIR__ . '/../../..' . '/lib/private/OCS/Result.php', - 'OC\\Preview' => __DIR__ . '/../../..' . '/lib/private/Preview.php', 'OC\\PreviewManager' => __DIR__ . '/../../..' . '/lib/private/PreviewManager.php', 'OC\\PreviewNotAvailableException' => __DIR__ . '/../../..' . '/lib/private/PreviewNotAvailableException.php', 'OC\\Preview\\BMP' => __DIR__ . '/../../..' . '/lib/private/Preview/BMP.php', @@ -822,6 +820,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Template\\JSResourceLocator' => __DIR__ . '/../../..' . '/lib/private/Template/JSResourceLocator.php', 'OC\\Template\\ResourceLocator' => __DIR__ . '/../../..' . '/lib/private/Template/ResourceLocator.php', 'OC\\Template\\ResourceNotFoundException' => __DIR__ . '/../../..' . '/lib/private/Template/ResourceNotFoundException.php', + 'OC\\Template\\SCSSCacher' => __DIR__ . '/../../..' . '/lib/private/Template/SCSSCacher.php', 'OC\\Template\\TemplateFileLocator' => __DIR__ . '/../../..' . '/lib/private/Template/TemplateFileLocator.php', 'OC\\URLGenerator' => __DIR__ . '/../../..' . '/lib/private/URLGenerator.php', 'OC\\Updater' => __DIR__ . '/../../..' . '/lib/private/Updater.php', diff --git a/lib/l10n/sv.js b/lib/l10n/sv.js index b0b2be3be0a..e8515196205 100644 --- a/lib/l10n/sv.js +++ b/lib/l10n/sv.js @@ -5,6 +5,7 @@ OC.L10N.register( "This can usually be fixed by giving the webserver write access to the config directory" : "Detta kan vanligtvis åtgärdas genom att ge skrivrättigheter till config katalgogen", "See %s" : "Se %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Detta kan vanligtvis åtgärdas genom att %s ger webbservern skrivrättigheter till konfigurations-katalogen %s.", + "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Filerna i appen %$1s ersattes inte korrekt. Se till att det är en version som är kompatibel med servern.", "Sample configuration detected" : "Exempel-konfiguration detekterad", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Det har detekterats att exempel-konfigurationen har kopierats. Detta kan förstöra din installation och stöds ej. Vänligen läs dokumentationen innan ändringar på config.php utförs", "%1$s and %2$s" : "%1$s och %2$s", @@ -35,11 +36,14 @@ OC.L10N.register( "_%n hour ago_::_%n hours ago_" : ["%n timme sedan","%n timmar sedan"], "_%n minute ago_::_%n minutes ago_" : ["%n minut sedan","%n minuter sedan"], "seconds ago" : "sekunder sedan", + "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modulen med id: %s finns inte. Vänligen aktivera det i dina app-inställningar eller kontakta din administratör.", "File name is a reserved word" : "Filnamnet är ett reserverat ord", "File name contains at least one invalid character" : "Filnamnet innehåller minst ett ogiltigt tecken", "File name is too long" : "Filnamnet är för långt", "Dot files are not allowed" : "Dot filer är inte tillåtna", "Empty filename is not allowed" : "Tomma filnamn är inte tillåtna", + "APCu" : "APCu", + "Redis" : "Redis", "Server settings" : "Serverinställningar", "Sharing" : "Delning", "Encryption" : "Kryptering", @@ -58,10 +62,12 @@ OC.L10N.register( "PostgreSQL username and/or password not valid" : "PostgreSQL-användarnamnet och/eller lösenordet är felaktigt", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X stöds inte och %s kommer inte att fungera korrekt på denna plattform. Använd på egen risk!", "For the best results, please consider using a GNU/Linux server instead." : "För bästa resultat, överväg att använda en GNU/Linux server istället.", + "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Det verkar som om denna %s instans körs på en 32-bitars PHP miljö och open_basedir har konfigurerats i php.ini. Detta kommer att leda till problem med filer över 4 GB och är verkligen inte rekommenderat!", + "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Ta bort open_basedir i din php.ini eller byt till 64-bitars PHP.", "Set an admin username." : "Ange ett användarnamn för administratören.", "Set an admin password." : "Ange ett administratörslösenord.", "Can't create or write into the data directory %s" : "Kan inte skapa eller skriva till data-katalogen %s", - "Invalid Federated Cloud ID" : "Ogiltigt \"Federated Cloud\"-ID", + "Invalid Federated Cloud ID" : "Ogiltigt Federarat Moln-ID", "%s shared »%s« with you" : "%s delade »%s« med dig", "%s via %s" : "%s via %s", "Sharing %s failed, because the backend does not allow shares from type %i" : "Misslyckades dela ut %s då backend inte tillåter delningar från typ %i", @@ -93,7 +99,7 @@ OC.L10N.register( "Sharing %s failed, because the sharing backend for %s could not find its source" : "Delning %s misslyckades därför att delningsgränsnittet för %s inte kunde hitta sin källa", "Sharing %s failed, because the file could not be found in the file cache" : "Delning %s misslyckades därför att filen inte kunde hittas i filcachen", "Cannot increase permissions of %s" : "Kan ej öka behörigheterna för %s", - "Files can't be shared with delete permissions" : "Filerna kan ej delas med \"ta-bort behörigheter\"", + "Files can't be shared with delete permissions" : "Filerna kan ej delas med \"radera behörigheter\"", "Files can't be shared with create permissions" : "Filerna kan ej delas med \"skapa behörigheter\"", "Expiration date is in the past" : "Utgångsdatum är i det förflutna", "Cannot set expiration date more than %s days in the future" : "Kan ej välja ett utgångsdatum längre fram än %s dagar", @@ -152,7 +158,7 @@ OC.L10N.register( "Login canceled by app" : "Inloggningen avbruten av appen", "User disabled" : "Användare inaktiverad", "Help" : "Hjälp", - "Personal" : "Min profil", + "Personal" : "Personligt", "Users" : "Användare", "Admin" : "Administration", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Applikationen \"%s\" kan ej installeras eftersom informationen från appen ej kunde läsas.", @@ -172,6 +178,7 @@ OC.L10N.register( "Cannot write into \"apps\" directory" : "Kan inte skriva till \"apps\" katalogen!", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Detta kan vanligtvis åtgärdas genom att %s ger webbservern skrivrättigheter till applikationskatalogen %s eller stänga av app-butik i konfigurationsfilen.", "Cannot create \"data\" directory (%s)" : "Kan inte skapa \"data\" katalog (%s)", + "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Detta kan vanligtvis åtgärdas genom att <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\"> ge webbserver skrivåtkomst till rotkatalogen </a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Rättigheterna kan vanligtvis åtgärdas genom att %s ger webbservern skrivrättigheter till rootkatalogen %s.", "Setting locale to %s failed" : "Sätta locale till %s misslyckades", "Please install one of these locales on your system and restart your webserver." : "Vänligen installera en av dessa locale på din server och starta om dinn webbserver,", @@ -179,6 +186,11 @@ OC.L10N.register( "PHP module %s not installed." : "PHP modulen %s är inte installerad.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP inställning \"%s\" är inte inställd på \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Att ändra denna inställning i php.ini kommer göra så att Nextcloud fungerar igen", + "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload är satt till \"%s\" istället för det förväntade värdet \"0\"", + "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "För att åtgärda detta problem sätt värdet <code> mbstring.func_overload till </ code> <code> 0 </ code> i din php.ini", + "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 är det minsta som krävs. För närvarande är %s installerat.", + "To fix this issue update your libxml2 version and restart your web server." : "För att åtgärda detta problem uppdatera libxml2 versionen och starta om din webbserver.", + "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP är tydligen inställt för att tömma \"inline doc blocks\". Detta kommer att göra flera kärnprogram otillgängliga.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Detta orsakas troligtvis av en cache/accelerator som t ex Zend OPchache eller eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "PHP-moduler har installerats, men de listas fortfarande som saknade?", "Please ask your server administrator to restart the web server." : "Vänligen be din serveradministratör att starta om webservern.", @@ -190,7 +202,7 @@ OC.L10N.register( "Check the value of \"datadirectory\" in your configuration" : "Kontrollera värdet av \"datakatalog\" i din konfiguration", "Data directory (%s) is invalid" : "Datakatlogen (%s) är ogiltig", "Please check that the data directory contains a file \".ocdata\" in its root." : "Vänligen kontrollera att datakatalogen innehåller filen \".ocdata\" i rooten.", - "Could not obtain lock type %d on \"%s\"." : "Kan inte hämta låstyp %d på \"%s\".", + "Could not obtain lock type %d on \"%s\"." : "Kunde inte hämta låstyp %d på \"%s\".", "Storage unauthorized. %s" : "Lagringsutrymme ej tillåtet. %s", "Storage incomplete configuration. %s" : "Lagringsutrymme felaktigt inställt. %s", "Storage connection error. %s" : "Lagringsutrymme lyckas inte ansluta. %s", @@ -206,10 +218,16 @@ OC.L10N.register( "Archives of type %s are not supported" : "Arkiv av typen %s stöds ej", "Failed to open archive when installing app" : "Kunde inte öppna arkivet när appen skulle installeras", "App does not provide an info.xml file" : "Appen har ingen info.xml fil", + "App cannot be installed because appinfo file cannot be read." : "Appen kan inte installeras eftersom app-informationen inte kan läsas i filen.", + "Signature could not get checked. Please contact the app developer and check your admin screen." : "Signaturen kunde inte kontrolleras. Vänligen kontakta appens utvecklare och kontrollera administratörsinställningarna.", "App can't be installed because of not allowed code in the App" : "Appen kan inte installeras eftersom att den innehåller otillåten kod", + "App can't be installed because it is not compatible with this version of the server" : "Appen kan inte installeras eftersom den inte är förenlig med den här versionen av servern", "App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps" : "Appen kan inte installeras eftersom att den innehåller etiketten <shipped>true</shipped> vilket inte är tillåtet för icke inkluderade appar", + "App can't be installed because the version in info.xml is not the same as the version reported from the app store" : "Appen kan inte installeras eftersom det är fel version på info.xml och stämmer inte överens med versionen från \"App Store\"", "Logging" : "Loggning", "Recommended" : "Rekomenderad", + "Microsoft Windows Platform is not supported" : "Microsoft Windows-plattformen stöds inte", + "Running Nextcloud Server on the Microsoft Windows platform is not supported. We suggest you use a Linux server in a virtual machine if you have no option for migrating the server itself." : "Att köra en Nextcloud-Server på Microsoft Windows-plattformen stöds inte. Vi föreslår att du använder en Linux-server i en virtuell maskin om du inte har möjlighet att migrera själva servern.", "Storage not available" : "Lagringsutrymme ej tillgängligt" }, "nplurals=2; plural=(n != 1);"); diff --git a/lib/l10n/sv.json b/lib/l10n/sv.json index 86fe274a033..bfbafe34ffc 100644 --- a/lib/l10n/sv.json +++ b/lib/l10n/sv.json @@ -3,6 +3,7 @@ "This can usually be fixed by giving the webserver write access to the config directory" : "Detta kan vanligtvis åtgärdas genom att ge skrivrättigheter till config katalgogen", "See %s" : "Se %s", "This can usually be fixed by %sgiving the webserver write access to the config directory%s." : "Detta kan vanligtvis åtgärdas genom att %s ger webbservern skrivrättigheter till konfigurations-katalogen %s.", + "The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server." : "Filerna i appen %$1s ersattes inte korrekt. Se till att det är en version som är kompatibel med servern.", "Sample configuration detected" : "Exempel-konfiguration detekterad", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Det har detekterats att exempel-konfigurationen har kopierats. Detta kan förstöra din installation och stöds ej. Vänligen läs dokumentationen innan ändringar på config.php utförs", "%1$s and %2$s" : "%1$s och %2$s", @@ -33,11 +34,14 @@ "_%n hour ago_::_%n hours ago_" : ["%n timme sedan","%n timmar sedan"], "_%n minute ago_::_%n minutes ago_" : ["%n minut sedan","%n minuter sedan"], "seconds ago" : "sekunder sedan", + "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modulen med id: %s finns inte. Vänligen aktivera det i dina app-inställningar eller kontakta din administratör.", "File name is a reserved word" : "Filnamnet är ett reserverat ord", "File name contains at least one invalid character" : "Filnamnet innehåller minst ett ogiltigt tecken", "File name is too long" : "Filnamnet är för långt", "Dot files are not allowed" : "Dot filer är inte tillåtna", "Empty filename is not allowed" : "Tomma filnamn är inte tillåtna", + "APCu" : "APCu", + "Redis" : "Redis", "Server settings" : "Serverinställningar", "Sharing" : "Delning", "Encryption" : "Kryptering", @@ -56,10 +60,12 @@ "PostgreSQL username and/or password not valid" : "PostgreSQL-användarnamnet och/eller lösenordet är felaktigt", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X stöds inte och %s kommer inte att fungera korrekt på denna plattform. Använd på egen risk!", "For the best results, please consider using a GNU/Linux server instead." : "För bästa resultat, överväg att använda en GNU/Linux server istället.", + "It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Det verkar som om denna %s instans körs på en 32-bitars PHP miljö och open_basedir har konfigurerats i php.ini. Detta kommer att leda till problem med filer över 4 GB och är verkligen inte rekommenderat!", + "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Ta bort open_basedir i din php.ini eller byt till 64-bitars PHP.", "Set an admin username." : "Ange ett användarnamn för administratören.", "Set an admin password." : "Ange ett administratörslösenord.", "Can't create or write into the data directory %s" : "Kan inte skapa eller skriva till data-katalogen %s", - "Invalid Federated Cloud ID" : "Ogiltigt \"Federated Cloud\"-ID", + "Invalid Federated Cloud ID" : "Ogiltigt Federarat Moln-ID", "%s shared »%s« with you" : "%s delade »%s« med dig", "%s via %s" : "%s via %s", "Sharing %s failed, because the backend does not allow shares from type %i" : "Misslyckades dela ut %s då backend inte tillåter delningar från typ %i", @@ -91,7 +97,7 @@ "Sharing %s failed, because the sharing backend for %s could not find its source" : "Delning %s misslyckades därför att delningsgränsnittet för %s inte kunde hitta sin källa", "Sharing %s failed, because the file could not be found in the file cache" : "Delning %s misslyckades därför att filen inte kunde hittas i filcachen", "Cannot increase permissions of %s" : "Kan ej öka behörigheterna för %s", - "Files can't be shared with delete permissions" : "Filerna kan ej delas med \"ta-bort behörigheter\"", + "Files can't be shared with delete permissions" : "Filerna kan ej delas med \"radera behörigheter\"", "Files can't be shared with create permissions" : "Filerna kan ej delas med \"skapa behörigheter\"", "Expiration date is in the past" : "Utgångsdatum är i det förflutna", "Cannot set expiration date more than %s days in the future" : "Kan ej välja ett utgångsdatum längre fram än %s dagar", @@ -150,7 +156,7 @@ "Login canceled by app" : "Inloggningen avbruten av appen", "User disabled" : "Användare inaktiverad", "Help" : "Hjälp", - "Personal" : "Min profil", + "Personal" : "Personligt", "Users" : "Användare", "Admin" : "Administration", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Applikationen \"%s\" kan ej installeras eftersom informationen från appen ej kunde läsas.", @@ -170,6 +176,7 @@ "Cannot write into \"apps\" directory" : "Kan inte skriva till \"apps\" katalogen!", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "Detta kan vanligtvis åtgärdas genom att %s ger webbservern skrivrättigheter till applikationskatalogen %s eller stänga av app-butik i konfigurationsfilen.", "Cannot create \"data\" directory (%s)" : "Kan inte skapa \"data\" katalog (%s)", + "This can usually be fixed by <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">giving the webserver write access to the root directory</a>." : "Detta kan vanligtvis åtgärdas genom att <a href=\"%s\" target=\"_blank\" rel=\"noreferrer\"> ge webbserver skrivåtkomst till rotkatalogen </a>.", "Permissions can usually be fixed by %sgiving the webserver write access to the root directory%s." : "Rättigheterna kan vanligtvis åtgärdas genom att %s ger webbservern skrivrättigheter till rootkatalogen %s.", "Setting locale to %s failed" : "Sätta locale till %s misslyckades", "Please install one of these locales on your system and restart your webserver." : "Vänligen installera en av dessa locale på din server och starta om dinn webbserver,", @@ -177,6 +184,11 @@ "PHP module %s not installed." : "PHP modulen %s är inte installerad.", "PHP setting \"%s\" is not set to \"%s\"." : "PHP inställning \"%s\" är inte inställd på \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Att ändra denna inställning i php.ini kommer göra så att Nextcloud fungerar igen", + "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload är satt till \"%s\" istället för det förväntade värdet \"0\"", + "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "För att åtgärda detta problem sätt värdet <code> mbstring.func_overload till </ code> <code> 0 </ code> i din php.ini", + "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 är det minsta som krävs. För närvarande är %s installerat.", + "To fix this issue update your libxml2 version and restart your web server." : "För att åtgärda detta problem uppdatera libxml2 versionen och starta om din webbserver.", + "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP är tydligen inställt för att tömma \"inline doc blocks\". Detta kommer att göra flera kärnprogram otillgängliga.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Detta orsakas troligtvis av en cache/accelerator som t ex Zend OPchache eller eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "PHP-moduler har installerats, men de listas fortfarande som saknade?", "Please ask your server administrator to restart the web server." : "Vänligen be din serveradministratör att starta om webservern.", @@ -188,7 +200,7 @@ "Check the value of \"datadirectory\" in your configuration" : "Kontrollera värdet av \"datakatalog\" i din konfiguration", "Data directory (%s) is invalid" : "Datakatlogen (%s) är ogiltig", "Please check that the data directory contains a file \".ocdata\" in its root." : "Vänligen kontrollera att datakatalogen innehåller filen \".ocdata\" i rooten.", - "Could not obtain lock type %d on \"%s\"." : "Kan inte hämta låstyp %d på \"%s\".", + "Could not obtain lock type %d on \"%s\"." : "Kunde inte hämta låstyp %d på \"%s\".", "Storage unauthorized. %s" : "Lagringsutrymme ej tillåtet. %s", "Storage incomplete configuration. %s" : "Lagringsutrymme felaktigt inställt. %s", "Storage connection error. %s" : "Lagringsutrymme lyckas inte ansluta. %s", @@ -204,10 +216,16 @@ "Archives of type %s are not supported" : "Arkiv av typen %s stöds ej", "Failed to open archive when installing app" : "Kunde inte öppna arkivet när appen skulle installeras", "App does not provide an info.xml file" : "Appen har ingen info.xml fil", + "App cannot be installed because appinfo file cannot be read." : "Appen kan inte installeras eftersom app-informationen inte kan läsas i filen.", + "Signature could not get checked. Please contact the app developer and check your admin screen." : "Signaturen kunde inte kontrolleras. Vänligen kontakta appens utvecklare och kontrollera administratörsinställningarna.", "App can't be installed because of not allowed code in the App" : "Appen kan inte installeras eftersom att den innehåller otillåten kod", + "App can't be installed because it is not compatible with this version of the server" : "Appen kan inte installeras eftersom den inte är förenlig med den här versionen av servern", "App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps" : "Appen kan inte installeras eftersom att den innehåller etiketten <shipped>true</shipped> vilket inte är tillåtet för icke inkluderade appar", + "App can't be installed because the version in info.xml is not the same as the version reported from the app store" : "Appen kan inte installeras eftersom det är fel version på info.xml och stämmer inte överens med versionen från \"App Store\"", "Logging" : "Loggning", "Recommended" : "Rekomenderad", + "Microsoft Windows Platform is not supported" : "Microsoft Windows-plattformen stöds inte", + "Running Nextcloud Server on the Microsoft Windows platform is not supported. We suggest you use a Linux server in a virtual machine if you have no option for migrating the server itself." : "Att köra en Nextcloud-Server på Microsoft Windows-plattformen stöds inte. Vi föreslår att du använder en Linux-server i en virtuell maskin om du inte har möjlighet att migrera själva servern.", "Storage not available" : "Lagringsutrymme ej tillgängligt" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index fca5c9b87ac..6b819ef7ac1 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -221,6 +221,21 @@ class AppManager implements IAppManager { } /** + * Whether a list of types contains a protected app type + * + * @param string[] $types + * @return bool + */ + public function hasProtectedAppType($types) { + if (empty($types)) { + return false; + } + + $protectedTypes = array_intersect($this->protectedAppTypes, $types); + return !empty($protectedTypes); + } + + /** * Enable an app only for specific groups * * @param string $appId diff --git a/lib/private/App/AppStore/Fetcher/AppFetcher.php b/lib/private/App/AppStore/Fetcher/AppFetcher.php index 9ebc12dbc27..7c5efafc92f 100644 --- a/lib/private/App/AppStore/Fetcher/AppFetcher.php +++ b/lib/private/App/AppStore/Fetcher/AppFetcher.php @@ -59,11 +59,14 @@ class AppFetcher extends Fetcher { /** * Only returns the latest compatible app release in the releases array * + * @param string $ETag + * @param string $content + * * @return array */ - protected function fetch() { + protected function fetch($ETag, $content) { /** @var mixed[] $response */ - $response = parent::fetch(); + $response = parent::fetch($ETag, $content); $ncVersion = $this->config->getSystemValue('version'); $ncMajorVersion = explode('.', $ncVersion)[0]; diff --git a/lib/private/App/AppStore/Fetcher/Fetcher.php b/lib/private/App/AppStore/Fetcher/Fetcher.php index 2067242e817..dab79e11821 100644 --- a/lib/private/App/AppStore/Fetcher/Fetcher.php +++ b/lib/private/App/AppStore/Fetcher/Fetcher.php @@ -21,6 +21,7 @@ namespace OC\App\AppStore\Fetcher; +use OCP\AppFramework\Http; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Files\IAppData; use OCP\Files\NotFoundException; @@ -62,15 +63,37 @@ abstract class Fetcher { /** * Fetches the response from the server * + * @param string $ETag + * @param string $content + * * @return array */ - protected function fetch() { + protected function fetch($ETag, $content) { + $options = []; + + if ($ETag !== '') { + $options['headers'] = [ + 'If-None-Match' => $ETag, + ]; + } + $client = $this->clientService->newClient(); - $response = $client->get($this->endpointUrl); + $response = $client->get($this->endpointUrl, $options); + $responseJson = []; - $responseJson['data'] = json_decode($response->getBody(), true); + if ($response->getStatusCode() === Http::STATUS_NOT_MODIFIED) { + $responseJson['data'] = json_decode($content, true); + } else { + $responseJson['data'] = json_decode($response->getBody(), true); + $ETag = $response->getHeader('ETag'); + } + $responseJson['timestamp'] = $this->timeFactory->getTime(); $responseJson['ncversion'] = $this->config->getSystemValue('version'); + if ($ETag !== '') { + $responseJson['ETag'] = $ETag; + } + return $responseJson; } @@ -82,6 +105,9 @@ abstract class Fetcher { public function get() { $rootFolder = $this->appData->getFolder('/'); + $ETag = ''; + $content = ''; + try { // File does already exists $file = $rootFolder->getFile($this->fileName); @@ -95,6 +121,11 @@ abstract class Fetcher { isset($jsonBlob['ncversion']) && $jsonBlob['ncversion'] === $this->config->getSystemValue('version', '0.0.0')) { return $jsonBlob['data']; } + + if (isset($jsonBlob['ETag'])) { + $ETag = $jsonBlob['ETag']; + $content = json_encode($jsonBlob['data']); + } } } catch (NotFoundException $e) { // File does not already exists @@ -103,7 +134,7 @@ abstract class Fetcher { // Refresh the file content try { - $responseJson = $this->fetch(); + $responseJson = $this->fetch($ETag, $content); $file->putContent(json_encode($responseJson)); return json_decode($file->getContent(), true)['data']; } catch (\Exception $e) { diff --git a/lib/private/Archive/TAR.php b/lib/private/Archive/TAR.php index bbd24bd05a1..07ccd09f399 100644 --- a/lib/private/Archive/TAR.php +++ b/lib/private/Archive/TAR.php @@ -33,6 +33,8 @@ namespace OC\Archive; +use Icewind\Streams\CallbackWrapper; + class TAR extends Archive { const PLAIN = 0; const GZIP = 1; @@ -359,22 +361,19 @@ class TAR extends Archive { if ($mode == 'r' or $mode == 'rb') { return fopen($tmpFile, $mode); } else { - \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); - self::$tempFiles[$tmpFile] = $path; - return fopen('close://' . $tmpFile, $mode); + $handle = fopen($tmpFile, $mode); + return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) { + $this->writeBack($tmpFile, $path); + }); } } - private static $tempFiles = array(); - /** * write back temporary files */ - function writeBack($tmpFile) { - if (isset(self::$tempFiles[$tmpFile])) { - $this->addFile(self::$tempFiles[$tmpFile], $tmpFile); - unlink($tmpFile); - } + function writeBack($tmpFile, $path) { + $this->addFile($path, $tmpFile); + unlink($tmpFile); } /** diff --git a/lib/private/Archive/ZIP.php b/lib/private/Archive/ZIP.php index 9e9fe40b2b4..0ed0f48acc4 100644 --- a/lib/private/Archive/ZIP.php +++ b/lib/private/Archive/ZIP.php @@ -31,6 +31,8 @@ namespace OC\Archive; +use Icewind\Streams\CallbackWrapper; + class ZIP extends Archive{ /** * @var \ZipArchive zip @@ -198,24 +200,22 @@ class ZIP extends Archive{ $ext=''; } $tmpFile=\OCP\Files::tmpFile($ext); - \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); if($this->fileExists($path)) { $this->extractFile($path, $tmpFile); } - self::$tempFiles[$tmpFile]=$path; - return fopen('close://'.$tmpFile, $mode); + $handle = fopen($tmpFile, $mode); + return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) { + $this->writeBack($tmpFile, $path); + }); } } - private static $tempFiles=array(); /** * write back temporary files */ - function writeBack($tmpFile) { - if(isset(self::$tempFiles[$tmpFile])) { - $this->addFile(self::$tempFiles[$tmpFile], $tmpFile); - unlink($tmpFile); - } + function writeBack($tmpFile, $path) { + $this->addFile($path, $tmpFile); + unlink($tmpFile); } /** diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php index f9170e97a02..f172260df79 100644 --- a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php @@ -30,6 +30,8 @@ use OC\DB\QueryBuilder\Literal; use OC\DB\QueryBuilder\QueryFunction; use OC\DB\QueryBuilder\QuoteHelper; use OCP\DB\QueryBuilder\IExpressionBuilder; +use OCP\DB\QueryBuilder\ILiteral; +use OCP\DB\QueryBuilder\IQueryFunction; use OCP\IDBConnection; class ExpressionBuilder implements IExpressionBuilder { @@ -39,12 +41,16 @@ class ExpressionBuilder implements IExpressionBuilder { /** @var QuoteHelper */ protected $helper; + /** @var IDBConnection */ + protected $connection; + /** * Initializes a new <tt>ExpressionBuilder</tt>. * * @param \OCP\IDBConnection $connection */ public function __construct(IDBConnection $connection) { + $this->connection = $connection; $this->helper = new QuoteHelper(); $this->expressionBuilder = new DoctrineExpressionBuilder($connection); } @@ -345,12 +351,42 @@ class ExpressionBuilder implements IExpressionBuilder { } /** + * Binary AND Operator copies a bit to the result if it exists in both operands. + * + * @param string|ILiteral $x The field or value to check + * @param int $y Bitmap that must be set + * @return IQueryFunction + * @since 12.0.0 + */ + public function bitwiseAnd($x, $y) { + return new QueryFunction($this->connection->getDatabasePlatform()->getBitAndComparisonExpression( + $this->helper->quoteColumnName($x), + $y + )); + } + + /** + * Binary OR Operator copies a bit if it exists in either operand. + * + * @param string|ILiteral $x The field or value to check + * @param int $y Bitmap that must be set + * @return IQueryFunction + * @since 12.0.0 + */ + public function bitwiseOr($x, $y) { + return new QueryFunction($this->connection->getDatabasePlatform()->getBitOrComparisonExpression( + $this->helper->quoteColumnName($x), + $y + )); + } + + /** * Quotes a given input parameter. * * @param mixed $input The parameter to be quoted. * @param mixed|null $type One of the IQueryBuilder::PARAM_* constants * - * @return Literal + * @return ILiteral */ public function literal($input, $type = null) { return new Literal($this->expressionBuilder->literal($input, $type)); diff --git a/lib/private/Files/ObjectStore/ObjectStoreStorage.php b/lib/private/Files/ObjectStore/ObjectStoreStorage.php index 2dcf830cc1e..ab77c21e6c4 100644 --- a/lib/private/Files/ObjectStore/ObjectStoreStorage.php +++ b/lib/private/Files/ObjectStore/ObjectStoreStorage.php @@ -25,16 +25,12 @@ namespace OC\Files\ObjectStore; +use Icewind\Streams\CallbackWrapper; use Icewind\Streams\IteratorDirectory; use OC\Files\Cache\CacheEntry; use OCP\Files\ObjectStore\IObjectStore; class ObjectStoreStorage extends \OC\Files\Storage\Common { - - /** - * @var array - */ - private static $tmpFiles = array(); /** * @var \OCP\Files\ObjectStore\IObjectStore $objectStore */ @@ -291,14 +287,14 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common { $ext = ''; } $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext); - \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); if ($this->file_exists($path)) { $source = $this->fopen($path, 'r'); file_put_contents($tmpFile, $source); } - self::$tmpFiles[$tmpFile] = $path; - - return fopen('close://' . $tmpFile, $mode); + $handle = fopen($tmpFile, $mode); + return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) { + $this->writeBack($tmpFile, $path); + }); } return false; } @@ -368,12 +364,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common { return true; } - public function writeBack($tmpFile) { - if (!isset(self::$tmpFiles[$tmpFile])) { - return; - } - - $path = self::$tmpFiles[$tmpFile]; + public function writeBack($tmpFile, $path) { $stat = $this->stat($path); if (empty($stat)) { // create new file diff --git a/lib/private/Files/Storage/DAV.php b/lib/private/Files/Storage/DAV.php index 2f821b218fc..abe219f99f2 100644 --- a/lib/private/Files/Storage/DAV.php +++ b/lib/private/Files/Storage/DAV.php @@ -36,6 +36,7 @@ namespace OC\Files\Storage; use Exception; use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Message\ResponseInterface; +use Icewind\Streams\CallbackWrapper; use OC\Files\Filesystem; use OC\Files\Stream\Close; use Icewind\Streams\IteratorDirectory; @@ -76,8 +77,6 @@ class DAV extends Common { private $client; /** @var ArrayCache */ private $statCache; - /** @var array */ - private static $tempFiles = []; /** @var \OCP\Http\Client\IClientService */ private $httpClientService; @@ -395,20 +394,19 @@ class DAV extends Common { } $tmpFile = $tempManager->getTemporaryFile($ext); } - Close::registerCallback($tmpFile, array($this, 'writeBack')); - self::$tempFiles[$tmpFile] = $path; - return fopen('close://' . $tmpFile, $mode); + $handle = fopen($tmpFile, $mode); + return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) { + $this->writeBack($tmpFile, $path); + }); } } /** * @param string $tmpFile */ - public function writeBack($tmpFile) { - if (isset(self::$tempFiles[$tmpFile])) { - $this->uploadFile($tmpFile, self::$tempFiles[$tmpFile]); - unlink($tmpFile); - } + public function writeBack($tmpFile, $path) { + $this->uploadFile($tmpFile, $path); + unlink($tmpFile); } /** {@inheritdoc} */ diff --git a/lib/private/Files/Stream/Close.php b/lib/private/Files/Stream/Close.php deleted file mode 100644 index 7cc9903c912..00000000000 --- a/lib/private/Files/Stream/Close.php +++ /dev/null @@ -1,119 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OC\Files\Stream; - -/** - * stream wrapper that provides a callback on stream close - */ -class Close { - private static $callBacks = array(); - private $path = ''; - private $source; - private static $open = array(); - - public function stream_open($path, $mode, $options, &$opened_path) { - $path = substr($path, strlen('close://')); - $this->path = $path; - $this->source = fopen($path, $mode); - if (is_resource($this->source)) { - $this->meta = stream_get_meta_data($this->source); - } - self::$open[] = $path; - return is_resource($this->source); - } - - public function stream_seek($offset, $whence = SEEK_SET) { - return fseek($this->source, $offset, $whence) === 0; - } - - public function stream_tell() { - return ftell($this->source); - } - - public function stream_read($count) { - return fread($this->source, $count); - } - - public function stream_write($data) { - return fwrite($this->source, $data); - } - - public function stream_set_option($option, $arg1, $arg2) { - switch ($option) { - case STREAM_OPTION_BLOCKING: - stream_set_blocking($this->source, $arg1); - break; - case STREAM_OPTION_READ_TIMEOUT: - stream_set_timeout($this->source, $arg1, $arg2); - break; - case STREAM_OPTION_WRITE_BUFFER: - stream_set_write_buffer($this->source, $arg1, $arg2); - } - } - - public function stream_stat() { - return fstat($this->source); - } - - public function stream_lock($mode) { - flock($this->source, $mode); - } - - public function stream_flush() { - return fflush($this->source); - } - - public function stream_eof() { - return feof($this->source); - } - - public function url_stat($path) { - $path = substr($path, strlen('close://')); - if (file_exists($path)) { - return stat($path); - } else { - return false; - } - } - - public function stream_close() { - fclose($this->source); - if (isset(self::$callBacks[$this->path])) { - call_user_func(self::$callBacks[$this->path], $this->path); - } - } - - public function unlink($path) { - $path = substr($path, strlen('close://')); - return unlink($path); - } - - /** - * @param string $path - */ - public static function registerCallback($path, $callback) { - self::$callBacks[$path] = $callback; - } -} diff --git a/lib/private/Files/Stream/Quota.php b/lib/private/Files/Stream/Quota.php index f064ca6c011..624a2021b9c 100644 --- a/lib/private/Files/Stream/Quota.php +++ b/lib/private/Files/Stream/Quota.php @@ -25,61 +25,44 @@ namespace OC\Files\Stream; +use Icewind\Streams\Wrapper; + /** * stream wrapper limits the amount of data that can be written to a stream * - * usage: void \OC\Files\Stream\Quota::register($id, $stream, $limit) - * or: resource \OC\Files\Stream\Quota::wrap($stream, $limit) + * usage: resource \OC\Files\Stream\Quota::wrap($stream, $limit) */ -class Quota { - private static $streams = array(); - - /** - * @var resource $source - */ - private $source; - +class Quota extends Wrapper { /** * @var int $limit */ private $limit; /** - * @param string $id - * @param resource $stream - * @param int $limit - */ - public static function register($id, $stream, $limit) { - self::$streams[$id] = array($stream, $limit); - } - - /** - * remove all registered streams - */ - public static function clear() { - self::$streams = array(); - } - - /** * @param resource $stream * @param int $limit * @return resource */ static public function wrap($stream, $limit) { - $id = uniqid(); - self::register($id, $stream, $limit); - $meta = stream_get_meta_data($stream); - return fopen('quota://' . $id, $meta['mode']); + $context = stream_context_create(array( + 'quota' => array( + 'source' => $stream, + 'limit' => $limit + ) + )); + return Wrapper::wrapSource($stream, $context, 'quota', self::class); } public function stream_open($path, $mode, $options, &$opened_path) { - $id = substr($path, strlen('quota://')); - if (isset(self::$streams[$id])) { - list($this->source, $this->limit) = self::$streams[$id]; - return true; - } else { - return false; - } + $context = $this->loadContext('quota'); + $this->source = $context['source']; + $this->limit = $context['limit']; + + return true; + } + + public function dir_opendir($path, $options) { + return false; } public function stream_seek($offset, $whence = SEEK_SET) { @@ -103,10 +86,6 @@ class Quota { return fseek($this->source, $offset, $whence) === 0; } - public function stream_tell() { - return ftell($this->source); - } - public function stream_read($count) { $this->limit -= $count; return fread($this->source, $count); @@ -121,37 +100,4 @@ class Quota { $this->limit -= $size; return fwrite($this->source, $data); } - - public function stream_set_option($option, $arg1, $arg2) { - switch ($option) { - case STREAM_OPTION_BLOCKING: - stream_set_blocking($this->source, $arg1); - break; - case STREAM_OPTION_READ_TIMEOUT: - stream_set_timeout($this->source, $arg1, $arg2); - break; - case STREAM_OPTION_WRITE_BUFFER: - stream_set_write_buffer($this->source, $arg1, $arg2); - } - } - - public function stream_stat() { - return fstat($this->source); - } - - public function stream_lock($mode) { - return flock($this->source, $mode); - } - - public function stream_flush() { - return fflush($this->source); - } - - public function stream_eof() { - return feof($this->source); - } - - public function stream_close() { - fclose($this->source); - } } diff --git a/lib/private/Files/Utils/Scanner.php b/lib/private/Files/Utils/Scanner.php index d26c601be1a..98e2c3c8ca2 100644 --- a/lib/private/Files/Utils/Scanner.php +++ b/lib/private/Files/Utils/Scanner.php @@ -31,6 +31,7 @@ use OC\Files\Filesystem; use OC\ForbiddenException; use OC\Hooks\PublicEmitter; use OC\Lock\DBLockingProvider; +use OCA\Files_Sharing\SharedStorage; use OCP\Files\Storage\IStorage; use OCP\Files\StorageNotAvailableException; use OCP\ILogger; @@ -118,14 +119,19 @@ class Scanner extends PublicEmitter { public function backgroundScan($dir) { $mounts = $this->getMounts($dir); foreach ($mounts as $mount) { - if (is_null($mount->getStorage())) { + $storage = $mount->getStorage(); + if (is_null($storage)) { continue; } // don't scan the root storage - if ($mount->getStorage()->instanceOfStorage('\OC\Files\Storage\Local') && $mount->getMountPoint() === '/') { + if ($storage->instanceOfStorage('\OC\Files\Storage\Local') && $mount->getMountPoint() === '/') { + continue; + } + + // don't scan received local shares, these can be scanned when scanning the owner's storage + if ($storage->instanceOfStorage(SharedStorage::class)) { continue; } - $storage = $mount->getStorage(); $scanner = $storage->getScanner(); $this->attachListener($mount); @@ -156,10 +162,10 @@ class Scanner extends PublicEmitter { } $mounts = $this->getMounts($dir); foreach ($mounts as $mount) { - if (is_null($mount->getStorage())) { + $storage = $mount->getStorage(); + if (is_null($storage)) { continue; } - $storage = $mount->getStorage(); // if the home storage isn't writable then the scanner is run as the wrong user if ($storage->instanceOfStorage('\OC\Files\Storage\Home') and (!$storage->isCreatable('') or !$storage->isCreatable('files')) @@ -171,6 +177,11 @@ class Scanner extends PublicEmitter { } } + + // don't scan received local shares, these can be scanned when scanning the owner's storage + if ($storage->instanceOfStorage(SharedStorage::class)) { + continue; + } $relativePath = $mount->getInternalPath($dir); $scanner = $storage->getScanner(); $scanner->setUseTransactions(false); diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php index 6facc7b9462..909c49197b8 100644 --- a/lib/private/Files/View.php +++ b/lib/private/Files/View.php @@ -931,39 +931,36 @@ class View { /** * @param string $path - * @param string $mode + * @param string $mode 'r' or 'w' * @return resource */ public function fopen($path, $mode) { + $mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support $hooks = array(); switch ($mode) { case 'r': - case 'rb': $hooks[] = 'read'; break; case 'r+': - case 'rb+': case 'w+': - case 'wb+': case 'x+': - case 'xb+': case 'a+': - case 'ab+': $hooks[] = 'read'; $hooks[] = 'write'; break; case 'w': - case 'wb': case 'x': - case 'xb': case 'a': - case 'ab': $hooks[] = 'write'; break; default: \OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR); } + if ($mode !== 'r' && $mode !== 'w') { + \OC::$server->getLogger()->info('Trying to open a file with a mode other than "r" or "w" can cause severe performance issues with some backends'); + } + return $this->basicOperation('fopen', $path, $hooks, $mode); } @@ -1005,7 +1002,7 @@ class View { // Create the directories if any if (!$this->file_exists($filePath)) { $result = $this->createParentDirectories($filePath); - if($result === false) { + if ($result === false) { return false; } } @@ -1357,7 +1354,7 @@ class View { //add the sizes of other mount points to the folder $extOnly = ($includeMountPoints === 'ext'); $mounts = Filesystem::getMountManager()->findIn($path); - $info->setSubMounts(array_filter($mounts, function(IMountPoint $mount) use ($extOnly) { + $info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) { $subStorage = $mount->getStorage(); return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage); })); @@ -2106,13 +2103,13 @@ class View { private function createParentDirectories($filePath) { $directoryParts = explode('/', $filePath); $directoryParts = array_filter($directoryParts); - foreach($directoryParts as $key => $part) { + foreach ($directoryParts as $key => $part) { $currentPathElements = array_slice($directoryParts, 0, $key); $currentPath = '/' . implode('/', $currentPathElements); - if($this->is_file($currentPath)) { + if ($this->is_file($currentPath)) { return false; } - if(!$this->file_exists($currentPath)) { + if (!$this->file_exists($currentPath)) { $this->mkdir($currentPath); } } diff --git a/lib/private/Log.php b/lib/private/Log.php index ef1b70d3cb9..fddd3593127 100644 --- a/lib/private/Log.php +++ b/lib/private/Log.php @@ -106,12 +106,8 @@ class Log implements ILogger { // FIXME: Add this for backwards compatibility, should be fixed at some point probably if($logger === null) { - // TODO: Drop backwards compatibility for config in the future $logType = $this->config->getValue('log_type', 'file'); - if($logType==='owncloud') { - $logType = 'file'; - } - $this->logger = 'OC\\Log\\'.ucfirst($logType); + $this->logger = static::getLogClass($logType); call_user_func(array($this->logger, 'init')); } else { $this->logger = $logger; @@ -327,4 +323,26 @@ class Log implements ILogger { $msg .= ': ' . json_encode($exception); $this->error($msg, $context); } + + /** + * @param string $logType + * @return string + * @internal + */ + public static function getLogClass($logType) { + switch (strtolower($logType)) { + case 'errorlog': + return \OC\Log\Errorlog::class; + case 'syslog': + return \OC\Log\Syslog::class; + case 'file': + return \OC\Log\File::class; + + // Backwards compatibility for old and fallback for unknown log types + case 'owncloud': + case 'nextcloud': + default: + return \OC\Log\File::class; + } + } } diff --git a/lib/private/Log/Rotate.php b/lib/private/Log/Rotate.php index 6c87c167378..866068433ff 100644 --- a/lib/private/Log/Rotate.php +++ b/lib/private/Log/Rotate.php @@ -32,7 +32,9 @@ namespace OC\Log; */ class Rotate extends \OC\BackgroundJob\Job { private $max_log_size; - public function run($logFile) { + public function run($dummy) { + $systemConfig = \OC::$server->getSystemConfig(); + $logFile = $systemConfig->getValue('logfile', $systemConfig->getValue('datadirectory', \OC::$SERVERROOT . '/data') . '/nextcloud.log'); $this->max_log_size = \OC::$server->getConfig()->getSystemValue('log_rotate_size', false); if ($this->max_log_size) { $filesize = @filesize($logFile); diff --git a/lib/private/Memcache/APC.php b/lib/private/Memcache/APC.php deleted file mode 100644 index 7db6d64b085..00000000000 --- a/lib/private/Memcache/APC.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andreas Fischer <bantu@owncloud.com> - * @author Clark Tomlinson <fallen013@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Otto Sabart <ottosabart@seberm.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OC\Memcache; - -use OCP\IMemcache; - -class APC extends Cache implements IMemcache { - use CASTrait { - cas as casEmulated; - } - - use CADTrait; - - public function get($key) { - $result = apc_fetch($this->getPrefix() . $key, $success); - if (!$success) { - return null; - } - return $result; - } - - public function set($key, $value, $ttl = 0) { - return apc_store($this->getPrefix() . $key, $value, $ttl); - } - - public function hasKey($key) { - return apc_exists($this->getPrefix() . $key); - } - - public function remove($key) { - return apc_delete($this->getPrefix() . $key); - } - - public function clear($prefix = '') { - $ns = $this->getPrefix() . $prefix; - $ns = preg_quote($ns, '/'); - $iter = new \APCIterator('user', '/^' . $ns . '/', APC_ITER_KEY); - return apc_delete($iter); - } - - /** - * Set a value in the cache if it's not already stored - * - * @param string $key - * @param mixed $value - * @param int $ttl Time To Live in seconds. Defaults to 60*60*24 - * @return bool - */ - public function add($key, $value, $ttl = 0) { - return apc_add($this->getPrefix() . $key, $value, $ttl); - } - - /** - * Increase a stored number - * - * @param string $key - * @param int $step - * @return int | bool - */ - public function inc($key, $step = 1) { - $this->add($key, 0); - return apc_inc($this->getPrefix() . $key, $step); - } - - /** - * Decrease a stored number - * - * @param string $key - * @param int $step - * @return int | bool - */ - public function dec($key, $step = 1) { - return apc_dec($this->getPrefix() . $key, $step); - } - - /** - * Compare and set - * - * @param string $key - * @param mixed $old - * @param mixed $new - * @return bool - */ - public function cas($key, $old, $new) { - // apc only does cas for ints - if (is_int($old) and is_int($new)) { - return apc_cas($this->getPrefix() . $key, $old, $new); - } else { - return $this->casEmulated($key, $old, $new); - } - } - - static public function isAvailable() { - if (!extension_loaded('apc')) { - return false; - } elseif (!\OC::$server->getIniWrapper()->getBool('apc.enabled')) { - return false; - } elseif (!\OC::$server->getIniWrapper()->getBool('apc.enable_cli') && \OC::$CLI) { - return false; - } else { - return true; - } - } -} - -if (!function_exists('apc_exists')) { - function apc_exists($keys) { - $result = false; - apc_fetch($keys, $result); - return $result; - } -} diff --git a/lib/private/Memcache/Factory.php b/lib/private/Memcache/Factory.php index f3841d31679..8e62e020faa 100644 --- a/lib/private/Memcache/Factory.php +++ b/lib/private/Memcache/Factory.php @@ -83,7 +83,7 @@ class Factory implements ICacheFactory { $missingCacheMessage = 'Memcache {class} not available for {use} cache'; $missingCacheHint = 'Is the matching PHP module installed and enabled?'; - if (!$localCacheClass::isAvailable()) { + if (!class_exists($localCacheClass) || !$localCacheClass::isAvailable()) { if (\OC::$CLI && !defined('PHPUNIT_RUN')) { // CLI should not hard-fail on broken memcache $this->logger->info($missingCacheMessage, [ @@ -98,7 +98,7 @@ class Factory implements ICacheFactory { ]), $missingCacheHint); } } - if (!$distributedCacheClass::isAvailable()) { + if (!class_exists($distributedCacheClass) || !$distributedCacheClass::isAvailable()) { if (\OC::$CLI && !defined('PHPUNIT_RUN')) { // CLI should not hard-fail on broken memcache $this->logger->info($missingCacheMessage, [ @@ -113,7 +113,7 @@ class Factory implements ICacheFactory { ]), $missingCacheHint); } } - if (!($lockingCacheClass && $lockingCacheClass::isAvailable())) { + if (!($lockingCacheClass && class_exists($distributedCacheClass) && $lockingCacheClass::isAvailable())) { // don't fallback since the fallback might not be suitable for storing lock $lockingCacheClass = self::NULL_CACHE; } diff --git a/lib/private/Preview.php b/lib/private/Preview.php deleted file mode 100644 index caa1e89bacc..00000000000 --- a/lib/private/Preview.php +++ /dev/null @@ -1,1349 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Frank Karlitschek <frank@karlitschek.de> - * @author Georg Ehrke <georg@owncloud.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Tobias Kaminsky <tobias@kaminsky.me> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ -namespace OC; - -use OC\Preview\Provider; -use OCP\Files\FileInfo; -use OCP\Files\NotFoundException; - -class Preview { - //the thumbnail folder - const THUMBNAILS_FOLDER = 'thumbnails'; - - const MODE_FILL = 'fill'; - const MODE_COVER = 'cover'; - - //config - private $maxScaleFactor; - /** @var int maximum width allowed for a preview */ - private $configMaxWidth; - /** @var int maximum height allowed for a preview */ - private $configMaxHeight; - - //fileview object - private $fileView = null; - private $userView = null; - - //vars - private $file; - private $maxX; - private $maxY; - private $scalingUp; - private $mimeType; - private $keepAspect = false; - private $mode = self::MODE_FILL; - - //used to calculate the size of the preview to generate - /** @var int $maxPreviewWidth max width a preview can have */ - private $maxPreviewWidth; - /** @var int $maxPreviewHeight max height a preview can have */ - private $maxPreviewHeight; - /** @var int $previewWidth calculated width of the preview we're looking for */ - private $previewWidth; - /** @var int $previewHeight calculated height of the preview we're looking for */ - private $previewHeight; - - //filemapper used for deleting previews - // index is path, value is fileinfo - static public $deleteFileMapper = array(); - static public $deleteChildrenMapper = array(); - - /** - * preview images object - * - * @var \OCP\IImage - */ - private $preview; - - /** - * @var \OCP\Files\FileInfo - */ - protected $info; - - /** - * check if thumbnail or bigger version of thumbnail of file is cached - * - * @param string $user userid - if no user is given, OC_User::getUser will be used - * @param string $root path of root - * @param string $file The path to the file where you want a thumbnail from - * @param int $maxX The maximum X size of the thumbnail. It can be smaller depending on the - * shape of the image - * @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the - * shape of the image - * @param bool $scalingUp Disable/Enable upscaling of previews - * - * @throws \Exception - * @return mixed (bool / string) - * false if thumbnail does not exist - * path to thumbnail if thumbnail exists - */ - public function __construct( - $user = '', - $root = '/', - $file = '', $maxX = 1, - $maxY = 1, - $scalingUp = true - ) { - //init fileviews - if ($user === '') { - $user = \OC_User::getUser(); - } - $this->fileView = new \OC\Files\View('/' . $user . '/' . $root); - $this->userView = new \OC\Files\View('/' . $user); - - //set config - $sysConfig = \OC::$server->getConfig(); - $this->configMaxWidth = $sysConfig->getSystemValue('preview_max_x', 2048); - $this->configMaxHeight = $sysConfig->getSystemValue('preview_max_y', 2048); - $this->maxScaleFactor = $sysConfig->getSystemValue('preview_max_scale_factor', 1); - - //save parameters - $this->setFile($file); - $this->setMaxX((int)$maxX); - $this->setMaxY((int)$maxY); - $this->setScalingup($scalingUp); - - $this->preview = null; - - //check if there are preview backends - if (!\OC::$server->getPreviewManager() - ->hasProviders() - && \OC::$server->getConfig() - ->getSystemValue('enable_previews', true) - ) { - \OCP\Util::writeLog('core', 'No preview providers exist', \OCP\Util::ERROR); - throw new \Exception('No preview providers'); - } - } - - /** - * returns the path of the file you want a thumbnail from - * - * @return string - */ - public function getFile() { - return $this->file; - } - - /** - * returns the max width of the preview - * - * @return integer - */ - public function getMaxX() { - return $this->maxX; - } - - /** - * returns the max height of the preview - * - * @return integer - */ - public function getMaxY() { - return $this->maxY; - } - - /** - * returns whether or not scalingup is enabled - * - * @return bool - */ - public function getScalingUp() { - return $this->scalingUp; - } - - /** - * returns the name of the thumbnailfolder - * - * @return string - */ - public function getThumbnailsFolder() { - return self::THUMBNAILS_FOLDER; - } - - /** - * returns the max scale factor - * - * @return string - */ - public function getMaxScaleFactor() { - return $this->maxScaleFactor; - } - - /** - * returns the max width set in ownCloud's config - * - * @return integer - */ - public function getConfigMaxX() { - return $this->configMaxWidth; - } - - /** - * returns the max height set in ownCloud's config - * - * @return integer - */ - public function getConfigMaxY() { - return $this->configMaxHeight; - } - - /** - * Returns the FileInfo object associated with the file to preview - * - * @return false|Files\FileInfo|\OCP\Files\FileInfo - */ - protected function getFileInfo() { - $absPath = $this->fileView->getAbsolutePath($this->file); - $absPath = Files\Filesystem::normalizePath($absPath); - if (array_key_exists($absPath, self::$deleteFileMapper)) { - $this->info = self::$deleteFileMapper[$absPath]; - } else if (!$this->info) { - $this->info = $this->fileView->getFileInfo($this->file); - } - - return $this->info; - } - - - /** - * @return array|null - */ - private function getChildren() { - $absPath = $this->fileView->getAbsolutePath($this->file); - $absPath = Files\Filesystem::normalizePath($absPath); - - if (array_key_exists($absPath, self::$deleteChildrenMapper)) { - return self::$deleteChildrenMapper[$absPath]; - } - - return null; - } - - /** - * Sets the path of the file you want a preview of - * - * @param string $file - * @param \OCP\Files\FileInfo|null $info - * - * @return \OC\Preview - */ - public function setFile($file, $info = null) { - $this->file = $file; - $this->info = $info; - - if ($file !== '') { - $this->getFileInfo(); - if ($this->info instanceof \OCP\Files\FileInfo) { - $this->mimeType = $this->info->getMimetype(); - } - } - - return $this; - } - - /** - * Forces the use of a specific media type - * - * @param string $mimeType - */ - public function setMimetype($mimeType) { - $this->mimeType = $mimeType; - } - - /** - * Sets the max width of the preview. It's capped by the maximum allowed size set in the - * configuration - * - * @param int $maxX - * - * @throws \Exception - * @return \OC\Preview - */ - public function setMaxX($maxX = 1) { - if ($maxX <= 0) { - throw new \Exception('Cannot set width of 0 or smaller!'); - } - $configMaxX = $this->getConfigMaxX(); - $maxX = $this->limitMaxDim($maxX, $configMaxX, 'maxX'); - $this->maxX = $maxX; - - return $this; - } - - /** - * Sets the max height of the preview. It's capped by the maximum allowed size set in the - * configuration - * - * @param int $maxY - * - * @throws \Exception - * @return \OC\Preview - */ - public function setMaxY($maxY = 1) { - if ($maxY <= 0) { - throw new \Exception('Cannot set height of 0 or smaller!'); - } - $configMaxY = $this->getConfigMaxY(); - $maxY = $this->limitMaxDim($maxY, $configMaxY, 'maxY'); - $this->maxY = $maxY; - - return $this; - } - - /** - * Sets whether we're allowed to scale up when generating a preview. It's capped by the maximum - * allowed scale factor set in the configuration - * - * @param bool $scalingUp - * - * @return \OC\Preview - */ - public function setScalingup($scalingUp) { - if ($this->getMaxScaleFactor() === 1) { - $scalingUp = false; - } - $this->scalingUp = $scalingUp; - - return $this; - } - - /** - * Set whether to cover or fill the specified dimensions - * - * @param string $mode - * - * @return \OC\Preview - */ - public function setMode($mode) { - $this->mode = $mode; - - return $this; - } - - /** - * Sets whether we need to generate a preview which keeps the aspect ratio of the original file - * - * @param bool $keepAspect - * - * @return \OC\Preview - */ - public function setKeepAspect($keepAspect) { - $this->keepAspect = $keepAspect; - - return $this; - } - - /** - * Makes sure we were given a file to preview and that it exists in the filesystem - * - * @return bool - */ - public function isFileValid() { - $file = $this->getFile(); - if ($file === '') { - \OCP\Util::writeLog('core', 'No filename passed', \OCP\Util::DEBUG); - - return false; - } - - if (!$this->getFileInfo() instanceof FileInfo) { - \OCP\Util::writeLog('core', 'File:"' . $file . '" not found', \OCP\Util::DEBUG); - - return false; - } - - return true; - } - - /** - * Deletes the preview of a file with specific width and height - * - * This should never delete the max preview, use deleteAllPreviews() instead - * - * @return bool - */ - public function deletePreview() { - $fileInfo = $this->getFileInfo(); - if ($fileInfo !== null && $fileInfo !== false) { - $fileId = $fileInfo->getId(); - - $previewPath = $this->buildCachePath($fileId); - if (!strpos($previewPath, 'max')) { - return $this->userView->unlink($previewPath); - } - } - - return false; - } - - /** - * Deletes all previews of a file - */ - public function deleteAllPreviews() { - $thumbnailMount = $this->userView->getMount($this->getThumbnailsFolder()); - $propagator = $thumbnailMount->getStorage()->getPropagator(); - $propagator->beginBatch(); - - $toDelete = $this->getChildren(); - $toDelete[] = $this->getFileInfo(); - - foreach ($toDelete as $delete) { - if ($delete instanceof FileInfo) { - /** @var \OCP\Files\FileInfo $delete */ - $fileId = $delete->getId(); - - // getId() might return null, e.g. when the file is a - // .ocTransferId*.part file from chunked file upload. - if (!empty($fileId)) { - $previewPath = $this->getPreviewPath($fileId); - $this->userView->rmdir($previewPath); - } - } - } - - $propagator->commitBatch(); - } - - /** - * Checks if a preview matching the asked dimensions or a bigger version is already cached - * - * * We first retrieve the size of the max preview since this is what we be used to create - * all our preview. If it doesn't exist we return false, so that it can be generated - * * Using the dimensions of the max preview, we calculate what the size of the new - * thumbnail should be - * * And finally, we look for a suitable candidate in the cache - * - * @param int $fileId fileId of the original file we need a preview of - * - * @return string|false path to the cached preview if it exists or false - */ - public function isCached($fileId) { - if (is_null($fileId)) { - return false; - } - - /** - * Phase 1: Looking for the max preview - */ - $previewPath = $this->getPreviewPath($fileId); - // We currently can't look for a single file due to bugs related to #16478 - $allThumbnails = $this->userView->getDirectoryContent($previewPath); - list($maxPreviewWidth, $maxPreviewHeight) = $this->getMaxPreviewSize($allThumbnails); - - // Only use the cache if we have a max preview - if (!is_null($maxPreviewWidth) && !is_null($maxPreviewHeight)) { - - /** - * Phase 2: Calculating the size of the preview we need to send back - */ - $this->maxPreviewWidth = $maxPreviewWidth; - $this->maxPreviewHeight = $maxPreviewHeight; - - list($previewWidth, $previewHeight) = $this->simulatePreviewDimensions(); - if (empty($previewWidth) || empty($previewHeight)) { - return false; - } - - $this->previewWidth = $previewWidth; - $this->previewHeight = $previewHeight; - - /** - * Phase 3: We look for a preview of the exact size - */ - // This gives us a calculated path to a preview of asked dimensions - // thumbnailFolder/fileId/<maxX>-<maxY>(-max|-with-aspect).png - $preview = $this->buildCachePath($fileId, $previewWidth, $previewHeight); - - // This checks if we have a preview of those exact dimensions in the cache - if ($this->thumbnailSizeExists($allThumbnails, basename($preview))) { - return $preview; - } - - /** - * Phase 4: We look for a larger preview, matching the aspect ratio - */ - if (($this->getMaxX() >= $maxPreviewWidth) - && ($this->getMaxY() >= $maxPreviewHeight) - ) { - // The preview we-re looking for is the exact size or larger than the max preview, - // so return that - return $this->buildCachePath($fileId, $maxPreviewWidth, $maxPreviewHeight); - } else { - // The last resort is to look for something bigger than what we've calculated, - // but still smaller than the max preview - return $this->isCachedBigger($fileId, $allThumbnails); - } - } - - return false; - } - - /** - * Returns the dimensions of the max preview - * - * @param FileInfo[] $allThumbnails the list of all our cached thumbnails - * - * @return int[] - */ - private function getMaxPreviewSize($allThumbnails) { - $maxPreviewX = null; - $maxPreviewY = null; - - foreach ($allThumbnails as $thumbnail) { - $name = $thumbnail['name']; - if (strpos($name, 'max')) { - list($maxPreviewX, $maxPreviewY) = $this->getDimensionsFromFilename($name); - break; - } - } - - return [$maxPreviewX, $maxPreviewY]; - } - - /** - * Check if a specific thumbnail size is cached - * - * @param FileInfo[] $allThumbnails the list of all our cached thumbnails - * @param string $name - * @return bool - */ - private function thumbnailSizeExists(array $allThumbnails, $name) { - - foreach ($allThumbnails as $thumbnail) { - if ($name === $thumbnail->getName()) { - return true; - } - } - - return false; - } - - /** - * Determines the size of the preview we should be looking for in the cache - * - * @return integer[] - */ - private function simulatePreviewDimensions() { - $askedWidth = $this->getMaxX(); - $askedHeight = $this->getMaxY(); - - if ($this->keepAspect) { - list($newPreviewWidth, $newPreviewHeight) = - $this->applyAspectRatio($askedWidth, $askedHeight); - } else { - list($newPreviewWidth, $newPreviewHeight) = $this->fixSize($askedWidth, $askedHeight); - } - - return [(int)$newPreviewWidth, (int)$newPreviewHeight]; - } - - /** - * Resizes the boundaries to match the aspect ratio - * - * @param int $askedWidth - * @param int $askedHeight - * - * @param int $originalWidth - * @param int $originalHeight - * @return integer[] - */ - private function applyAspectRatio($askedWidth, $askedHeight, $originalWidth = 0, $originalHeight = 0) { - if (!$originalWidth) { - $originalWidth = $this->maxPreviewWidth; - } - if (!$originalHeight) { - $originalHeight = $this->maxPreviewHeight; - } - $originalRatio = $originalWidth / $originalHeight; - // Defines the box in which the preview has to fit - $scaleFactor = $this->scalingUp ? $this->maxScaleFactor : 1; - $askedWidth = min($askedWidth, $originalWidth * $scaleFactor); - $askedHeight = min($askedHeight, $originalHeight * $scaleFactor); - - if ($askedWidth / $originalRatio < $askedHeight) { - // width restricted - $askedHeight = round($askedWidth / $originalRatio); - } else { - $askedWidth = round($askedHeight * $originalRatio); - } - - return [(int)$askedWidth, (int)$askedHeight]; - } - - /** - * Resizes the boundaries to cover the area - * - * @param int $askedWidth - * @param int $askedHeight - * @param int $previewWidth - * @param int $previewHeight - * @return integer[] - */ - private function applyCover($askedWidth, $askedHeight, $previewWidth, $previewHeight) { - $originalRatio = $previewWidth / $previewHeight; - // Defines the box in which the preview has to fit - $scaleFactor = $this->scalingUp ? $this->maxScaleFactor : 1; - $askedWidth = min($askedWidth, $previewWidth * $scaleFactor); - $askedHeight = min($askedHeight, $previewHeight * $scaleFactor); - - if ($askedWidth / $originalRatio > $askedHeight) { - // height restricted - $askedHeight = round($askedWidth / $originalRatio); - } else { - $askedWidth = round($askedHeight * $originalRatio); - } - - return [(int)$askedWidth, (int)$askedHeight]; - } - - /** - * Makes sure an upscaled preview doesn't end up larger than the max dimensions defined in the - * config - * - * @param int $askedWidth - * @param int $askedHeight - * - * @return integer[] - */ - private function fixSize($askedWidth, $askedHeight) { - if ($this->scalingUp) { - $askedWidth = min($this->configMaxWidth, $askedWidth); - $askedHeight = min($this->configMaxHeight, $askedHeight); - } - - return [(int)$askedWidth, (int)$askedHeight]; - } - - /** - * Checks if a bigger version of a file preview is cached and if not - * return the preview of max allowed dimensions - * - * @param int $fileId fileId of the original image - * @param FileInfo[] $allThumbnails the list of all our cached thumbnails - * - * @return string path to bigger thumbnail - */ - private function isCachedBigger($fileId, $allThumbnails) { - // This is used to eliminate any thumbnail narrower than what we need - $maxX = $this->getMaxX(); - - //array for usable cached thumbnails - $possibleThumbnails = $this->getPossibleThumbnails($allThumbnails); - - foreach ($possibleThumbnails as $width => $path) { - if ($width < $maxX) { - continue; - } else { - return $path; - } - } - - // At this stage, we didn't find a preview, so we return the max preview - return $this->buildCachePath($fileId, $this->maxPreviewWidth, $this->maxPreviewHeight); - } - - /** - * Get possible bigger thumbnails of the given image with the proper aspect ratio - * - * @param FileInfo[] $allThumbnails the list of all our cached thumbnails - * - * @return string[] an array of paths to bigger thumbnails - */ - private function getPossibleThumbnails($allThumbnails) { - if ($this->keepAspect) { - $wantedAspectRatio = (float)($this->maxPreviewWidth / $this->maxPreviewHeight); - } else { - $wantedAspectRatio = (float)($this->getMaxX() / $this->getMaxY()); - } - - //array for usable cached thumbnails - $possibleThumbnails = array(); - foreach ($allThumbnails as $thumbnail) { - $name = rtrim($thumbnail['name'], '.png'); - list($x, $y, $aspectRatio) = $this->getDimensionsFromFilename($name); - if (abs($aspectRatio - $wantedAspectRatio) >= 0.000001 - || $this->unscalable($x, $y) - ) { - continue; - } - $possibleThumbnails[$x] = $thumbnail['path']; - } - - ksort($possibleThumbnails); - - return $possibleThumbnails; - } - - /** - * Looks at the preview filename from the cache and extracts the size of the preview - * - * @param string $name - * - * @return array<int,int,float> - */ - private function getDimensionsFromFilename($name) { - $size = explode('-', $name); - $x = (int)$size[0]; - $y = (int)$size[1]; - $aspectRatio = (float)($x / $y); - - return array($x, $y, $aspectRatio); - } - - /** - * @param int $x - * @param int $y - * - * @return bool - */ - private function unscalable($x, $y) { - - $maxX = $this->getMaxX(); - $maxY = $this->getMaxY(); - $scalingUp = $this->getScalingUp(); - $maxScaleFactor = $this->getMaxScaleFactor(); - - if ($x < $maxX || $y < $maxY) { - if ($scalingUp) { - $scaleFactor = $maxX / $x; - if ($scaleFactor > $maxScaleFactor) { - return true; - } - } else { - return true; - } - } - - return false; - } - - /** - * Returns a preview of a file - * - * The cache is searched first and if nothing usable was found then a preview is - * generated by one of the providers - * - * @return \OCP\IImage - */ - public function getPreview() { - if (!is_null($this->preview) && $this->preview->valid()) { - return $this->preview; - } - - $this->preview = null; - $fileInfo = $this->getFileInfo(); - if ($fileInfo === null || $fileInfo === false || !$fileInfo->isReadable()) { - return new \OC_Image(); - } - - $fileId = $fileInfo->getId(); - $cached = $this->isCached($fileId); - if ($cached) { - $this->getCachedPreview($fileId, $cached); - } - - if (is_null($this->preview)) { - $this->generatePreview($fileId); - } - - // We still don't have a preview, so we send back an empty object - if (is_null($this->preview)) { - $this->preview = new \OC_Image(); - } - - return $this->preview; - } - - /** - * Sends the preview, including the headers to client which requested it - * - * @param null|string $mimeTypeForHeaders the media type to use when sending back the reply - * - * @throws NotFoundException - * @throws PreviewNotAvailableException - */ - public function showPreview($mimeTypeForHeaders = null) { - // Check if file is valid - if ($this->isFileValid() === false) { - throw new NotFoundException('File not found.'); - } - - if (is_null($this->preview)) { - $this->getPreview(); - } - if ($this->preview instanceof \OCP\IImage) { - if ($this->preview->valid()) { - \OCP\Response::enableCaching(3600 * 24); // 24 hours - } else { - $this->getMimeIcon(); - } - $this->preview->show($mimeTypeForHeaders); - } - } - - /** - * Retrieves the preview from the cache and resizes it if necessary - * - * @param int $fileId fileId of the original image - * @param string $cached the path to the cached preview - */ - private function getCachedPreview($fileId, $cached) { - $stream = $this->userView->fopen($cached, 'r'); - $this->preview = null; - if ($stream) { - $image = new \OC_Image(); - $image->loadFromFileHandle($stream); - - $this->preview = $image->valid() ? $image : null; - - if (!is_null($this->preview)) { - // Size of the preview we calculated - $maxX = $this->previewWidth; - $maxY = $this->previewHeight; - // Size of the preview we retrieved from the cache - $previewX = (int)$this->preview->width(); - $previewY = (int)$this->preview->height(); - - // We don't have an exact match - if ($previewX !== $maxX || $previewY !== $maxY) { - $this->resizeAndStore($fileId); - } - } - - fclose($stream); - } - } - - /** - * Resizes, crops, fixes orientation and stores in the cache - * - * @param int $fileId fileId of the original image - */ - private function resizeAndStore($fileId) { - $image = $this->preview; - if (!($image instanceof \OCP\IImage)) { - \OCP\Util::writeLog( - 'core', '$this->preview is not an instance of \OCP\IImage', \OCP\Util::DEBUG - ); - - return; - } - $previewWidth = (int)$image->width(); - $previewHeight = (int)$image->height(); - $askedWidth = $this->getMaxX(); - $askedHeight = $this->getMaxY(); - - if ($this->mode === self::MODE_COVER) { - list($askedWidth, $askedHeight) = - $this->applyCover($askedWidth, $askedHeight, $previewWidth, $previewHeight); - } - - /** - * Phase 1: If required, adjust boundaries to keep aspect ratio - */ - if ($this->keepAspect) { - list($askedWidth, $askedHeight) = - $this->applyAspectRatio($askedWidth, $askedHeight, $previewWidth, $previewHeight); - } - - /** - * Phase 2: Resizes preview to try and match requirements. - * Takes the scaling ratio into consideration - */ - list($newPreviewWidth, $newPreviewHeight) = $this->scale( - $image, $askedWidth, $askedHeight, $previewWidth, $previewHeight - ); - - // The preview has been resized and should now have the asked dimensions - if ($newPreviewWidth === $askedWidth && $newPreviewHeight === $askedHeight) { - $this->storePreview($fileId, $newPreviewWidth, $newPreviewHeight); - - return; - } - - /** - * Phase 3: We're still not there yet, so we're clipping and filling - * to match the asked dimensions - */ - // It turns out the scaled preview is now too big, so we crop the image - if ($newPreviewWidth >= $askedWidth && $newPreviewHeight >= $askedHeight) { - $this->crop($image, $askedWidth, $askedHeight, $newPreviewWidth, $newPreviewHeight); - $this->storePreview($fileId, $askedWidth, $askedHeight); - - return; - } - - // At least one dimension of the scaled preview is too small, - // so we fill the space with a transparent background - if (($newPreviewWidth < $askedWidth || $newPreviewHeight < $askedHeight)) { - $this->cropAndFill( - $image, $askedWidth, $askedHeight, $newPreviewWidth, $newPreviewHeight - ); - $this->storePreview($fileId, $askedWidth, $askedHeight); - - return; - } - - // The preview is smaller, but we can't touch it - $this->storePreview($fileId, $newPreviewWidth, $newPreviewHeight); - } - - /** - * Calculates the new dimensions of the preview - * - * The new dimensions can be larger or smaller than the ones of the preview we have to resize - * - * @param \OCP\IImage $image - * @param int $askedWidth - * @param int $askedHeight - * @param int $previewWidth - * @param int $previewHeight - * - * @return int[] - */ - private function scale($image, $askedWidth, $askedHeight, $previewWidth, $previewHeight) { - $scalingUp = $this->getScalingUp(); - $maxScaleFactor = $this->getMaxScaleFactor(); - - $factorX = $askedWidth / $previewWidth; - $factorY = $askedHeight / $previewHeight; - - if ($factorX >= $factorY) { - $factor = $factorX; - } else { - $factor = $factorY; - } - - if ($scalingUp === false) { - if ($factor > 1) { - $factor = 1; - } - } - - // We cap when upscaling - if (!is_null($maxScaleFactor)) { - if ($factor > $maxScaleFactor) { - \OCP\Util::writeLog( - 'core', 'scale factor reduced from ' . $factor . ' to ' . $maxScaleFactor, - \OCP\Util::DEBUG - ); - $factor = $maxScaleFactor; - } - } - - $newPreviewWidth = round($previewWidth * $factor); - $newPreviewHeight = round($previewHeight * $factor); - - $image->preciseResize($newPreviewWidth, $newPreviewHeight); - $this->preview = $image; - - return [$newPreviewWidth, $newPreviewHeight]; - } - - /** - * Crops a preview which is larger than the dimensions we've received - * - * @param \OCP\IImage $image - * @param int $askedWidth - * @param int $askedHeight - * @param int $previewWidth - * @param int $previewHeight - */ - private function crop($image, $askedWidth, $askedHeight, $previewWidth, $previewHeight = null) { - $cropX = floor(abs($askedWidth - $previewWidth) * 0.5); - //don't crop previews on the Y axis, this sucks if it's a document. - //$cropY = floor(abs($y - $newPreviewHeight) * 0.5); - $cropY = 0; - $image->crop($cropX, $cropY, $askedWidth, $askedHeight); - $this->preview = $image; - } - - /** - * Crops an image if it's larger than the dimensions we've received and fills the empty space - * with a transparent background - * - * @param \OCP\IImage $image - * @param int $askedWidth - * @param int $askedHeight - * @param int $previewWidth - * @param int $previewHeight - */ - private function cropAndFill($image, $askedWidth, $askedHeight, $previewWidth, $previewHeight) { - if ($previewWidth > $askedWidth) { - $cropX = floor(($previewWidth - $askedWidth) * 0.5); - $image->crop($cropX, 0, $askedWidth, $previewHeight); - $previewWidth = $askedWidth; - } - - if ($previewHeight > $askedHeight) { - $cropY = floor(($previewHeight - $askedHeight) * 0.5); - $image->crop(0, $cropY, $previewWidth, $askedHeight); - $previewHeight = $askedHeight; - } - - // Creates a transparent background - $backgroundLayer = imagecreatetruecolor($askedWidth, $askedHeight); - imagealphablending($backgroundLayer, false); - $transparency = imagecolorallocatealpha($backgroundLayer, 0, 0, 0, 127); - imagefill($backgroundLayer, 0, 0, $transparency); - imagesavealpha($backgroundLayer, true); - - $image = $image->resource(); - - $mergeX = floor(abs($askedWidth - $previewWidth) * 0.5); - $mergeY = floor(abs($askedHeight - $previewHeight) * 0.5); - - // Pastes the preview on top of the background - imagecopy( - $backgroundLayer, $image, $mergeX, $mergeY, 0, 0, $previewWidth, - $previewHeight - ); - - $image = new \OC_Image($backgroundLayer); - - $this->preview = $image; - } - - /** - * Saves a preview in the cache to speed up future calls - * - * Do not nullify the preview as it might send the whole process in a loop - * - * @param int $fileId fileId of the original image - * @param int $previewWidth - * @param int $previewHeight - */ - private function storePreview($fileId, $previewWidth, $previewHeight) { - if (empty($previewWidth) || empty($previewHeight)) { - \OCP\Util::writeLog( - 'core', 'Cannot save preview of dimension ' . $previewWidth . 'x' . $previewHeight, - \OCP\Util::DEBUG - ); - - } else { - $cachePath = $this->buildCachePath($fileId, $previewWidth, $previewHeight); - $this->userView->file_put_contents($cachePath, $this->preview->data()); - } - } - - /** - * Returns the path to a preview based on its dimensions and aspect - * - * @param int $fileId - * @param int|null $maxX - * @param int|null $maxY - * - * @return string - */ - private function buildCachePath($fileId, $maxX = null, $maxY = null) { - if (is_null($maxX)) { - $maxX = $this->getMaxX(); - } - if (is_null($maxY)) { - $maxY = $this->getMaxY(); - } - - $previewPath = $this->getPreviewPath($fileId); - $previewPath = $previewPath . strval($maxX) . '-' . strval($maxY); - $isMaxPreview = - ($maxX === $this->maxPreviewWidth && $maxY === $this->maxPreviewHeight) ? true : false; - if ($isMaxPreview) { - $previewPath .= '-max'; - } - if ($this->keepAspect && !$isMaxPreview) { - $previewPath .= '-with-aspect'; - } - if ($this->mode === self::MODE_COVER) { - $previewPath .= '-cover'; - } - $previewPath .= '.png'; - - return $previewPath; - } - - /** - * Returns the path to the folder where the previews are stored, identified by the fileId - * - * @param int $fileId - * - * @return string - */ - private function getPreviewPath($fileId) { - return $this->getThumbnailsFolder() . '/' . $fileId . '/'; - } - - /** - * Asks the provider to send a preview of the file which respects the maximum dimensions - * defined in the configuration and after saving it in the cache, it is then resized to the - * asked dimensions - * - * This is only called once in order to generate a large PNG of dimensions defined in the - * configuration file. We'll be able to quickly resize it later on. - * We never upscale the original conversion as this will be done later by the resizing - * operation - * - * @param int $fileId fileId of the original image - */ - private function generatePreview($fileId) { - $file = $this->getFile(); - $preview = null; - - $previewProviders = \OC::$server->getPreviewManager() - ->getProviders(); - foreach ($previewProviders as $supportedMimeType => $providers) { - if (!preg_match($supportedMimeType, $this->mimeType)) { - continue; - } - - foreach ($providers as $closure) { - $provider = $closure(); - if (!($provider instanceof \OCP\Preview\IProvider)) { - continue; - } - - \OCP\Util::writeLog( - 'core', 'Generating preview for "' . $file . '" with "' . get_class($provider) - . '"', \OCP\Util::DEBUG - ); - - /** @var $provider Provider */ - $preview = $provider->getThumbnail( - $file, $this->configMaxWidth, $this->configMaxHeight, $scalingUp = false, - $this->fileView - ); - - if (!($preview instanceof \OCP\IImage)) { - continue; - } - - $this->preview = $preview; - $previewPath = $this->getPreviewPath($fileId); - - if ($this->userView->is_dir($this->getThumbnailsFolder() . '/') === false) { - $this->userView->mkdir($this->getThumbnailsFolder() . '/'); - } - - if ($this->userView->is_dir($previewPath) === false) { - $this->userView->mkdir($previewPath); - } - - // This stores our large preview so that it can be used in subsequent resizing requests - $this->storeMaxPreview($previewPath); - - break 2; - } - } - - // The providers have been kind enough to give us a preview - if ($preview) { - $this->resizeAndStore($fileId); - } - } - - /** - * Defines the media icon, for the media type of the original file, as the preview - * @throws PreviewNotAvailableException - */ - private function getMimeIcon() { - $image = new \OC_Image(); - $mimeIconWebPath = \OC::$server->getMimeTypeDetector()->mimeTypeIcon($this->mimeType); - if (empty(\OC::$WEBROOT)) { - $mimeIconServerPath = \OC::$SERVERROOT . $mimeIconWebPath; - } else { - $mimeIconServerPath = str_replace(\OC::$WEBROOT, \OC::$SERVERROOT, $mimeIconWebPath); - } - // we can't load SVGs into an image - if (substr($mimeIconWebPath, -4) === '.svg') { - throw new PreviewNotAvailableException('SVG mimetype cannot be rendered'); - } - $image->loadFromFile($mimeIconServerPath); - - $this->preview = $image; - } - - /** - * Stores the max preview in the cache - * - * @param string $previewPath path to the preview - */ - private function storeMaxPreview($previewPath) { - $maxPreviewExists = false; - $preview = $this->preview; - - $allThumbnails = $this->userView->getDirectoryContent($previewPath); - // This is so that the cache doesn't need emptying when upgrading - // Can be replaced by an upgrade script... - foreach ($allThumbnails as $thumbnail) { - $name = rtrim($thumbnail['name'], '.png'); - if (strpos($name, 'max')) { - $maxPreviewExists = true; - break; - } - } - // We haven't found the max preview, so we create it - if (!$maxPreviewExists) { - $previewWidth = $preview->width(); - $previewHeight = $preview->height(); - $previewPath = $previewPath . strval($previewWidth) . '-' . strval($previewHeight); - $previewPath .= '-max.png'; - $this->userView->file_put_contents($previewPath, $preview->data()); - $this->maxPreviewWidth = $previewWidth; - $this->maxPreviewHeight = $previewHeight; - } - } - - /** - * Limits a dimension to the maximum dimension provided as argument - * - * @param int $dim - * @param int $maxDim - * @param string $dimName - * - * @return integer - */ - private function limitMaxDim($dim, $maxDim, $dimName) { - if (!is_null($maxDim)) { - if ($dim > $maxDim) { - \OCP\Util::writeLog( - 'core', $dimName . ' reduced from ' . $dim . ' to ' . $maxDim, \OCP\Util::DEBUG - ); - $dim = $maxDim; - } - } - - return $dim; - } - - /** - * @param array $args - */ - public static function post_write($args) { - self::post_delete($args, 'files/'); - } - - /** - * @param array $args - */ - public static function prepare_delete_files($args) { - self::prepare_delete($args, 'files/'); - } - - /** - * @param array $args - * @param string $prefix - */ - public static function prepare_delete(array $args, $prefix = '') { - $path = $args['path']; - if (substr($path, 0, 1) === '/') { - $path = substr($path, 1); - } - - $view = new \OC\Files\View('/' . \OC_User::getUser() . '/' . $prefix); - - $absPath = Files\Filesystem::normalizePath($view->getAbsolutePath($path)); - $fileInfo = $view->getFileInfo($path); - if ($fileInfo === false) { - return; - } - self::addPathToDeleteFileMapper($absPath, $fileInfo); - if ($view->is_dir($path)) { - $children = self::getAllChildren($view, $path); - self::$deleteChildrenMapper[$absPath] = $children; - } - } - - /** - * @param string $absolutePath - * @param \OCP\Files\FileInfo $info - */ - private static function addPathToDeleteFileMapper($absolutePath, $info) { - self::$deleteFileMapper[$absolutePath] = $info; - } - - /** - * @param \OC\Files\View $view - * @param string $path - * - * @return array - */ - private static function getAllChildren($view, $path) { - $children = $view->getDirectoryContent($path); - $childrensFiles = array(); - - $fakeRootLength = strlen($view->getRoot()); - - for ($i = 0; $i < count($children); $i++) { - $child = $children[$i]; - - $childsPath = substr($child->getPath(), $fakeRootLength); - - if ($view->is_dir($childsPath)) { - $children = array_merge( - $children, - $view->getDirectoryContent($childsPath) - ); - } else { - $childrensFiles[] = $child; - } - } - - return $childrensFiles; - } - - /** - * @param array $args - */ - public static function post_delete_files($args) { - self::post_delete($args, 'files/'); - } - - /** - * @param array $args - */ - public static function post_delete_versions($args) { - self::post_delete($args, 'files/'); - } - - /** - * @param array $args - * @param string $prefix - */ - public static function post_delete($args, $prefix = '') { - $path = Files\Filesystem::normalizePath($args['path']); - - $preview = new Preview(\OC_User::getUser(), $prefix, $path); - $preview->deleteAllPreviews(); - } - -} diff --git a/lib/private/PreviewManager.php b/lib/private/PreviewManager.php index 36b3730a720..8c5a7ad29f1 100644 --- a/lib/private/PreviewManager.php +++ b/lib/private/PreviewManager.php @@ -68,6 +68,9 @@ class PreviewManager implements IPreview { /** @var array */ protected $defaultProviders; + /** @var string */ + protected $userId; + /** * PreviewManager constructor. * @@ -75,15 +78,18 @@ class PreviewManager implements IPreview { * @param IRootFolder $rootFolder * @param IAppData $appData * @param EventDispatcherInterface $eventDispatcher + * @param string $userId */ public function __construct(IConfig $config, IRootFolder $rootFolder, IAppData $appData, - EventDispatcherInterface $eventDispatcher) { + EventDispatcherInterface $eventDispatcher, + $userId) { $this->config = $config; $this->rootFolder = $rootFolder; $this->appData = $appData; $this->eventDispatcher = $eventDispatcher; + $this->userId = $userId; } /** @@ -144,10 +150,22 @@ class PreviewManager implements IPreview { * @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image * @param boolean $scaleUp Scale smaller images up to the thumbnail size or not. Might look ugly * @return \OCP\IImage + * @deprecated 11 Use getPreview */ public function createPreview($file, $maxX = 100, $maxY = 75, $scaleUp = false) { - $preview = new \OC\Preview('', '/', $file, $maxX, $maxY, $scaleUp); - return $preview->getPreview(); + try { + $userRoot = $this->rootFolder->getUserFolder($this->userId)->getParent(); + $node = $userRoot->get($file); + if (!($file instanceof File)) { + throw new NotFoundException(); + } + + $preview = $this->getPreview($node, $maxX, $maxY); + } catch (\Exception $e) { + return new \OC_Image(); + } + + return new \OC_Image($preview->getContent()); } /** diff --git a/lib/private/Repair/RepairInvalidShares.php b/lib/private/Repair/RepairInvalidShares.php index 6cb690057bb..04624c910dd 100644 --- a/lib/private/Repair/RepairInvalidShares.php +++ b/lib/private/Repair/RepairInvalidShares.php @@ -27,6 +27,7 @@ namespace OC\Repair; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; +use Doctrine\DBAL\Platforms\OraclePlatform; /** * Repairs shares with invalid data @@ -92,6 +93,26 @@ class RepairInvalidShares implements IRepairStep { } /** + * Adjust file share permissions + */ + private function adjustFileSharePermissions(IOutput $out) { + $mask = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_SHARE; + $builder = $this->connection->getQueryBuilder(); + + $permsFunc = $builder->expr()->bitwiseAnd('permissions', $mask); + $builder + ->update('share') + ->set('permissions', $permsFunc) + ->where($builder->expr()->eq('item_type', $builder->expr()->literal('file'))) + ->andWhere($builder->expr()->neq('permissions', $permsFunc)); + + $updatedEntries = $builder->execute(); + if ($updatedEntries > 0) { + $out->info('Fixed file share permissions for ' . $updatedEntries . ' shares'); + } + } + + /** * Remove shares where the parent share does not exist anymore */ private function removeSharesNonExistingParent(IOutput $out) { @@ -137,6 +158,9 @@ class RepairInvalidShares implements IRepairStep { // this situation was only possible before 9.1 $this->addShareLinkDeletePermission($out); } + if (version_compare($ocVersionFromBeforeUpdate, '12.0.0.11', '<')) { + $this->adjustFileSharePermissions($out); + } $this->removeSharesNonExistingParent($out); } diff --git a/lib/private/Security/CertificateManager.php b/lib/private/Security/CertificateManager.php index f7bf0df58c5..461ef9457a7 100644 --- a/lib/private/Security/CertificateManager.php +++ b/lib/private/Security/CertificateManager.php @@ -30,6 +30,7 @@ namespace OC\Security; use OC\Files\Filesystem; use OCP\ICertificateManager; use OCP\IConfig; +use OCP\ILogger; /** * Manage trusted certificates for users @@ -51,14 +52,21 @@ class CertificateManager implements ICertificateManager { protected $config; /** + * @var ILogger + */ + protected $logger; + + /** * @param string $uid * @param \OC\Files\View $view relative to data/ * @param IConfig $config + * @param ILogger $logger */ - public function __construct($uid, \OC\Files\View $view, IConfig $config) { + public function __construct($uid, \OC\Files\View $view, IConfig $config, ILogger $logger) { $this->uid = $uid; $this->view = $view; $this->config = $config; + $this->logger = $logger; } /** @@ -104,6 +112,13 @@ class CertificateManager implements ICertificateManager { $this->view->mkdir($path); } + $defaultCertificates = file_get_contents(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt'); + if (strlen($defaultCertificates) < 1024) { // sanity check to verify that we have some content for our bundle + // log as exception so we have a stacktrace + $this->logger->logException(new \Exception('Shipped ca-bundle is empty, refusing to create certificate bundle')); + return; + } + $fhCerts = $this->view->fopen($path . '/rootcerts.crt', 'w'); // Write user certificates @@ -117,7 +132,6 @@ class CertificateManager implements ICertificateManager { } // Append the default certificates - $defaultCertificates = file_get_contents(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt'); fwrite($fhCerts, $defaultCertificates); // Append the system certificate bundle @@ -203,7 +217,7 @@ class CertificateManager implements ICertificateManager { } if ($this->needsRebundling($uid)) { if (is_null($uid)) { - $manager = new CertificateManager(null, $this->view, $this->config); + $manager = new CertificateManager(null, $this->view, $this->config, $this->logger); $manager->createCertificateBundle(); } else { $this->createCertificateBundle(); diff --git a/lib/private/Server.php b/lib/private/Server.php index 5bc72e3614f..d88a687bbc4 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -124,7 +124,8 @@ class Server extends ServerContainer implements IServerContainer { $c->getConfig(), $c->getRootFolder(), $c->getAppDataDir('preview'), - $c->getEventDispatcher() + $c->getEventDispatcher(), + $c->getSession()->get('user_id') ); }); @@ -418,9 +419,8 @@ class Server extends ServerContainer implements IServerContainer { ); }); $this->registerService('Logger', function (Server $c) { - $logClass = $c->query('AllConfig')->getSystemValue('log_type', 'file'); - // TODO: Drop backwards compatibility for config in the future - $logger = 'OC\\Log\\' . ucfirst($logClass=='owncloud' ? 'file' : $logClass); + $logType = $c->query('AllConfig')->getSystemValue('log_type', 'file'); + $logger = Log::getLogClass($logType); call_user_func(array($logger, 'init')); return new Log($logger); @@ -482,7 +482,7 @@ class Server extends ServerContainer implements IServerContainer { $uid = $user ? $user : null; return new ClientService( $c->getConfig(), - new \OC\Security\CertificateManager($uid, new View(), $c->getConfig()) + new \OC\Security\CertificateManager($uid, new View(), $c->getConfig(), $c->getLogger()) ); }); $this->registerService('EventLogger', function (Server $c) { @@ -898,7 +898,6 @@ class Server extends ServerContainer implements IServerContainer { return $this->query('SystemTagObjectMapper'); } - /** * Returns the avatar manager, used for avatar functionality * @@ -1221,7 +1220,7 @@ class Server extends ServerContainer implements IServerContainer { } $userId = $user->getUID(); } - return new CertificateManager($userId, new View(), $this->getConfig()); + return new CertificateManager($userId, new View(), $this->getConfig(), $this->getLogger()); } /** diff --git a/lib/private/Setup/PostgreSQL.php b/lib/private/Setup/PostgreSQL.php index 085e8609ab2..c01e5bc0332 100644 --- a/lib/private/Setup/PostgreSQL.php +++ b/lib/private/Setup/PostgreSQL.php @@ -150,14 +150,16 @@ class PostgreSQL extends AbstractDatabase { } private function createDBUser(IDBConnection $connection) { + $dbUser = $this->dbUser; try { - if ($this->userExists($connection)) { - // change the password - $query = $connection->prepare("ALTER ROLE " . addslashes($this->dbUser) . " WITH CREATEDB PASSWORD '" . addslashes($this->dbPassword) . "'"); - } else { - // create the user - $query = $connection->prepare("CREATE USER " . addslashes($this->dbUser) . " CREATEDB PASSWORD '" . addslashes($this->dbPassword) . "'"); - } + $i = 1; + while ($this->userExists($connection)) { + $i++; + $this->dbUser = $dbUser . $i; + }; + + // create the user + $query = $connection->prepare("CREATE USER " . addslashes($this->dbUser) . " CREATEDB PASSWORD '" . addslashes($this->dbPassword) . "'"); $query->execute(); } catch (DatabaseException $e) { $this->logger->error('Error while trying to create database user'); diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index 6eab5e05a2f..acc142f62be 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -587,7 +587,6 @@ class Manager implements IManager { $share->setPassword($this->hasher->hash($share->getPassword())); } } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { - $this->linkCreateChecks($share); $share->setToken( $this->secureRandom->generate( \OC\Share\Constants::TOKEN_LENGTH, diff --git a/lib/private/Template/CSSResourceLocator.php b/lib/private/Template/CSSResourceLocator.php index ffeaf765ff5..351e6d1366f 100644 --- a/lib/private/Template/CSSResourceLocator.php +++ b/lib/private/Template/CSSResourceLocator.php @@ -25,13 +25,34 @@ namespace OC\Template; +use OCP\ILogger; + class CSSResourceLocator extends ResourceLocator { + + /** @var SCSSCacher */ + protected $scssCacher; + + /** + * @param ILogger $logger + * @param string $theme + * @param array $core_map + * @param array $party_map + * @param SCSSCacher $scssCacher + */ + public function __construct(ILogger $logger, $theme, $core_map, $party_map, SCSSCacher $scssCacher) { + $this->scssCacher = $scssCacher; + + parent::__construct($logger, $theme, $core_map, $party_map); + } + /** * @param string $style */ public function doFind($style) { if (strpos($style, '3rdparty') === 0 && $this->appendIfExist($this->thirdpartyroot, $style.'.css') + || $this->cacheAndAppendScssIfExist($this->serverroot, $style.'.scss') + || $this->cacheAndAppendScssIfExist($this->serverroot, 'core/'.$style.'.scss') || $this->appendIfExist($this->serverroot, $style.'.css') || $this->appendIfExist($this->serverroot, 'core/'.$style.'.css') ) { @@ -53,4 +74,25 @@ class CSSResourceLocator extends ResourceLocator { || $this->appendIfExist($this->serverroot, $theme_dir.$style.'.css') || $this->appendIfExist($this->serverroot, $theme_dir.'core/'.$style.'.css'); } + + /** + * cache and append the scss $file if exist at $root + * + * @param string $root path to check + * @param string $file the filename + * @param string|null $webRoot base for path, default map $root to $webRoot + * @return bool True if the resource was found and cached, false otherwise + */ + protected function cacheAndAppendScssIfExist($root, $file, $webRoot = null) { + if (is_file($root.'/'.$file)) { + if($this->scssCacher->process($root, $file)) { + $this->append($root, $this->scssCacher->getCachedSCSS('core', $file), $webRoot, false); + return true; + } else { + $this->logger->error('Failed to compile and/or save '.$root.'/'.$file, ['app' => 'core']); + return false; + } + } + return false; + } } diff --git a/lib/private/Template/ResourceLocator.php b/lib/private/Template/ResourceLocator.php index 420317d27ac..420317d27ac 100644..100755 --- a/lib/private/Template/ResourceLocator.php +++ b/lib/private/Template/ResourceLocator.php diff --git a/lib/private/Template/SCSSCacher.php b/lib/private/Template/SCSSCacher.php new file mode 100644 index 00000000000..0c1711b9fb7 --- /dev/null +++ b/lib/private/Template/SCSSCacher.php @@ -0,0 +1,189 @@ +<?php +/** + * @copyright Copyright (c) 2016, John Molakvoæ (skjnldsv@protonmail.com) + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Template; + +use Leafo\ScssPhp\Compiler; +use Leafo\ScssPhp\Exception\ParserException; +use Leafo\ScssPhp\Formatter\Crunched; +use Leafo\ScssPhp\Formatter\Expanded; +use OC\SystemConfig; +use OCP\Files\IAppData; +use OCP\Files\NotFoundException; +use OCP\Files\SimpleFS\ISimpleFolder; +use OCP\ILogger; +use OCP\IURLGenerator; + +class SCSSCacher { + + /** @var ILogger */ + protected $logger; + + /** @var IAppData */ + protected $appData; + + /** @var IURLGenerator */ + protected $urlGenerator; + + /** @var SystemConfig */ + protected $systemConfig; + + /** + * @param ILogger $logger + * @param IAppData $appData + * @param IURLGenerator $urlGenerator + * @param SystemConfig $systemConfig + */ + public function __construct(ILogger $logger, IAppData $appData, IURLGenerator $urlGenerator, SystemConfig $systemConfig) { + $this->logger = $logger; + $this->appData = $appData; + $this->urlGenerator = $urlGenerator; + $this->systemConfig = $systemConfig; + } + + /** + * Process the caching process if needed + * @param string $root Root path to the nextcloud installation + * @param string $file + * @return boolean + */ + public function process($root, $file) { + $path = explode('/', $root . '/' . $file); + + $fileNameSCSS = array_pop($path); + $fileNameCSS = str_replace('.scss', '.css', $fileNameSCSS); + + $path = implode('/', $path); + + $webDir = explode('/', $file); + array_pop($webDir); + $webDir = implode('/', $webDir); + + try { + $folder = $this->appData->getFolder('core'); + } catch(NotFoundException $e) { + // creating css appdata folder + $folder = $this->appData->newFolder('core'); + } + + if($this->isCached($fileNameCSS, $fileNameSCSS, $folder, $path)) { + return true; + } else { + return $this->cache($path, $fileNameCSS, $fileNameSCSS, $folder, $webDir); + } + } + + /** + * Check if the file is cached or not + * @param string $fileNameCSS + * @param string $fileNameSCSS + * @param ISimpleFolder $folder + * @param string $path + * @return boolean + */ + private function isCached($fileNameCSS, $fileNameSCSS, ISimpleFolder $folder, $path) { + try{ + $cachedFile = $folder->getFile($fileNameCSS); + if( $cachedFile->getMTime() > filemtime($path.'/'.$fileNameSCSS) + && $cachedFile->getSize() > 0 ) { + return true; + } + } catch(NotFoundException $e) { + return false; + } + return false; + } + + /** + * Cache the file with AppData + * @param string $path + * @param string $fileNameCSS + * @param string $fileNameSCSS + * @param ISimpleFolder $folder + * @param string $webDir + * @return boolean + */ + private function cache($path, $fileNameCSS, $fileNameSCSS, ISimpleFolder $folder, $webDir) { + $scss = new Compiler(); + $scss->setImportPaths($path); + if($this->systemConfig->getValue('debug')) { + // Debug mode + $scss->setFormatter(Expanded::class); + $scss->setLineNumberStyle(Compiler::LINE_COMMENTS); + } else { + // Compression + $scss->setFormatter(Crunched::class); + } + + try { + $cachedfile = $folder->getFile($fileNameCSS); + } catch(NotFoundException $e) { + $cachedfile = $folder->newFile($fileNameCSS); + } + + // Compile + try { + $compiledScss = $scss->compile('@import "'.$fileNameSCSS.'";'); + } catch(ParserException $e) { + $this->logger->error($e, ['app' => 'core']); + return false; + } + + try { + $cachedfile->putContent($this->rebaseUrls($compiledScss, $webDir)); + $this->logger->debug($webDir.'/'.$fileNameSCSS.' compiled and successfully cached', ['app' => 'core']); + return true; + } catch(NotFoundException $e) { + return false; + } + } + + /** + * Add the correct uri prefix to make uri valid again + * @param string $css + * @param string $webDir + * @return string + */ + private function rebaseUrls($css, $webDir) { + $re = '/url\([\'"]([\.\w?=\/-]*)[\'"]\)/x'; + // OC\Route\Router:75 + if(($this->systemConfig->getValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true')) { + $subst = 'url(\'../../'.$webDir.'/$1\')'; + } else { + $subst = 'url(\'../../../'.$webDir.'/$1\')'; + } + return preg_replace($re, $subst, $css); + } + + /** + * Return the cached css file uri + * @param string $appName the app name + * @param string $fileName + * @return string + */ + public function getCachedSCSS($appName, $fileName) { + $tmpfileLoc = explode('/', $fileName); + $fileName = array_pop($tmpfileLoc); + $fileName = str_replace('.scss', '.css', $fileName); + + return substr($this->urlGenerator->linkToRoute('core.Css.getCss', array('fileName' => $fileName, 'appName' => $appName)), strlen(\OC::$WEBROOT) + 1); + } +} diff --git a/lib/private/TemplateLayout.php b/lib/private/TemplateLayout.php index ad9f8bac0e4..bc6a485ad43 100644 --- a/lib/private/TemplateLayout.php +++ b/lib/private/TemplateLayout.php @@ -36,6 +36,7 @@ namespace OC; use OC\Template\JSConfigHelper; +use OC\Template\SCSSCacher; class TemplateLayout extends \OC_Template { @@ -159,8 +160,17 @@ class TemplateLayout extends \OC_Template { $this->append( 'jsfiles', $web.'/'.$file . '?v=' . self::$versionHash); } - // Add the css files - $cssFiles = self::findStylesheetFiles(\OC_Util::$styles); + // Add the css files and check if server is already installed to prevent + // appdata initialisation before database configuration + if(\OC::$server->getSystemConfig()->getValue('installed', false)) { + $cssFiles = self::findStylesheetFiles(\OC_Util::$styles); + } else { + $cssFiles = array( + [\OC::$SERVERROOT, \OC::$WEBROOT, 'core/css/global.css'], + [\OC::$SERVERROOT, \OC::$WEBROOT, 'core/css/fonts.css'], + [\OC::$SERVERROOT, \OC::$WEBROOT, 'core/css/installation.css'] + ); + } $this->assign('cssfiles', array()); $this->assign('printcssfiles', []); $this->assign('versionHash', self::$versionHash); @@ -184,11 +194,19 @@ class TemplateLayout extends \OC_Template { // Read the selected theme from the config file $theme = \OC_Util::getTheme(); + $SCSSCacher = new SCSSCacher( + \OC::$server->getLogger(), + \OC::$server->getAppDataDir('css'), + \OC::$server->getURLGenerator(), + \OC::$server->getSystemConfig() + ); + $locator = new \OC\Template\CSSResourceLocator( \OC::$server->getLogger(), $theme, array( \OC::$SERVERROOT => \OC::$WEBROOT ), - array( \OC::$SERVERROOT => \OC::$WEBROOT )); + array( \OC::$SERVERROOT => \OC::$WEBROOT ), + $SCSSCacher); $locator->find($styles); return $locator->getResources(); } diff --git a/lib/private/User/Database.php b/lib/private/User/Database.php index 28cb3302858..69826f49be3 100644 --- a/lib/private/User/Database.php +++ b/lib/private/User/Database.php @@ -75,6 +75,7 @@ class Database extends Backend implements IUserBackend { */ public function __construct($eventDispatcher = null) { $this->cache = new CappedMemoryCache(); + $this->cache[null] = false; $this->eventDispatcher = $eventDispatcher ? $eventDispatcher : \OC::$server->getEventDispatcher(); } diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php index dcda825b9db..1834bd025d1 100644 --- a/lib/private/User/Session.php +++ b/lib/private/User/Session.php @@ -743,6 +743,7 @@ class Session implements IUserSession, Emitter { //login $this->setUser($user); + $this->setLoginName($this->tokenProvider->getToken($sessionId)->getLoginName()); $user->updateLastLoginTimestamp(); $this->manager->emit('\OC\User', 'postRememberedLogin', [$user]); return true; diff --git a/lib/private/legacy/app.php b/lib/private/legacy/app.php index adf29601ac6..8ef7e08c733 100644 --- a/lib/private/legacy/app.php +++ b/lib/private/legacy/app.php @@ -273,9 +273,17 @@ class OC_App { $appTypes = implode(',', $appData['types']); } else { $appTypes = ''; + $appData['types'] = []; } \OC::$server->getAppConfig()->setValue($app, 'types', $appTypes); + + if (\OC::$server->getAppManager()->hasProtectedAppType($appData['types'])) { + $enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'yes'); + if ($enabled !== 'yes' && $enabled !== 'no') { + \OC::$server->getAppConfig()->setValue($app, 'enabled', 'yes'); + } + } } /** diff --git a/lib/public/App/IAppManager.php b/lib/public/App/IAppManager.php index 057a964ce0a..72c99777124 100644 --- a/lib/public/App/IAppManager.php +++ b/lib/public/App/IAppManager.php @@ -62,6 +62,15 @@ interface IAppManager { public function enableApp($appId); /** + * Whether a list of types contains a protected app type + * + * @param string[] $types + * @return bool + * @since 12.0.0 + */ + public function hasProtectedAppType($types); + + /** * Enable an app only for specific groups * * @param string $appId diff --git a/lib/public/AppFramework/Http/Response.php b/lib/public/AppFramework/Http/Response.php index 8591d6abc68..051e68f3144 100644 --- a/lib/public/AppFramework/Http/Response.php +++ b/lib/public/AppFramework/Http/Response.php @@ -43,11 +43,11 @@ use OCP\AppFramework\Http; class Response { /** - * Headers - defaults to ['Cache-Control' => 'no-cache, must-revalidate'] + * Headers - defaults to ['Cache-Control' => 'no-cache, no-store, must-revalidate'] * @var array */ private $headers = array( - 'Cache-Control' => 'no-cache, must-revalidate' + 'Cache-Control' => 'no-cache, no-store, must-revalidate' ); diff --git a/lib/public/DB/QueryBuilder/IExpressionBuilder.php b/lib/public/DB/QueryBuilder/IExpressionBuilder.php index 8164c738ca5..c123875b803 100644 --- a/lib/public/DB/QueryBuilder/IExpressionBuilder.php +++ b/lib/public/DB/QueryBuilder/IExpressionBuilder.php @@ -305,6 +305,27 @@ interface IExpressionBuilder { */ public function notIn($x, $y, $type = null); + + /** + * Creates a bitwise AND comparison + * + * @param string|ILiteral $x The field or value to check + * @param int $y Bitmap that must be set + * @return IQueryFunction + * @since 12.0.0 + */ + public function bitwiseAnd($x, $y); + + /** + * Creates a bitwise OR comparison + * + * @param string|ILiteral $x The field or value to check + * @param int $y Bitmap that must be set + * @return IQueryFunction + * @since 12.0.0 + */ + public function bitwiseOr($x, $y); + /** * Quotes a given input parameter. * diff --git a/lib/public/IPreview.php b/lib/public/IPreview.php index a1a03fee3e1..207539b1170 100644 --- a/lib/public/IPreview.php +++ b/lib/public/IPreview.php @@ -86,7 +86,7 @@ interface IPreview { * @param boolean $scaleUp Scale smaller images up to the thumbnail size or not. Might look ugly * @return \OCP\IImage * @since 6.0.0 - * @deprecated 9.2.0 Use getPreview + * @deprecated 11 Use getPreview */ public function createPreview($file, $maxX = 100, $maxY = 75, $scaleUp = false); diff --git a/resources/config/mimetypemapping.dist.json b/resources/config/mimetypemapping.dist.json index a49a04e43f2..99638b6c02d 100644 --- a/resources/config/mimetypemapping.dist.json +++ b/resources/config/mimetypemapping.dist.json @@ -78,6 +78,7 @@ "key": ["application/x-iwork-keynote-sffkey"], "keynote": ["application/x-iwork-keynote-sffkey"], "kra": ["application/x-krita"], + "ldif": ["text/x-ldif"], "lwp": ["application/vnd.lotus-wordpro"], "m2t": ["video/mp2t"], "m4a": ["audio/mp4"], @@ -142,6 +143,7 @@ "rss": ["application/rss+xml"], "rtf": ["text/rtf"], "rw2": ["image/x-dcraw"], + "schema": ["text/plain"], "sgf": ["application/sgf"], "sh-lib": ["text/x-shellscript"], "sh": ["text/x-shellscript"], diff --git a/settings/l10n/sk_SK.js b/settings/l10n/sk_SK.js index 398a30ee9b0..6af204caaa9 100644 --- a/settings/l10n/sk_SK.js +++ b/settings/l10n/sk_SK.js @@ -27,39 +27,79 @@ OC.L10N.register( "Unable to create user." : "Nie je možné vytvoriť používateľa.", "Your %s account was created" : "Váš účet %s bol vytvorený", "Unable to delete user." : "Nie je možné zmazať používateľa.", + "Settings saved" : "Nastavenia uložené", "Unable to change full name" : "Nemožno zmeniť meno a priezvisko", + "Unable to change email address" : "Nemožno zmeniť emailovú adresu", "Your full name has been changed." : "Vaše meno a priezvisko bolo zmenené.", "Forbidden" : "Zakázané", "Invalid user" : "Neplatný používateľ", "Unable to change mail address" : "Nemožno zmeniť emailovú adresu", "Email saved" : "Email uložený", + "Password confirmation is required" : "Vyžaduje sa overenie heslom", "Couldn't remove app." : "Nemožno odstrániť aplikáciu.", "Admins can't remove themself from the admin group" : "Administrátori nesmú odstrániť sami seba zo skupiny admin", "Unable to add user to group %s" : "Nie je možné pridať používateľa do skupiny %s", "Unable to remove user from group %s" : "Nie je možné odstrániť používateľa zo skupiny %s", "Couldn't update app." : "Nemožno aktualizovať aplikáciu.", + "Are you really sure you want add {domain} as trusted domain?" : "Ste si istí, že chcete pridať {domain} medzi dôveryhodné domény?", "Add trusted domain" : "Pridať dôveryhodnú doménu", "Migration in progress. Please wait until the migration is finished" : "Prebieha migrácia. Počkajte prosím, kým sa skončí", "Migration started …" : "Migrácia spustená ...", + "Not saved" : "Neuložené", "Sending..." : "Odosielam...", "Official" : "Oficiálny", "All" : "Všetky", "Update to %s" : "Aktualizovať na %s", + "_You have %n app update pending_::_You have %n app updates pending_" : ["Prebieha aktualizácia %n aplikácie","Prebieha aktualizácia %n aplikácií","Prebieha aktualizácia %n aplikácií"], "No apps found for your version" : "Aplikácie pre vašu verziu sa nenašli", + "This app is not checked for security issues and is new or known to be unstable. Install at your own risk." : "Táto aplikácia nie je skontrolovaná na bezpečnostné chyby, je nová, alebo patrí medzi nestabilné. Inštalácia na vlastné riziko.", + "Enabling app …" : "Povoľujem aplikáciu …", "Error while disabling app" : "Chyba pri zakázaní aplikácie", "Disable" : "Zakázať", "Enable" : "Zapnúť", "Error while enabling app" : "Chyba pri povoľovaní aplikácie", + "Error: this app cannot be enabled because it makes the server unstable" : "Chyba: aplikáciu nie je možné povoliť, lebo naruší stabilitu servera", + "Error: could not disable broken app" : "Chyba: nebolo možné zakázať poškodenú aplikáciu", + "Error while disabling broken app" : "Nastala chyba počas zakazovania poškodenej aplikácie", "Updating...." : "Aktualizujem...", "Error while updating app" : "chyba pri aktualizácii aplikácie", "Updated" : "Aktualizované", "Uninstalling ...." : "Prebieha odinštalovanie...", "Error while uninstalling app" : "Chyba pri odinštalovaní aplikácie", "Uninstall" : "Odinštalácia", + "The app has been enabled but needs to be updated. You will be redirected to the update page in 5 seconds." : "Aplikácia bola povolená, ale je potrebné ju aktualizovať. O 5 sekúnd budete presmerovaní na aktualizačnú stránku.", + "App update" : "Aktualizácia aplikácie", "Approved" : "Schválené", "Experimental" : "Experimentálny", + "No apps found for {query}" : "Žiadna aplikácia nebola nájdená pre {query}", + "Allow filesystem access" : "Povoliť prístup ku súborovému systému", + "Disconnect" : "Odpojiť", + "Revoke" : "Odvolať", + "Internet Explorer" : "Internet Explorer", + "Firefox" : "Firefox", + "Google Chrome" : "Google Chrome", + "Safari" : "Safari", + "Google Chrome for Android" : "Google Chrome pre Android", + "iPhone iOS" : "iPhone iOS", + "iPad iOS" : "iPad iOS", + "iOS Client" : "iOS Klient", + "Android Client" : "Android Klient", + "Copy" : "Kopírovať", + "Copied!" : "Skopírované!", + "Not supported!" : "Nie je podporované!", + "Press ⌘-C to copy." : "Stlač ⌘-C pre skopírovanie.", + "Press Ctrl-C to copy." : "Stlač Ctrl-C pre skopírovanie.", + "An error occurred. Please upload an ASCII-encoded PEM certificate." : "Vyskytla sa chyba. Nahrajte prosím PEM certifikát v ASCII kódovaní.", "Valid until {date}" : "Platný do {date}", "Delete" : "Zmazať", + "Local" : "Lokálny", + "Private" : "Súkromný", + "Only visible to local users" : "Viditeľné iba pre lokálnych používateľov", + "Only visible to you" : "Viditeľné iba pre seba", + "Contacts" : "Kontakty", + "Visible to local users and to trusted servers" : "Viditeľné pre lokálnych používateľov a dôveryhodné servery", + "Public" : "Verejné", + "Will be synced to a global and public address book" : "Bude synchronizované s globálnym a verejným adresárom", "Select a profile picture" : "Vybrať avatara", "Very weak password" : "Veľmi slabé heslo", "Weak password" : "Slabé heslo", @@ -68,23 +108,32 @@ OC.L10N.register( "Strong password" : "Silné heslo", "Groups" : "Skupiny", "Unable to delete {objName}" : "Nemožno vymazať {objName}", + "Error creating group: {message}" : "Chyba pri vytváraní skupiny: {message}", "A valid group name must be provided" : "Musíte zadať platný názov skupiny", "deleted {groupName}" : "vymazaná {groupName}", "undo" : "vrátiť", "never" : "nikdy", "deleted {userName}" : "vymazané {userName}", + "Add group" : "Pridať skupinu", + "Invalid quota value \"{val}\"" : "Neplatná hodnota kvóty \"{val}\"", + "Password successfully changed" : "Heslo úspešne zmenené", "Changing the password will result in data loss, because data recovery is not available for this user" : "Zmena hesla bude mať za následok stratu dát, pretože obnova dát nie je k dispozícii pre tohto používateľa", + "Could not change the users email" : "Nemožno zmeniť email používateľa", "A valid username must be provided" : "Musíte zadať platné používateľské meno", + "Error creating user: {message}" : "Chyba pri vytváraní používateľa: {message}", "A valid password must be provided" : "Musíte zadať platné heslo", "A valid email must be provided" : "Musíte zadať platnú emailovú adresu", "__language_name__" : "Slovensky", "Unlimited" : "Nelimitované", "Personal info" : "Osobné informácie", + "App passwords" : "Heslá aplikácie", "Sync clients" : "Klienti synchronizácie", "None" : "Žiadny", "Login" : "Prihlásenie", "Plain" : "Neformátovaný", "NT LAN Manager" : "NT LAN Manager", + "SSL/TLS" : "SSL/TLS", + "STARTTLS" : "STARTTLS", "Email server" : "Email server", "Open documentation" : "Otvoriť dokumentáciu", "This is used for sending out notifications." : "Používa sa na odosielanie upozornení.", @@ -104,7 +153,12 @@ OC.L10N.register( "Send email" : "Odoslať email", "Server-side encryption" : "Šifrovanie na serveri", "Enable server-side encryption" : "Povoliť šifrovanie na serveri", + "Please read carefully before activating server-side encryption: " : "Prečítajte prosím pozorne pred použitím šifrovania na serveri:", + "Be aware that encryption always increases the file size." : "Majte na vedomí, že šifrovanie vždy zväčší veľkosť súborov.", + "It is always good to create regular backups of your data, in case of encryption make sure to backup the encryption keys along with your data." : "Je dobré vytvárať pravidelné zálohy vašich dát, uistite sa, že v prípade šifrovania spolu s vašimi dátami zálohujete aj šifrovacie kľúče.", + "This is the final warning: Do you really want to enable encryption?" : "Toto je posledné varovanie: Vážne si prajete povoliť šifrovanie?", "Enable encryption" : "Povoliť šifrovanie", + "No encryption module loaded, please enable an encryption module in the app menu." : "Nebol načítaný žiadny šifrovací modul, povoľte prosím šifrovací modul v menu aplikácií.", "Select default encryption module:" : "Vybrať predvolený šifrovací modul:", "Start migration" : "Začať migráciu", "Security & setup warnings" : "Bezpečnosť a nastavenia upozornení", @@ -116,6 +170,7 @@ OC.L10N.register( "This means that there might be problems with certain characters in file names." : "To znamená, že sa môžu vyskytnúť problémy s niektorými znakmi v názvoch súborov.", "We strongly suggest installing the required packages on your system to support one of the following locales: %s." : "Dôrazne doporučujeme nainštalovať na váš systém požadované balíčky podporujúce jednu z nasledovných znakových sád: %s.", "If your installation is not installed in the root of the domain and uses system cron, there can be issues with the URL generation. To avoid these problems, please set the \"overwrite.cli.url\" option in your config.php file to the webroot path of your installation (Suggested: \"%s\")" : "Inštalácia mimo koreňový priečinok domény a používanie systémového príkazu cron môže spôsobiť problém s generovaním správnej URL. Pre zabránenie týmto chybám nastavte prosím správnu cestu v svojom config.php súbore pre hodnotu \"overwrite.cli.url\" (Doporučujeme: \"%s\")", + "All checks passed." : "Všetky kontroly prešli úspešne.", "Cron" : "Cron", "Last cron job execution: %s." : "Posledný cron prebehol: %s.", "Last cron job execution: %s. Something seems wrong." : "Posledný cron prebehol: %s. Zdá sa, že niečo nie je vporiadku.", @@ -123,6 +178,7 @@ OC.L10N.register( "Execute one task with each page loaded" : "Vykonať jednu úlohu s každým načítaní stránky", "cron.php is registered at a webcron service to call cron.php every 15 minutes over http." : "cron.php je zaregistrovaná v službe WebCron a zavolá cron.php každých 15 minút cez http.", "Use system's cron service to call the cron.php file every 15 minutes." : "Použiť systémovú službu cron na spúšťanie súboru cron.php každých 15 minút.", + "The cron.php needs to be executed by the system user \"%s\"." : "Je potrebné, aby cron.php bol spustený systémovým používateľom \"%s\".", "Version" : "Verzia", "Sharing" : "Sprístupňovanie", "Allow apps to use the Share API" : "Povoliť aplikáciám používať API pre sprístupňovanie", @@ -134,6 +190,7 @@ OC.L10N.register( "days" : "dni", "Enforce expiration date" : "Vynútiť dátum expirácie", "Allow resharing" : "Povoliť sprístupňovanie ďalej", + "Allow sharing with groups" : "Povoliť sprístupnenie so skupinami", "Restrict users to only share with users in their groups" : "Povoliť používateľom sprístupňovanie obsahu len v rámci ich skupiny", "Exclude groups from sharing" : "Nesprístupniť obsah skupinám", "These groups will still be able to receive shares, but not to initiate them." : "Tieto skupiny nebudú mocť sprístupňovať obsah, môžu však stále čítať sprístupnené súbory", @@ -145,32 +202,56 @@ OC.L10N.register( "Theming" : "Vzhľady tém", "Hardening and security guidance" : "Sprievodca vylepšením bezpečnosti", "Developer documentation" : "Dokumentácia vývojára", + "by %s" : "od %s", + "%s-licensed" : "%s-licencovaný", "Documentation:" : "Dokumentácia:", "User documentation" : "Príručka používateľa", "Admin documentation" : "Príručka administrátora", + "Visit website" : "Navštíviť webstránku", + "Report a bug" : "Nahlásiť chybu", "Show description …" : "Zobraziť popis …", "Hide description …" : "Skryť popis …", + "This app has an update available." : "Pre túto aplikáciu je dostupná aktualizácia.", + "This app has no minimum Nextcloud version assigned. This will be an error in the future." : "Pre túto aplikáciu nie je zadaná minimálna verzia Nextcloudu. Toto v budúcnosti spôsobí chybu.", + "This app has no maximum Nextcloud version assigned. This will be an error in the future." : "Pre túto aplikáciu nie je zadaná maximálna verzia Nextcloudu. Toto v budúcnosti spôsobí chybu.", "This app cannot be installed because the following dependencies are not fulfilled:" : "Túto aplikáciu nemožno nainštalovať, pretože nie sú splnené nasledovné závislosti:", "Enable only for specific groups" : "Povoliť len pre vybrané skupiny", + "Uninstall app" : "Odinštalovať aplikáciu", + "SSL Root Certificates" : "Koreňové certifikáty SSL", "Common Name" : "Bežný názov", "Valid until" : "Platný do", "Issued By" : "Vydal", "Valid until %s" : "Platný do %s", "Import root certificate" : "Importovať koreňový certifikát", + "Hey there,<br><br>just letting you know that you now have a %s account.<br><br>Your username: <strong>%s</strong><br>Access it: <strong><a href=\"%s\">%s</a></strong><br><br>" : "Dobrý deň,<br><br>toto je oznámenie o novo vytvorenom účte %s.<br><br>Vaše používateľské meno: <strong>%s</strong><br>Prihlásiť sa môžete tu: <strong><a href=\"%s\">%s</a></strong><br><br>", "Cheers!" : "Pekný deň!", "Administrator documentation" : "Príručka administrátora", "Online documentation" : "Online príručka", "Forum" : "Fórum", + "Getting help" : "Získať pomoc", "Commercial support" : "Komerčná podpora", + "You are using <strong>%s</strong> of <strong>%s</strong>" : "Využívate <strong>%s</strong> z <strong>%s</strong>", "Profile picture" : "Avatar", "Upload new" : "Nahrať nový", + "Select from Files" : "Vybrať zo súborov", "Remove image" : "Zmazať obrázok", + "png or jpg, max. 20 MB" : "png alebo jpg, max. 20 MB", + "Picture provided by original account" : "Obrázok poskytnutý originálnym účtom", "Cancel" : "Zrušiť", + "Choose as profile picture" : "Použiť ako obrázok avatara", "Full name" : "Meno a priezvisko", "No display name set" : "Zobrazované meno nie je nastavené", "Email" : "Email", "Your email address" : "Vaša emailová adresa", "No email address set" : "Emailová adresa nie je nastavená", + "For password recovery and notifications" : "Pre obnovu hesla a oznámenia", + "Phone number" : "Telefónne číslo", + "Your phone number" : "Vaše telefónne číslo", + "Address" : "Adresa", + "Your postal address" : "Vaša poštová adresa", + "Website" : "Webstránka", + "Your website" : "Vaša webstránka", + "Twitter" : "Twitter", "You are member of the following groups:" : "Ste členom nasledovných skupín:", "Password" : "Heslo", "Current password" : "Aktuálne heslo", @@ -182,10 +263,20 @@ OC.L10N.register( "Desktop client" : "Desktopový klient", "Android app" : "Android aplikácia", "iOS app" : "iOS aplikácia", + "If you want to support the project {contributeopen}join development{linkclose} or {contributeopen}spread the word{linkclose}!" : "Ak chcete podporiť projekt {contributeopen}zapojte sa do vývoja{linkclose}, alebo {contributeopen}dajte o nás vedieť{linkclose}!", "Show First Run Wizard again" : "Znovu zobraziť sprievodcu prvým spustením", + "Web, desktop and mobile clients currently logged in to your account." : "Weboví, desktopoví, alebo mobilní klienti práve prihlásení na váš účet.", + "Device" : "Zariadenie", + "Last activity" : "Posledná aktivita", + "Passcodes that give an app or device permissions to access your account." : "Prístupové heslá, ktoré dovolia aplikáciam alebo zariadeniam prístup na váš účet.", "Name" : "Názov", + "App name" : "Názov aplikácie", + "Create new app password" : "Vytvoriť nové heslo aplikácie", + "Use the credentials below to configure your app or device." : "Pre konfiguráciu vašej aplikácie, alebo zariadenia použite nižšie uvedené prihlasovacie údaje.", + "For security reasons this password will only be shown once." : "Z dôvodu bezpečnosti toto heslo bude zobrazené iba jeden krát.", "Username" : "Používateľské meno", "Done" : "Hotovo", + "Follow us on Google Plus!" : "Sleduj nás na Google Plus!", "Show storage location" : "Zobraziť umiestnenie úložiska", "Show last log in" : "Zobraziť posledné prihlásenie", "Show user backend" : "Zobraziť backend používateľa", @@ -195,11 +286,17 @@ OC.L10N.register( "Create" : "Vytvoriť", "Admin Recovery Password" : "Obnovenie hesla administrátora", "Enter the recovery password in order to recover the users files during password change" : "Zadajte heslo pre obnovenie súborov používateľa pri zmene hesla", + "Group name" : "Názov skupiny", "Everyone" : "Všetci", "Admins" : "Administrátori", + "Default quota" : "Predvolená kvóta", "Please enter storage quota (ex: \"512 MB\" or \"12 GB\")" : "Prosím zadajte kvótu úložného priestoru (napr.: \"512 MB\" alebo \"12 GB\")", "Other" : "Iné", + "Group admin for" : "Administrátor skupiny pre", "Quota" : "Kvóta", + "Storage location" : "Umiestnenie úložiska", + "User backend" : "Backend používateľa", + "Last login" : "Posledné prihlásenie", "change full name" : "zmeniť meno a priezvisko", "set new password" : "nastaviť nové heslo", "change email address" : "zmeniť emailovú adresu", @@ -208,12 +305,15 @@ OC.L10N.register( "Language changed" : "Jazyk zmenený", "Are you really sure you want add \"{domain}\" as trusted domain?" : "Ste si istí, že chcete pridať \"{domain}\" medzi dôveryhodné domény?", "Please wait...." : "Čakajte prosím...", + "iPhone" : "iPhone", + "add group" : "pridať skupinu", "Everything (fatal issues, errors, warnings, info, debug)" : "Všetko (fatálne problémy, chyby, upozornenia, info, debug)", "Info, warnings, errors and fatal issues" : "Info, upozornenia, chyby a fatálne problémy", "Warnings, errors and fatal issues" : "Upozornenia, chyby a fatálne problémy", "Errors and fatal issues" : "Chyby a fatálne problémy", "Fatal issues only" : "Len fatálne problémy", "Log" : "Záznam", + "What to log" : "Čo sa má zaznamenávať", "Download logfile" : "Stiahnuť súbor logu", "More" : "Viac", "Less" : "Menej", @@ -228,6 +328,13 @@ OC.L10N.register( "Enable experimental apps" : "Povoliť experimentálne aplikácie", "Hey there,<br><br>just letting you know that you now have an %s account.<br><br>Your username: %s<br>Access it: <a href=\"%s\">%s</a><br><br>" : "Dobrý deň,<br><br>toto je oznámenie o novo vytvorenom účte %s.<br><br>Vaše používateľské meno: %s<br>Prihlásiť sa môžete tu: <a href=\"%s\">%s</a><br><br>", "Hey there,\n\njust letting you know that you now have an %s account.\n\nYour username: %s\nAccess it: %s\n\n" : "Ahoj,\n\ntoto je oznámenie o novo vytvorenom účte %s.\n\nVaše používateľské meno: %s\nPrihlásiť sa môžete tu: %s\n\n", - "Group" : "Skupina" + "Add Group" : "Pridať skupinu", + "Group" : "Skupina", + "Default Quota" : "Predvolená kvóta", + "Full Name" : "Meno a priezvisko", + "Group Admin for" : "Administrátor skupiny pre", + "Storage Location" : "Umiestnenie úložiska", + "User Backend" : "Backend používateľa", + "Last Login" : "Posledné prihlásenie" }, "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"); diff --git a/settings/l10n/sk_SK.json b/settings/l10n/sk_SK.json index 1545d16d931..faf66bbaca6 100644 --- a/settings/l10n/sk_SK.json +++ b/settings/l10n/sk_SK.json @@ -25,39 +25,79 @@ "Unable to create user." : "Nie je možné vytvoriť používateľa.", "Your %s account was created" : "Váš účet %s bol vytvorený", "Unable to delete user." : "Nie je možné zmazať používateľa.", + "Settings saved" : "Nastavenia uložené", "Unable to change full name" : "Nemožno zmeniť meno a priezvisko", + "Unable to change email address" : "Nemožno zmeniť emailovú adresu", "Your full name has been changed." : "Vaše meno a priezvisko bolo zmenené.", "Forbidden" : "Zakázané", "Invalid user" : "Neplatný používateľ", "Unable to change mail address" : "Nemožno zmeniť emailovú adresu", "Email saved" : "Email uložený", + "Password confirmation is required" : "Vyžaduje sa overenie heslom", "Couldn't remove app." : "Nemožno odstrániť aplikáciu.", "Admins can't remove themself from the admin group" : "Administrátori nesmú odstrániť sami seba zo skupiny admin", "Unable to add user to group %s" : "Nie je možné pridať používateľa do skupiny %s", "Unable to remove user from group %s" : "Nie je možné odstrániť používateľa zo skupiny %s", "Couldn't update app." : "Nemožno aktualizovať aplikáciu.", + "Are you really sure you want add {domain} as trusted domain?" : "Ste si istí, že chcete pridať {domain} medzi dôveryhodné domény?", "Add trusted domain" : "Pridať dôveryhodnú doménu", "Migration in progress. Please wait until the migration is finished" : "Prebieha migrácia. Počkajte prosím, kým sa skončí", "Migration started …" : "Migrácia spustená ...", + "Not saved" : "Neuložené", "Sending..." : "Odosielam...", "Official" : "Oficiálny", "All" : "Všetky", "Update to %s" : "Aktualizovať na %s", + "_You have %n app update pending_::_You have %n app updates pending_" : ["Prebieha aktualizácia %n aplikácie","Prebieha aktualizácia %n aplikácií","Prebieha aktualizácia %n aplikácií"], "No apps found for your version" : "Aplikácie pre vašu verziu sa nenašli", + "This app is not checked for security issues and is new or known to be unstable. Install at your own risk." : "Táto aplikácia nie je skontrolovaná na bezpečnostné chyby, je nová, alebo patrí medzi nestabilné. Inštalácia na vlastné riziko.", + "Enabling app …" : "Povoľujem aplikáciu …", "Error while disabling app" : "Chyba pri zakázaní aplikácie", "Disable" : "Zakázať", "Enable" : "Zapnúť", "Error while enabling app" : "Chyba pri povoľovaní aplikácie", + "Error: this app cannot be enabled because it makes the server unstable" : "Chyba: aplikáciu nie je možné povoliť, lebo naruší stabilitu servera", + "Error: could not disable broken app" : "Chyba: nebolo možné zakázať poškodenú aplikáciu", + "Error while disabling broken app" : "Nastala chyba počas zakazovania poškodenej aplikácie", "Updating...." : "Aktualizujem...", "Error while updating app" : "chyba pri aktualizácii aplikácie", "Updated" : "Aktualizované", "Uninstalling ...." : "Prebieha odinštalovanie...", "Error while uninstalling app" : "Chyba pri odinštalovaní aplikácie", "Uninstall" : "Odinštalácia", + "The app has been enabled but needs to be updated. You will be redirected to the update page in 5 seconds." : "Aplikácia bola povolená, ale je potrebné ju aktualizovať. O 5 sekúnd budete presmerovaní na aktualizačnú stránku.", + "App update" : "Aktualizácia aplikácie", "Approved" : "Schválené", "Experimental" : "Experimentálny", + "No apps found for {query}" : "Žiadna aplikácia nebola nájdená pre {query}", + "Allow filesystem access" : "Povoliť prístup ku súborovému systému", + "Disconnect" : "Odpojiť", + "Revoke" : "Odvolať", + "Internet Explorer" : "Internet Explorer", + "Firefox" : "Firefox", + "Google Chrome" : "Google Chrome", + "Safari" : "Safari", + "Google Chrome for Android" : "Google Chrome pre Android", + "iPhone iOS" : "iPhone iOS", + "iPad iOS" : "iPad iOS", + "iOS Client" : "iOS Klient", + "Android Client" : "Android Klient", + "Copy" : "Kopírovať", + "Copied!" : "Skopírované!", + "Not supported!" : "Nie je podporované!", + "Press ⌘-C to copy." : "Stlač ⌘-C pre skopírovanie.", + "Press Ctrl-C to copy." : "Stlač Ctrl-C pre skopírovanie.", + "An error occurred. Please upload an ASCII-encoded PEM certificate." : "Vyskytla sa chyba. Nahrajte prosím PEM certifikát v ASCII kódovaní.", "Valid until {date}" : "Platný do {date}", "Delete" : "Zmazať", + "Local" : "Lokálny", + "Private" : "Súkromný", + "Only visible to local users" : "Viditeľné iba pre lokálnych používateľov", + "Only visible to you" : "Viditeľné iba pre seba", + "Contacts" : "Kontakty", + "Visible to local users and to trusted servers" : "Viditeľné pre lokálnych používateľov a dôveryhodné servery", + "Public" : "Verejné", + "Will be synced to a global and public address book" : "Bude synchronizované s globálnym a verejným adresárom", "Select a profile picture" : "Vybrať avatara", "Very weak password" : "Veľmi slabé heslo", "Weak password" : "Slabé heslo", @@ -66,23 +106,32 @@ "Strong password" : "Silné heslo", "Groups" : "Skupiny", "Unable to delete {objName}" : "Nemožno vymazať {objName}", + "Error creating group: {message}" : "Chyba pri vytváraní skupiny: {message}", "A valid group name must be provided" : "Musíte zadať platný názov skupiny", "deleted {groupName}" : "vymazaná {groupName}", "undo" : "vrátiť", "never" : "nikdy", "deleted {userName}" : "vymazané {userName}", + "Add group" : "Pridať skupinu", + "Invalid quota value \"{val}\"" : "Neplatná hodnota kvóty \"{val}\"", + "Password successfully changed" : "Heslo úspešne zmenené", "Changing the password will result in data loss, because data recovery is not available for this user" : "Zmena hesla bude mať za následok stratu dát, pretože obnova dát nie je k dispozícii pre tohto používateľa", + "Could not change the users email" : "Nemožno zmeniť email používateľa", "A valid username must be provided" : "Musíte zadať platné používateľské meno", + "Error creating user: {message}" : "Chyba pri vytváraní používateľa: {message}", "A valid password must be provided" : "Musíte zadať platné heslo", "A valid email must be provided" : "Musíte zadať platnú emailovú adresu", "__language_name__" : "Slovensky", "Unlimited" : "Nelimitované", "Personal info" : "Osobné informácie", + "App passwords" : "Heslá aplikácie", "Sync clients" : "Klienti synchronizácie", "None" : "Žiadny", "Login" : "Prihlásenie", "Plain" : "Neformátovaný", "NT LAN Manager" : "NT LAN Manager", + "SSL/TLS" : "SSL/TLS", + "STARTTLS" : "STARTTLS", "Email server" : "Email server", "Open documentation" : "Otvoriť dokumentáciu", "This is used for sending out notifications." : "Používa sa na odosielanie upozornení.", @@ -102,7 +151,12 @@ "Send email" : "Odoslať email", "Server-side encryption" : "Šifrovanie na serveri", "Enable server-side encryption" : "Povoliť šifrovanie na serveri", + "Please read carefully before activating server-side encryption: " : "Prečítajte prosím pozorne pred použitím šifrovania na serveri:", + "Be aware that encryption always increases the file size." : "Majte na vedomí, že šifrovanie vždy zväčší veľkosť súborov.", + "It is always good to create regular backups of your data, in case of encryption make sure to backup the encryption keys along with your data." : "Je dobré vytvárať pravidelné zálohy vašich dát, uistite sa, že v prípade šifrovania spolu s vašimi dátami zálohujete aj šifrovacie kľúče.", + "This is the final warning: Do you really want to enable encryption?" : "Toto je posledné varovanie: Vážne si prajete povoliť šifrovanie?", "Enable encryption" : "Povoliť šifrovanie", + "No encryption module loaded, please enable an encryption module in the app menu." : "Nebol načítaný žiadny šifrovací modul, povoľte prosím šifrovací modul v menu aplikácií.", "Select default encryption module:" : "Vybrať predvolený šifrovací modul:", "Start migration" : "Začať migráciu", "Security & setup warnings" : "Bezpečnosť a nastavenia upozornení", @@ -114,6 +168,7 @@ "This means that there might be problems with certain characters in file names." : "To znamená, že sa môžu vyskytnúť problémy s niektorými znakmi v názvoch súborov.", "We strongly suggest installing the required packages on your system to support one of the following locales: %s." : "Dôrazne doporučujeme nainštalovať na váš systém požadované balíčky podporujúce jednu z nasledovných znakových sád: %s.", "If your installation is not installed in the root of the domain and uses system cron, there can be issues with the URL generation. To avoid these problems, please set the \"overwrite.cli.url\" option in your config.php file to the webroot path of your installation (Suggested: \"%s\")" : "Inštalácia mimo koreňový priečinok domény a používanie systémového príkazu cron môže spôsobiť problém s generovaním správnej URL. Pre zabránenie týmto chybám nastavte prosím správnu cestu v svojom config.php súbore pre hodnotu \"overwrite.cli.url\" (Doporučujeme: \"%s\")", + "All checks passed." : "Všetky kontroly prešli úspešne.", "Cron" : "Cron", "Last cron job execution: %s." : "Posledný cron prebehol: %s.", "Last cron job execution: %s. Something seems wrong." : "Posledný cron prebehol: %s. Zdá sa, že niečo nie je vporiadku.", @@ -121,6 +176,7 @@ "Execute one task with each page loaded" : "Vykonať jednu úlohu s každým načítaní stránky", "cron.php is registered at a webcron service to call cron.php every 15 minutes over http." : "cron.php je zaregistrovaná v službe WebCron a zavolá cron.php každých 15 minút cez http.", "Use system's cron service to call the cron.php file every 15 minutes." : "Použiť systémovú službu cron na spúšťanie súboru cron.php každých 15 minút.", + "The cron.php needs to be executed by the system user \"%s\"." : "Je potrebné, aby cron.php bol spustený systémovým používateľom \"%s\".", "Version" : "Verzia", "Sharing" : "Sprístupňovanie", "Allow apps to use the Share API" : "Povoliť aplikáciám používať API pre sprístupňovanie", @@ -132,6 +188,7 @@ "days" : "dni", "Enforce expiration date" : "Vynútiť dátum expirácie", "Allow resharing" : "Povoliť sprístupňovanie ďalej", + "Allow sharing with groups" : "Povoliť sprístupnenie so skupinami", "Restrict users to only share with users in their groups" : "Povoliť používateľom sprístupňovanie obsahu len v rámci ich skupiny", "Exclude groups from sharing" : "Nesprístupniť obsah skupinám", "These groups will still be able to receive shares, but not to initiate them." : "Tieto skupiny nebudú mocť sprístupňovať obsah, môžu však stále čítať sprístupnené súbory", @@ -143,32 +200,56 @@ "Theming" : "Vzhľady tém", "Hardening and security guidance" : "Sprievodca vylepšením bezpečnosti", "Developer documentation" : "Dokumentácia vývojára", + "by %s" : "od %s", + "%s-licensed" : "%s-licencovaný", "Documentation:" : "Dokumentácia:", "User documentation" : "Príručka používateľa", "Admin documentation" : "Príručka administrátora", + "Visit website" : "Navštíviť webstránku", + "Report a bug" : "Nahlásiť chybu", "Show description …" : "Zobraziť popis …", "Hide description …" : "Skryť popis …", + "This app has an update available." : "Pre túto aplikáciu je dostupná aktualizácia.", + "This app has no minimum Nextcloud version assigned. This will be an error in the future." : "Pre túto aplikáciu nie je zadaná minimálna verzia Nextcloudu. Toto v budúcnosti spôsobí chybu.", + "This app has no maximum Nextcloud version assigned. This will be an error in the future." : "Pre túto aplikáciu nie je zadaná maximálna verzia Nextcloudu. Toto v budúcnosti spôsobí chybu.", "This app cannot be installed because the following dependencies are not fulfilled:" : "Túto aplikáciu nemožno nainštalovať, pretože nie sú splnené nasledovné závislosti:", "Enable only for specific groups" : "Povoliť len pre vybrané skupiny", + "Uninstall app" : "Odinštalovať aplikáciu", + "SSL Root Certificates" : "Koreňové certifikáty SSL", "Common Name" : "Bežný názov", "Valid until" : "Platný do", "Issued By" : "Vydal", "Valid until %s" : "Platný do %s", "Import root certificate" : "Importovať koreňový certifikát", + "Hey there,<br><br>just letting you know that you now have a %s account.<br><br>Your username: <strong>%s</strong><br>Access it: <strong><a href=\"%s\">%s</a></strong><br><br>" : "Dobrý deň,<br><br>toto je oznámenie o novo vytvorenom účte %s.<br><br>Vaše používateľské meno: <strong>%s</strong><br>Prihlásiť sa môžete tu: <strong><a href=\"%s\">%s</a></strong><br><br>", "Cheers!" : "Pekný deň!", "Administrator documentation" : "Príručka administrátora", "Online documentation" : "Online príručka", "Forum" : "Fórum", + "Getting help" : "Získať pomoc", "Commercial support" : "Komerčná podpora", + "You are using <strong>%s</strong> of <strong>%s</strong>" : "Využívate <strong>%s</strong> z <strong>%s</strong>", "Profile picture" : "Avatar", "Upload new" : "Nahrať nový", + "Select from Files" : "Vybrať zo súborov", "Remove image" : "Zmazať obrázok", + "png or jpg, max. 20 MB" : "png alebo jpg, max. 20 MB", + "Picture provided by original account" : "Obrázok poskytnutý originálnym účtom", "Cancel" : "Zrušiť", + "Choose as profile picture" : "Použiť ako obrázok avatara", "Full name" : "Meno a priezvisko", "No display name set" : "Zobrazované meno nie je nastavené", "Email" : "Email", "Your email address" : "Vaša emailová adresa", "No email address set" : "Emailová adresa nie je nastavená", + "For password recovery and notifications" : "Pre obnovu hesla a oznámenia", + "Phone number" : "Telefónne číslo", + "Your phone number" : "Vaše telefónne číslo", + "Address" : "Adresa", + "Your postal address" : "Vaša poštová adresa", + "Website" : "Webstránka", + "Your website" : "Vaša webstránka", + "Twitter" : "Twitter", "You are member of the following groups:" : "Ste členom nasledovných skupín:", "Password" : "Heslo", "Current password" : "Aktuálne heslo", @@ -180,10 +261,20 @@ "Desktop client" : "Desktopový klient", "Android app" : "Android aplikácia", "iOS app" : "iOS aplikácia", + "If you want to support the project {contributeopen}join development{linkclose} or {contributeopen}spread the word{linkclose}!" : "Ak chcete podporiť projekt {contributeopen}zapojte sa do vývoja{linkclose}, alebo {contributeopen}dajte o nás vedieť{linkclose}!", "Show First Run Wizard again" : "Znovu zobraziť sprievodcu prvým spustením", + "Web, desktop and mobile clients currently logged in to your account." : "Weboví, desktopoví, alebo mobilní klienti práve prihlásení na váš účet.", + "Device" : "Zariadenie", + "Last activity" : "Posledná aktivita", + "Passcodes that give an app or device permissions to access your account." : "Prístupové heslá, ktoré dovolia aplikáciam alebo zariadeniam prístup na váš účet.", "Name" : "Názov", + "App name" : "Názov aplikácie", + "Create new app password" : "Vytvoriť nové heslo aplikácie", + "Use the credentials below to configure your app or device." : "Pre konfiguráciu vašej aplikácie, alebo zariadenia použite nižšie uvedené prihlasovacie údaje.", + "For security reasons this password will only be shown once." : "Z dôvodu bezpečnosti toto heslo bude zobrazené iba jeden krát.", "Username" : "Používateľské meno", "Done" : "Hotovo", + "Follow us on Google Plus!" : "Sleduj nás na Google Plus!", "Show storage location" : "Zobraziť umiestnenie úložiska", "Show last log in" : "Zobraziť posledné prihlásenie", "Show user backend" : "Zobraziť backend používateľa", @@ -193,11 +284,17 @@ "Create" : "Vytvoriť", "Admin Recovery Password" : "Obnovenie hesla administrátora", "Enter the recovery password in order to recover the users files during password change" : "Zadajte heslo pre obnovenie súborov používateľa pri zmene hesla", + "Group name" : "Názov skupiny", "Everyone" : "Všetci", "Admins" : "Administrátori", + "Default quota" : "Predvolená kvóta", "Please enter storage quota (ex: \"512 MB\" or \"12 GB\")" : "Prosím zadajte kvótu úložného priestoru (napr.: \"512 MB\" alebo \"12 GB\")", "Other" : "Iné", + "Group admin for" : "Administrátor skupiny pre", "Quota" : "Kvóta", + "Storage location" : "Umiestnenie úložiska", + "User backend" : "Backend používateľa", + "Last login" : "Posledné prihlásenie", "change full name" : "zmeniť meno a priezvisko", "set new password" : "nastaviť nové heslo", "change email address" : "zmeniť emailovú adresu", @@ -206,12 +303,15 @@ "Language changed" : "Jazyk zmenený", "Are you really sure you want add \"{domain}\" as trusted domain?" : "Ste si istí, že chcete pridať \"{domain}\" medzi dôveryhodné domény?", "Please wait...." : "Čakajte prosím...", + "iPhone" : "iPhone", + "add group" : "pridať skupinu", "Everything (fatal issues, errors, warnings, info, debug)" : "Všetko (fatálne problémy, chyby, upozornenia, info, debug)", "Info, warnings, errors and fatal issues" : "Info, upozornenia, chyby a fatálne problémy", "Warnings, errors and fatal issues" : "Upozornenia, chyby a fatálne problémy", "Errors and fatal issues" : "Chyby a fatálne problémy", "Fatal issues only" : "Len fatálne problémy", "Log" : "Záznam", + "What to log" : "Čo sa má zaznamenávať", "Download logfile" : "Stiahnuť súbor logu", "More" : "Viac", "Less" : "Menej", @@ -226,6 +326,13 @@ "Enable experimental apps" : "Povoliť experimentálne aplikácie", "Hey there,<br><br>just letting you know that you now have an %s account.<br><br>Your username: %s<br>Access it: <a href=\"%s\">%s</a><br><br>" : "Dobrý deň,<br><br>toto je oznámenie o novo vytvorenom účte %s.<br><br>Vaše používateľské meno: %s<br>Prihlásiť sa môžete tu: <a href=\"%s\">%s</a><br><br>", "Hey there,\n\njust letting you know that you now have an %s account.\n\nYour username: %s\nAccess it: %s\n\n" : "Ahoj,\n\ntoto je oznámenie o novo vytvorenom účte %s.\n\nVaše používateľské meno: %s\nPrihlásiť sa môžete tu: %s\n\n", - "Group" : "Skupina" + "Add Group" : "Pridať skupinu", + "Group" : "Skupina", + "Default Quota" : "Predvolená kvóta", + "Full Name" : "Meno a priezvisko", + "Group Admin for" : "Administrátor skupiny pre", + "Storage Location" : "Umiestnenie úložiska", + "User Backend" : "Backend používateľa", + "Last Login" : "Posledné prihlásenie" },"pluralForm" :"nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;" }
\ No newline at end of file diff --git a/settings/l10n/sv.js b/settings/l10n/sv.js index c0b76233ded..f3829364552 100644 --- a/settings/l10n/sv.js +++ b/settings/l10n/sv.js @@ -11,8 +11,8 @@ OC.L10N.register( "Please provide an admin recovery password, otherwise all user data will be lost" : "Ange ett återställningslösenord för administratören. Annars kommer all användardata förloras", "Wrong admin recovery password. Please check the password and try again." : "Felaktigt återställningslösenord för administratör. Kolla lösenordet och prova igen.", "Backend doesn't support password change, but the user's encryption key was successfully updated." : "Backend stödjer inte lösenordsbyte, men användarens ändring av krypteringsnyckel lyckades.", - "installing and updating apps via the app store or Federated Cloud Sharing" : "installering och uppdatering utav applikationer eller Federate Cloud delning.", - "Federated Cloud Sharing" : "Federerad Cloud delning", + "installing and updating apps via the app store or Federated Cloud Sharing" : "installering och uppdatering utav applikationer eller Federerad Moln-delning.", + "Federated Cloud Sharing" : "Federerad Moln-delning", "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL använder en föråldrad %s version (%s). Var god uppdatera ditt operativsystem annars kan funktioner som %s sluta fungera pålitligt.", "A problem occurred, please check your log files (Error: %s)" : "Ett problem uppstod, var god kontrollera loggfiler (Error: %s)", "Migration Completed" : "Migrering Färdigställd", @@ -30,7 +30,7 @@ OC.L10N.register( "Your %s account was created" : "Ditt %s konto skapades", "Unable to delete user." : "Kan inte radera användare.", "Settings saved" : "Inställningar sparade", - "Unable to change full name" : "Kunde inte ändra hela namnet", + "Unable to change full name" : "Kunde inte ändra namn", "Unable to change email address" : "Kunde inte ändra e-postadress", "Your full name has been changed." : "Hela ditt namn har ändrats", "Forbidden" : "Förbjuden", @@ -60,11 +60,11 @@ OC.L10N.register( "This app is not checked for security issues and is new or known to be unstable. Install at your own risk." : "Denna applikation är ej kontrollerad för säkerhetsbrister och är ny eller känd att orsaka instabilitetsproblem. Installera på egen risk.", "Enabling app …" : "Aktiverar app ...", "Error while disabling app" : "Fel vid inaktivering av app", - "Disable" : "Deaktivera", + "Disable" : "Inaktivera", "Enable" : "Aktivera", "Error while enabling app" : "Fel vid aktivering av app", "Error: this app cannot be enabled because it makes the server unstable" : "Fel uppstod: Denna applikation kan ej startas för det gör servern ostabil.", - "Error: could not disable broken app" : "Fel: Gick ej att inaktivera trasig applikation.", + "Error: could not disable broken app" : "Fel: Gick inte att inaktivera trasig applikation.", "Error while disabling broken app" : "Fel under inaktivering av trasig applikation.", "Updating...." : "Uppdaterar ...", "Error while updating app" : "Fel uppstod vid uppdatering av appen", @@ -109,8 +109,8 @@ OC.L10N.register( "Only visible to you" : "Endast synlig för dig", "Contacts" : "Kontakter", "Visible to local users and to trusted servers" : "Synlig för lokala användare och tillförlitliga servrar", - "Public" : "Publik", - "Will be synced to a global and public address book" : "Kommer att synkroniseras till global och publik adressbok", + "Public" : "Offentlig", + "Will be synced to a global and public address book" : "Kommer att synkroniseras till global och offentlig adressbok", "Select a profile picture" : "Välj en profilbild", "Very weak password" : "Väldigt svagt lösenord", "Weak password" : "Svagt lösenord", @@ -136,7 +136,7 @@ OC.L10N.register( "A valid password must be provided" : "Ett giltigt lösenord måste anges", "A valid email must be provided" : "En giltig e-postadress måste anges", "__language_name__" : "Svenska", - "Unlimited" : "Obegränsad", + "Unlimited" : "Obegränsat", "Personal info" : "Personlig information", "Sessions" : "Sessioner", "App passwords" : "Applösenord", @@ -221,7 +221,7 @@ OC.L10N.register( "These groups will still be able to receive shares, but not to initiate them." : "Dessa grupper kommer fortfarande kunna ta emot delningar, men inte skapa delningar.", "Allow username autocompletion in share dialog. If this is disabled the full username needs to be entered." : "Tillåt användarnamn att autokompletteras i delningsfönstret. Om det är inaktiverat krävs fullständigt användarnamn i rutan.", "Show disclaimer text on the public link upload page. (Only shown when the file list is hidden.)" : "Visa friskrivningstext på offentliga uppladdningssidan. (Visas endast när fil-listan är gömd.)", - "This text will be shown on the public link upload page when the file list is hidden." : "Denna text kommer att visa på den publika uppladdnings-sidan när fil-listan är gömd.", + "This text will be shown on the public link upload page when the file list is hidden." : "Denna text kommer att visa på den offentliga uppladdnings-sidan när fil-listan är gömd.", "Tips & tricks" : "Tips & tricks", "SQLite is currently being used as the backend database. For larger installations we recommend that you switch to a different database backend." : "SQLite används som databas. För större installationer så rekommenderar vi ett byte till en annan databasbackend.", "This is particularly recommended when using the desktop client for file synchronisation." : "Detta rekommenderas speciellt när skrivbordsklienten används för att synkronisera filer.", @@ -271,7 +271,7 @@ OC.L10N.register( "Picture provided by original account" : "Bild gjordes tillgänglig av orginal konto", "Cancel" : "Avbryt", "Choose as profile picture" : "Välj som profilbild", - "Full name" : "Fullständigt namn", + "Full name" : "Namn", "No display name set" : "Inget visningsnamn angivet", "Email" : "E-post", "Your email address" : "Din e-postadress", @@ -296,7 +296,7 @@ OC.L10N.register( "Desktop client" : "Skrivbordsklient", "Android app" : "Android-app", "iOS app" : "iOS-app", - "If you want to support the project {contributeopen}join development{linkclose} or {contributeopen}spread the word{linkclose}!" : "Om du vill stödja projektet {contributeopen}delta i utvecklinngen{linkclose} eller {contributeopen}sprid det vidare{linkclose}!", + "If you want to support the project {contributeopen}join development{linkclose} or {contributeopen}spread the word{linkclose}!" : "Om du vill stödja projektet {contributeopen}, delta i utvecklinngen{linkclose} eller {contributeopen}sprida det vidare{linkclose}!", "Show First Run Wizard again" : "Visa Första uppstarts-guiden igen", "Web, desktop and mobile clients currently logged in to your account." : "Webb, skrivbordsklienter och mobila klienter som är inloggade på ditt konto just nu.", "Device" : "Enhet", @@ -309,7 +309,7 @@ OC.L10N.register( "For security reasons this password will only be shown once." : "Av säkerhetsskäl kommer lösenordet endast att visas en gång", "Username" : "Användarnamn", "Done" : "Färdig", - "Developed by the {communityopen}Nextcloud community{linkclose}, the {githubopen}source code{linkclose} is licensed under the {licenseopen}AGPL{linkclose}." : "Utvecklat av {communityopen}Nextclouds community{linkclose}, {githubopen}källkoden{linkclose}är licensierad enligt {licenseopen}AGPL-standard{linkclose}.", + "Developed by the {communityopen}Nextcloud community{linkclose}, the {githubopen}source code{linkclose} is licensed under the {licenseopen}AGPL{linkclose}." : "Utvecklat av {communityopen}Nextclouds community{linkclose}, {githubopen}källkoden{linkclose} är licensierad enligt {licenseopen}AGPL-standard{linkclose}.", "Follow us on Google Plus!" : "Fälj oss på Google Plus!", "Like our facebook page!" : "Gilla vår Facebooksida!", "Subscribe to our twitter channel!" : "Prenumerera på vårt Twitterkonto!", @@ -322,20 +322,20 @@ OC.L10N.register( "Show email address" : "Visa e-postadress", "E-Mail" : "E-post", "Create" : "Skapa", - "Admin Recovery Password" : "Admin-återställningslösenord", + "Admin Recovery Password" : "Admin-återställningslösen", "Enter the recovery password in order to recover the users files during password change" : "Ange återställningslösenordet för att återställa användarnas filer vid lösenordsbyte", "Group name" : "Gruppnamn", "Everyone" : "Alla", "Admins" : "Administratörer", - "Default quota" : "Förvald standardkvot", - "Please enter storage quota (ex: \"512 MB\" or \"12 GB\")" : "Var god skriv in lagringskvot (ex: \"512MB\" eller \"12 GB\")", + "Default quota" : "Förvalt lagringsutrymme", + "Please enter storage quota (ex: \"512 MB\" or \"12 GB\")" : "Ange storlek på lagringsutrymmet (t.ex: \"512 MB\" eller \"12 GB\")", "Other" : "Annat", "Group admin for" : "Gruppadministratör för", - "Quota" : "Kvot", + "Quota" : "Lagringsutrymme", "Storage location" : "Lagringsplats", "User backend" : "Användarbackend", "Last login" : "Senaste inloggning", - "change full name" : "ändra hela namnet", + "change full name" : "ändra namn", "set new password" : "ange nytt lösenord", "change email address" : "ändra e-postadress", "Default" : "Förvald", @@ -352,10 +352,10 @@ OC.L10N.register( "Fatal issues only" : "Endast allvarliga fel", "Log" : "Logg", "What to log" : "Vad som ska loggas", - "Download logfile" : "Ladda ner loggfil", + "Download logfile" : "Ladda ned loggfil", "More" : "Mer", "Less" : "Mindre", - "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Logfilen är större än 100 MB. Nerladdningen kan ta en stund!", + "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Logfilen är större än 100 MB. Nedladdningen kan ta en stund!", "Allow users to send mail notification for shared files" : "Tillåt användare att skicka mailnotifieringar för delade filer", "Allow users to send mail notification for shared files to other users" : "Tillåt användare att skicka mejlnotifiering för delade filer till andra användare", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite används som databas. För större installationer så rekommenderar vi ett byte till en annan databasmotor.", @@ -370,7 +370,7 @@ OC.L10N.register( "Add Group" : "Lägg till Grupp", "Group" : "Grupp", "Default Quota" : "Förvald datakvot", - "Full Name" : "Fullständigt namn", + "Full Name" : "Namn", "Group Admin for" : "Gruppadmin för", "Storage Location" : "Lagringsplats", "User Backend" : "Användarbackend", diff --git a/settings/l10n/sv.json b/settings/l10n/sv.json index daad2d3f605..f672d6be427 100644 --- a/settings/l10n/sv.json +++ b/settings/l10n/sv.json @@ -9,8 +9,8 @@ "Please provide an admin recovery password, otherwise all user data will be lost" : "Ange ett återställningslösenord för administratören. Annars kommer all användardata förloras", "Wrong admin recovery password. Please check the password and try again." : "Felaktigt återställningslösenord för administratör. Kolla lösenordet och prova igen.", "Backend doesn't support password change, but the user's encryption key was successfully updated." : "Backend stödjer inte lösenordsbyte, men användarens ändring av krypteringsnyckel lyckades.", - "installing and updating apps via the app store or Federated Cloud Sharing" : "installering och uppdatering utav applikationer eller Federate Cloud delning.", - "Federated Cloud Sharing" : "Federerad Cloud delning", + "installing and updating apps via the app store or Federated Cloud Sharing" : "installering och uppdatering utav applikationer eller Federerad Moln-delning.", + "Federated Cloud Sharing" : "Federerad Moln-delning", "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL använder en föråldrad %s version (%s). Var god uppdatera ditt operativsystem annars kan funktioner som %s sluta fungera pålitligt.", "A problem occurred, please check your log files (Error: %s)" : "Ett problem uppstod, var god kontrollera loggfiler (Error: %s)", "Migration Completed" : "Migrering Färdigställd", @@ -28,7 +28,7 @@ "Your %s account was created" : "Ditt %s konto skapades", "Unable to delete user." : "Kan inte radera användare.", "Settings saved" : "Inställningar sparade", - "Unable to change full name" : "Kunde inte ändra hela namnet", + "Unable to change full name" : "Kunde inte ändra namn", "Unable to change email address" : "Kunde inte ändra e-postadress", "Your full name has been changed." : "Hela ditt namn har ändrats", "Forbidden" : "Förbjuden", @@ -58,11 +58,11 @@ "This app is not checked for security issues and is new or known to be unstable. Install at your own risk." : "Denna applikation är ej kontrollerad för säkerhetsbrister och är ny eller känd att orsaka instabilitetsproblem. Installera på egen risk.", "Enabling app …" : "Aktiverar app ...", "Error while disabling app" : "Fel vid inaktivering av app", - "Disable" : "Deaktivera", + "Disable" : "Inaktivera", "Enable" : "Aktivera", "Error while enabling app" : "Fel vid aktivering av app", "Error: this app cannot be enabled because it makes the server unstable" : "Fel uppstod: Denna applikation kan ej startas för det gör servern ostabil.", - "Error: could not disable broken app" : "Fel: Gick ej att inaktivera trasig applikation.", + "Error: could not disable broken app" : "Fel: Gick inte att inaktivera trasig applikation.", "Error while disabling broken app" : "Fel under inaktivering av trasig applikation.", "Updating...." : "Uppdaterar ...", "Error while updating app" : "Fel uppstod vid uppdatering av appen", @@ -107,8 +107,8 @@ "Only visible to you" : "Endast synlig för dig", "Contacts" : "Kontakter", "Visible to local users and to trusted servers" : "Synlig för lokala användare och tillförlitliga servrar", - "Public" : "Publik", - "Will be synced to a global and public address book" : "Kommer att synkroniseras till global och publik adressbok", + "Public" : "Offentlig", + "Will be synced to a global and public address book" : "Kommer att synkroniseras till global och offentlig adressbok", "Select a profile picture" : "Välj en profilbild", "Very weak password" : "Väldigt svagt lösenord", "Weak password" : "Svagt lösenord", @@ -134,7 +134,7 @@ "A valid password must be provided" : "Ett giltigt lösenord måste anges", "A valid email must be provided" : "En giltig e-postadress måste anges", "__language_name__" : "Svenska", - "Unlimited" : "Obegränsad", + "Unlimited" : "Obegränsat", "Personal info" : "Personlig information", "Sessions" : "Sessioner", "App passwords" : "Applösenord", @@ -219,7 +219,7 @@ "These groups will still be able to receive shares, but not to initiate them." : "Dessa grupper kommer fortfarande kunna ta emot delningar, men inte skapa delningar.", "Allow username autocompletion in share dialog. If this is disabled the full username needs to be entered." : "Tillåt användarnamn att autokompletteras i delningsfönstret. Om det är inaktiverat krävs fullständigt användarnamn i rutan.", "Show disclaimer text on the public link upload page. (Only shown when the file list is hidden.)" : "Visa friskrivningstext på offentliga uppladdningssidan. (Visas endast när fil-listan är gömd.)", - "This text will be shown on the public link upload page when the file list is hidden." : "Denna text kommer att visa på den publika uppladdnings-sidan när fil-listan är gömd.", + "This text will be shown on the public link upload page when the file list is hidden." : "Denna text kommer att visa på den offentliga uppladdnings-sidan när fil-listan är gömd.", "Tips & tricks" : "Tips & tricks", "SQLite is currently being used as the backend database. For larger installations we recommend that you switch to a different database backend." : "SQLite används som databas. För större installationer så rekommenderar vi ett byte till en annan databasbackend.", "This is particularly recommended when using the desktop client for file synchronisation." : "Detta rekommenderas speciellt när skrivbordsklienten används för att synkronisera filer.", @@ -269,7 +269,7 @@ "Picture provided by original account" : "Bild gjordes tillgänglig av orginal konto", "Cancel" : "Avbryt", "Choose as profile picture" : "Välj som profilbild", - "Full name" : "Fullständigt namn", + "Full name" : "Namn", "No display name set" : "Inget visningsnamn angivet", "Email" : "E-post", "Your email address" : "Din e-postadress", @@ -294,7 +294,7 @@ "Desktop client" : "Skrivbordsklient", "Android app" : "Android-app", "iOS app" : "iOS-app", - "If you want to support the project {contributeopen}join development{linkclose} or {contributeopen}spread the word{linkclose}!" : "Om du vill stödja projektet {contributeopen}delta i utvecklinngen{linkclose} eller {contributeopen}sprid det vidare{linkclose}!", + "If you want to support the project {contributeopen}join development{linkclose} or {contributeopen}spread the word{linkclose}!" : "Om du vill stödja projektet {contributeopen}, delta i utvecklinngen{linkclose} eller {contributeopen}sprida det vidare{linkclose}!", "Show First Run Wizard again" : "Visa Första uppstarts-guiden igen", "Web, desktop and mobile clients currently logged in to your account." : "Webb, skrivbordsklienter och mobila klienter som är inloggade på ditt konto just nu.", "Device" : "Enhet", @@ -307,7 +307,7 @@ "For security reasons this password will only be shown once." : "Av säkerhetsskäl kommer lösenordet endast att visas en gång", "Username" : "Användarnamn", "Done" : "Färdig", - "Developed by the {communityopen}Nextcloud community{linkclose}, the {githubopen}source code{linkclose} is licensed under the {licenseopen}AGPL{linkclose}." : "Utvecklat av {communityopen}Nextclouds community{linkclose}, {githubopen}källkoden{linkclose}är licensierad enligt {licenseopen}AGPL-standard{linkclose}.", + "Developed by the {communityopen}Nextcloud community{linkclose}, the {githubopen}source code{linkclose} is licensed under the {licenseopen}AGPL{linkclose}." : "Utvecklat av {communityopen}Nextclouds community{linkclose}, {githubopen}källkoden{linkclose} är licensierad enligt {licenseopen}AGPL-standard{linkclose}.", "Follow us on Google Plus!" : "Fälj oss på Google Plus!", "Like our facebook page!" : "Gilla vår Facebooksida!", "Subscribe to our twitter channel!" : "Prenumerera på vårt Twitterkonto!", @@ -320,20 +320,20 @@ "Show email address" : "Visa e-postadress", "E-Mail" : "E-post", "Create" : "Skapa", - "Admin Recovery Password" : "Admin-återställningslösenord", + "Admin Recovery Password" : "Admin-återställningslösen", "Enter the recovery password in order to recover the users files during password change" : "Ange återställningslösenordet för att återställa användarnas filer vid lösenordsbyte", "Group name" : "Gruppnamn", "Everyone" : "Alla", "Admins" : "Administratörer", - "Default quota" : "Förvald standardkvot", - "Please enter storage quota (ex: \"512 MB\" or \"12 GB\")" : "Var god skriv in lagringskvot (ex: \"512MB\" eller \"12 GB\")", + "Default quota" : "Förvalt lagringsutrymme", + "Please enter storage quota (ex: \"512 MB\" or \"12 GB\")" : "Ange storlek på lagringsutrymmet (t.ex: \"512 MB\" eller \"12 GB\")", "Other" : "Annat", "Group admin for" : "Gruppadministratör för", - "Quota" : "Kvot", + "Quota" : "Lagringsutrymme", "Storage location" : "Lagringsplats", "User backend" : "Användarbackend", "Last login" : "Senaste inloggning", - "change full name" : "ändra hela namnet", + "change full name" : "ändra namn", "set new password" : "ange nytt lösenord", "change email address" : "ändra e-postadress", "Default" : "Förvald", @@ -350,10 +350,10 @@ "Fatal issues only" : "Endast allvarliga fel", "Log" : "Logg", "What to log" : "Vad som ska loggas", - "Download logfile" : "Ladda ner loggfil", + "Download logfile" : "Ladda ned loggfil", "More" : "Mer", "Less" : "Mindre", - "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Logfilen är större än 100 MB. Nerladdningen kan ta en stund!", + "The logfile is bigger than 100 MB. Downloading it may take some time!" : "Logfilen är större än 100 MB. Nedladdningen kan ta en stund!", "Allow users to send mail notification for shared files" : "Tillåt användare att skicka mailnotifieringar för delade filer", "Allow users to send mail notification for shared files to other users" : "Tillåt användare att skicka mejlnotifiering för delade filer till andra användare", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite används som databas. För större installationer så rekommenderar vi ett byte till en annan databasmotor.", @@ -368,7 +368,7 @@ "Add Group" : "Lägg till Grupp", "Group" : "Grupp", "Default Quota" : "Förvald datakvot", - "Full Name" : "Fullständigt namn", + "Full Name" : "Namn", "Group Admin for" : "Gruppadmin för", "Storage Location" : "Lagringsplats", "User Backend" : "Användarbackend", diff --git a/settings/templates/apps.php b/settings/templates/apps.php index 9f27b53e5e8..24f8061a530 100644 --- a/settings/templates/apps.php +++ b/settings/templates/apps.php @@ -24,7 +24,7 @@ script( <?php if($_['appstoreEnabled']): ?> <li> - <a class="app-external" target="_blank" rel="noreferrer" href="https://docs.nextcloud.org/server/11/developer_manual/"><?php p($l->t('Developer documentation'));?> ↗</a> + <a class="app-external" target="_blank" rel="noreferrer" href="https://docs.nextcloud.org/server/12/developer_manual/"><?php p($l->t('Developer documentation'));?> ↗</a> </li> <?php endif; ?> </script> diff --git a/tests/Core/Controller/CssControllerTest.php b/tests/Core/Controller/CssControllerTest.php new file mode 100644 index 00000000000..60fef9dddad --- /dev/null +++ b/tests/Core/Controller/CssControllerTest.php @@ -0,0 +1,111 @@ +<?php +/** + * @copyright 2017, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @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 Tests\Core\Controller; + +use OC\Core\Controller\CssController; +use OC\HintException; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\FileDisplayResponse; +use OCP\AppFramework\Http\NotFoundResponse; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Files\IAppData; +use OCP\Files\NotFoundException; +use OCP\Files\SimpleFS\ISimpleFile; +use OCP\Files\SimpleFS\ISimpleFolder; +use OCP\IRequest; +use Test\TestCase; + +class CssControllerTest extends TestCase { + + /** @var IAppData|\PHPUnit_Framework_MockObject_MockObject */ + private $appData; + + /** @var CssController */ + private $controller; + + public function setUp() { + parent::setUp(); + + $this->appData = $this->createMock(IAppData::class); + + $timeFactory = $this->createMock(ITimeFactory::class); + $timeFactory->method('getTime') + ->willReturn(1337); + + $this->controller = new CssController( + 'core', + $this->createMock(IRequest::class), + $this->appData, + $timeFactory + ); + } + + public function testNoCssFolderForApp() { + $this->appData->method('getFolder') + ->with('myapp') + ->willThrowException(new NotFoundException()); + + $result = $this->controller->getCss('file.css', 'myapp'); + + $this->assertInstanceOf(NotFoundResponse::class, $result); + } + + + public function testNoCssFile() { + $folder = $this->createMock(ISimpleFolder::class); + $this->appData->method('getFolder') + ->with('myapp') + ->willReturn($folder); + + $folder->method('getFile') + ->willThrowException(new NotFoundException()); + + $result = $this->controller->getCss('file.css', 'myapp'); + + $this->assertInstanceOf(NotFoundResponse::class, $result); + } + + public function testGetFile() { + $folder = $this->createMock(ISimpleFolder::class); + $file = $this->createMock(ISimpleFile::class); + $this->appData->method('getFolder') + ->with('myapp') + ->willReturn($folder); + + $folder->method('getFile') + ->with('file.css') + ->willReturn($file); + + $expected = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'text/css']); + $expected->cacheFor(86400); + $expires = new \DateTime(); + $expires->setTimestamp(1337); + $expires->add(new \DateInterval('PT24H')); + $expected->addHeader('Expires', $expires->format(\DateTime::RFC1123)); + $expected->addHeader('Pragma', 'cache'); + + $result = $this->controller->getCss('file.css', 'myapp'); + $this->assertEquals($expected, $result); + } + +} diff --git a/tests/karma.config.js b/tests/karma.config.js index f20672f4a55..afb1fa81463 100644 --- a/tests/karma.config.js +++ b/tests/karma.config.js @@ -223,6 +223,7 @@ module.exports = function(config) { // include core CSS files.push({pattern: 'core/css/*.css', watched: true, included: true, served: true}); + files.push({pattern: 'tests/css/*.css', watched: true, included: true, served: true}); config.set({ @@ -242,8 +243,9 @@ module.exports = function(config) { proxies: { // prevent warnings for images - '/context.html//core/img/': 'http://localhost:9876/base/core/img/', - '/context.html//core/css/': 'http://localhost:9876/base/core/css/', + '/base/tests/img/': 'http://localhost:9876/base/core/img/', + '/base/tests/css/': 'http://localhost:9876/base/core/css/', + '/actions/': 'http://localhost:9876/base/core/img/actions/', '/context.html//core/fonts/': 'http://localhost:9876/base/core/fonts/' }, diff --git a/tests/lib/App/AppStore/Fetcher/AppFetcherTest.php b/tests/lib/App/AppStore/Fetcher/AppFetcherTest.php index 3affab2dbaa..9d09898bb95 100644 --- a/tests/lib/App/AppStore/Fetcher/AppFetcherTest.php +++ b/tests/lib/App/AppStore/Fetcher/AppFetcherTest.php @@ -105,6 +105,9 @@ EOD; ->expects($this->once()) ->method('getBody') ->willReturn(self::$responseJson); + $response->method('getHeader') + ->with($this->equalTo('ETag')) + ->willReturn('"myETag"'); $this->timeFactory ->expects($this->once()) ->method('getTime') @@ -1884,6 +1887,7 @@ EJL3BaQAQaASSsvFrcozYxrQG4VzEg== ), 'timestamp' => 1234, 'ncversion' => '11.0.0.2', + 'ETag' => '"myETag"', ); $dataToPut = $expected; diff --git a/tests/lib/App/AppStore/Fetcher/FetcherBase.php b/tests/lib/App/AppStore/Fetcher/FetcherBase.php index cb47d0e08ac..73fcbbaab6f 100644 --- a/tests/lib/App/AppStore/Fetcher/FetcherBase.php +++ b/tests/lib/App/AppStore/Fetcher/FetcherBase.php @@ -127,7 +127,10 @@ abstract class FetcherBase extends TestCase { ->expects($this->once()) ->method('getBody') ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]'); - $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2"}'; + $response->method('getHeader') + ->with($this->equalTo('ETag')) + ->willReturn('"myETag"'); + $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2","ETag":"\"myETag\""}'; $file ->expects($this->at(0)) ->method('putContent') @@ -189,7 +192,10 @@ abstract class FetcherBase extends TestCase { ->expects($this->once()) ->method('getBody') ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]'); - $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2"}'; + $response->method('getHeader') + ->with($this->equalTo('ETag')) + ->willReturn('"myETag"'); + $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2","ETag":"\"myETag\""}'; $file ->expects($this->at(1)) ->method('putContent') @@ -251,7 +257,10 @@ abstract class FetcherBase extends TestCase { ->expects($this->once()) ->method('getBody') ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]'); - $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1201,"ncversion":"11.0.0.2"}'; + $response->method('getHeader') + ->with($this->equalTo('ETag')) + ->willReturn('"myETag"'); + $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1201,"ncversion":"11.0.0.2","ETag":"\"myETag\""}'; $file ->expects($this->at(1)) ->method('putContent') @@ -289,7 +298,7 @@ abstract class FetcherBase extends TestCase { $file ->expects($this->at(0)) ->method('getContent') - ->willReturn('{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}},"ncversion":"11.0.0.1"}'); + ->willReturn('{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}},"ncversion":"11.0.0.1"'); $this->timeFactory ->method('getTime') ->willReturn(1201); @@ -308,7 +317,10 @@ abstract class FetcherBase extends TestCase { ->expects($this->once()) ->method('getBody') ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]'); - $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1201,"ncversion":"11.0.0.2"}'; + $response->method('getHeader') + ->with($this->equalTo('ETag')) + ->willReturn('"myETag"'); + $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1201,"ncversion":"11.0.0.2","ETag":"\"myETag\""}'; $file ->expects($this->at(1)) ->method('putContent') @@ -364,4 +376,147 @@ abstract class FetcherBase extends TestCase { $this->assertSame([], $this->fetcher->get()); } + + public function testGetMatchingETag() { + $folder = $this->createMock(ISimpleFolder::class); + $file = $this->createMock(ISimpleFile::class); + $this->appData + ->expects($this->once()) + ->method('getFolder') + ->with('/') + ->willReturn($folder); + $folder + ->expects($this->once()) + ->method('getFile') + ->with($this->fileName) + ->willReturn($file); + $origData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1200,"ncversion":"11.0.0.2","ETag":"\"myETag\""}'; + $file + ->expects($this->at(0)) + ->method('getContent') + ->willReturn($origData); + $this->timeFactory + ->expects($this->at(0)) + ->method('getTime') + ->willReturn(1501); + $this->timeFactory + ->expects($this->at(1)) + ->method('getTime') + ->willReturn(1502); + $client = $this->createMock(IClient::class); + $this->clientService + ->expects($this->once()) + ->method('newClient') + ->willReturn($client); + $response = $this->createMock(IResponse::class); + $client + ->expects($this->once()) + ->method('get') + ->with( + $this->equalTo($this->endpoint), + $this->equalTo([ + 'headers' => [ + 'If-None-Match' => '"myETag"' + ] + ]) + )->willReturn($response); + $response->method('getStatusCode') + ->willReturn(304); + + $newData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2","ETag":"\"myETag\""}'; + $file + ->expects($this->at(1)) + ->method('putContent') + ->with($newData); + $file + ->expects($this->at(2)) + ->method('getContent') + ->willReturn($newData); + + $expected = [ + [ + 'id' => 'MyNewApp', + 'foo' => 'foo', + ], + [ + 'id' => 'bar', + ], + ]; + + $this->assertSame($expected, $this->fetcher->get()); + } + + public function testGetNoMatchingETag() { + $folder = $this->createMock(ISimpleFolder::class); + $file = $this->createMock(ISimpleFile::class); + $this->appData + ->expects($this->once()) + ->method('getFolder') + ->with('/') + ->willReturn($folder); + $folder + ->expects($this->at(0)) + ->method('getFile') + ->with($this->fileName) + ->willReturn($file); + $file + ->expects($this->at(0)) + ->method('getContent') + ->willReturn('{"data":[{"id":"MyOldApp","abc":"def"}],"timestamp":1200,"ncversion":"11.0.0.2","ETag":"\"myETag\""}'); + $client = $this->createMock(IClient::class); + $this->clientService + ->expects($this->once()) + ->method('newClient') + ->willReturn($client); + $response = $this->createMock(IResponse::class); + $client + ->expects($this->once()) + ->method('get') + ->with( + $this->equalTo($this->endpoint), + $this->equalTo([ + 'headers' => [ + 'If-None-Match' => '"myETag"', + ] + ]) + ) + ->willReturn($response); + $response->method('getStatusCode') + ->willReturn(200); + $response + ->expects($this->once()) + ->method('getBody') + ->willReturn('[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}]'); + $response->method('getHeader') + ->with($this->equalTo('ETag')) + ->willReturn('"newETag"'); + $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2","ETag":"\"newETag\""}'; + $file + ->expects($this->at(1)) + ->method('putContent') + ->with($fileData); + $file + ->expects($this->at(2)) + ->method('getContent') + ->willReturn($fileData); + $this->timeFactory + ->expects($this->at(0)) + ->method('getTime') + ->willReturn(1501); + $this->timeFactory + ->expects($this->at(1)) + ->method('getTime') + ->willReturn(1502); + + $expected = [ + [ + 'id' => 'MyNewApp', + 'foo' => 'foo', + ], + [ + 'id' => 'bar', + ], + ]; + $this->assertSame($expected, $this->fetcher->get()); + } } diff --git a/tests/lib/AppFramework/Controller/ControllerTest.php b/tests/lib/AppFramework/Controller/ControllerTest.php index 63cc2873575..640853ccda9 100644 --- a/tests/lib/AppFramework/Controller/ControllerTest.php +++ b/tests/lib/AppFramework/Controller/ControllerTest.php @@ -182,7 +182,7 @@ class ControllerTest extends \Test\TestCase { public function testFormatDataResponseJSON() { $expectedHeaders = [ 'test' => 'something', - 'Cache-Control' => 'no-cache, must-revalidate', + 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Content-Type' => 'application/json; charset=utf-8', 'Content-Security-Policy' => "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'", ]; diff --git a/tests/lib/AppFramework/Http/DataResponseTest.php b/tests/lib/AppFramework/Http/DataResponseTest.php index cde553cdf5f..159efded97a 100644 --- a/tests/lib/AppFramework/Http/DataResponseTest.php +++ b/tests/lib/AppFramework/Http/DataResponseTest.php @@ -67,7 +67,7 @@ class DataResponseTest extends \Test\TestCase { $response = new DataResponse($data, $code, $headers); $expectedHeaders = [ - 'Cache-Control' => 'no-cache, must-revalidate', + 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Content-Security-Policy' => "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'", ]; $expectedHeaders = array_merge($expectedHeaders, $headers); diff --git a/tests/lib/AppFramework/Http/ResponseTest.php b/tests/lib/AppFramework/Http/ResponseTest.php index 0c582f8f6ea..3ed946dc6ca 100644 --- a/tests/lib/AppFramework/Http/ResponseTest.php +++ b/tests/lib/AppFramework/Http/ResponseTest.php @@ -97,7 +97,7 @@ class ResponseTest extends \Test\TestCase { public function testCacheHeadersAreDisabledByDefault(){ $headers = $this->childResponse->getHeaders(); - $this->assertEquals('no-cache, must-revalidate', $headers['Cache-Control']); + $this->assertEquals('no-cache, no-store, must-revalidate', $headers['Cache-Control']); } diff --git a/tests/lib/Files/Stream/QuotaTest.php b/tests/lib/Files/Stream/QuotaTest.php index d084f0c769c..24b0e7f9474 100644 --- a/tests/lib/Files/Stream/QuotaTest.php +++ b/tests/lib/Files/Stream/QuotaTest.php @@ -9,14 +9,11 @@ namespace Test\Files\Stream; class QuotaTest extends \Test\TestCase { - protected function tearDown() { - \OC\Files\Stream\Quota::clear(); - parent::tearDown(); - } /** * @param string $mode * @param integer $limit + * @return resource */ protected function getStream($mode, $limit) { $source = fopen('php://temp', $mode); diff --git a/tests/lib/Files/Utils/ScannerTest.php b/tests/lib/Files/Utils/ScannerTest.php index 7895331d615..1379bc2e906 100644 --- a/tests/lib/Files/Utils/ScannerTest.php +++ b/tests/lib/Files/Utils/ScannerTest.php @@ -11,6 +11,7 @@ namespace Test\Files\Utils; use OC\Files\Filesystem; use OC\Files\Mount\MountPoint; use OC\Files\Storage\Temporary; +use OCA\Files_Sharing\SharedStorage; use OCP\Files\Config\IMountProvider; use OCP\Files\Storage\IStorageFactory; use OCP\IUser; @@ -188,4 +189,24 @@ class ScannerTest extends \Test\TestCase { $this->assertNotEquals($oldRoot->getEtag(), $newRoot->getEtag()); } + + public function testSkipLocalShares() { + $sharedStorage = $this->createMock(SharedStorage::class); + $sharedMount = new MountPoint($sharedStorage, '/share'); + Filesystem::getMountManager()->addMount($sharedMount); + + $sharedStorage->expects($this->any()) + ->method('instanceOfStorage') + ->will($this->returnValueMap([ + [SharedStorage::class, true], + ])); + $sharedStorage->expects($this->never()) + ->method('getScanner'); + + $scanner = new TestScanner('', \OC::$server->getDatabaseConnection(), \OC::$server->getLogger()); + $scanner->addMount($sharedMount); + $scanner->scan(''); + + $scanner->backgroundScan(''); + } } diff --git a/tests/lib/LoggerTest.php b/tests/lib/LoggerTest.php index abb9deebd55..da9cedc9f56 100644 --- a/tests/lib/LoggerTest.php +++ b/tests/lib/LoggerTest.php @@ -138,4 +138,22 @@ class LoggerTest extends TestCase { } } + public function dataGetLogClass() { + return [ + ['file', \OC\Log\File::class], + ['errorlog', \OC\Log\Errorlog::class], + ['syslog', \OC\Log\Syslog::class], + + ['owncloud', \OC\Log\File::class], + ['nextcloud', \OC\Log\File::class], + ['foobar', \OC\Log\File::class], + ]; + } + + /** + * @dataProvider dataGetLogClass + */ + public function testGetLogClass($type, $class) { + $this->assertEquals($class, Log::getLogClass($type)); + } } diff --git a/tests/lib/Memcache/APCTest.php b/tests/lib/Memcache/APCTest.php deleted file mode 100644 index 4bd7e62b94a..00000000000 --- a/tests/lib/Memcache/APCTest.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -namespace Test\Memcache; - -class APCTest extends Cache { - protected function setUp() { - parent::setUp(); - - if(!\OC\Memcache\APC::isAvailable()) { - $this->markTestSkipped('The apc extension is not available.'); - return; - } - if(\OC\Memcache\APCu::isAvailable()) { - $this->markTestSkipped('The apc extension is emulated by ACPu.'); - return; - } - $this->instance=new \OC\Memcache\APC($this->getUniqueID()); - } -} diff --git a/tests/lib/PreviewTest.php b/tests/lib/PreviewTest.php deleted file mode 100644 index 2d6f53e2ce8..00000000000 --- a/tests/lib/PreviewTest.php +++ /dev/null @@ -1,963 +0,0 @@ -<?php -/** - * @author Georg Ehrke <georg@owncloud.com> - * @author Olivier Paroz <owncloud@interfasys.ch> - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace Test; - -use OC\Files\FileInfo; -use OC\Files\Filesystem; -use OC\Files\Storage\Temporary; -use OC\Files\View; -use OC\Preview; -use Test\Traits\MountProviderTrait; -use Test\Traits\UserTrait; - -/** - * Class PreviewTest - * - * @group DB - * - * @package Test - */ -class PreviewTest extends TestCase { - use UserTrait; - use MountProviderTrait; - - const TEST_PREVIEW_USER1 = "test-preview-user1"; - - /** @var View */ - private $rootView; - /** - * Note that using 756 with an image with a ratio of 1.6 brings interesting rounding issues - * - * @var int maximum width allowed for a preview - * */ - private $configMaxWidth = 756; - /** @var int maximum height allowed for a preview */ - private $configMaxHeight = 756; - private $keepAspect; - private $scalingUp; - - private $samples = []; - private $sampleFileId; - private $sampleFilename; - private $sampleWidth; - private $sampleHeight; - private $maxScaleFactor; - /** @var int width of the max preview */ - private $maxPreviewWidth; - /** @var int height of the max preview */ - private $maxPreviewHeight; - /** @var int height of the max preview, which is the same as the one of the original image */ - private $maxPreviewRatio; - private $cachedBigger = []; - - /** - * Make sure your configuration file doesn't contain any additional providers - */ - protected function setUp() { - parent::setUp(); - - $this->createUser(self::TEST_PREVIEW_USER1, self::TEST_PREVIEW_USER1); - $this->loginAsUser(self::TEST_PREVIEW_USER1); - - $storage = new Temporary([]); - Filesystem::mount($storage, [], '/' . self::TEST_PREVIEW_USER1 . '/'); - - $this->rootView = new View(''); - $this->rootView->mkdir('/' . self::TEST_PREVIEW_USER1); - $this->rootView->mkdir('/' . self::TEST_PREVIEW_USER1 . '/files'); - - // We simulate the max dimension set in the config - \OC::$server->getConfig() - ->setSystemValue('preview_max_x', $this->configMaxWidth); - \OC::$server->getConfig() - ->setSystemValue('preview_max_y', $this->configMaxHeight); - // Used to test upscaling - $this->maxScaleFactor = 2; - \OC::$server->getConfig() - ->setSystemValue('preview_max_scale_factor', $this->maxScaleFactor); - - // We need to enable the providers we're going to use in the tests - $providers = [ - 'OC\\Preview\\JPEG', - 'OC\\Preview\\PNG', - 'OC\\Preview\\GIF', - 'OC\\Preview\\TXT', - 'OC\\Preview\\Postscript' - ]; - \OC::$server->getConfig() - ->setSystemValue('enabledPreviewProviders', $providers); - - // Sample is 1680x1050 JPEG - $this->prepareSample('testimage.jpg', 1680, 1050); - // Sample is 2400x1707 EPS - $this->prepareSample('testimage.eps', 2400, 1707); - // Sample is 1200x450 PNG - $this->prepareSample('testimage-wide.png', 1200, 450); - // Sample is 64x64 GIF - $this->prepareSample('testimage.gif', 64, 64); - } - - protected function tearDown() { - $this->logout(); - - parent::tearDown(); - } - - /** - * Tests if a preview can be deleted - */ - public function testIsPreviewDeleted() { - - $sampleFile = '/' . self::TEST_PREVIEW_USER1 . '/files/test.txt'; - - $this->rootView->file_put_contents($sampleFile, 'dummy file data'); - - $x = 50; - $y = 50; - - $preview = new Preview(self::TEST_PREVIEW_USER1, 'files/', 'test.txt', $x, $y); - $preview->getPreview(); - - $fileInfo = $this->rootView->getFileInfo($sampleFile); - /** @var int $fileId */ - $fileId = $fileInfo['fileid']; - $thumbCacheFile = $this->buildCachePath($fileId, $x, $y, true); - - $this->assertSame( - true, $this->rootView->file_exists($thumbCacheFile), "$thumbCacheFile \n" - ); - - $preview->deletePreview(); - - $this->assertSame(false, $this->rootView->file_exists($thumbCacheFile)); - } - - /** - * Tests if all previews can be deleted - * - * We test this first to make sure we'll be able to cleanup after each preview generating test - */ - public function testAreAllPreviewsDeleted() { - - $sampleFile = '/' . self::TEST_PREVIEW_USER1 . '/files/test.txt'; - - $this->rootView->file_put_contents($sampleFile, 'dummy file data'); - - $x = 50; - $y = 50; - - $preview = new Preview(self::TEST_PREVIEW_USER1, 'files/', 'test.txt', $x, $y); - $preview->getPreview(); - - $fileInfo = $this->rootView->getFileInfo($sampleFile); - /** @var int $fileId */ - $fileId = $fileInfo['fileid']; - - $thumbCacheFolder = '/' . self::TEST_PREVIEW_USER1 . '/' . Preview::THUMBNAILS_FOLDER . - '/' . $fileId . '/'; - - $this->assertSame(true, $this->rootView->is_dir($thumbCacheFolder), "$thumbCacheFolder \n"); - - $preview->deleteAllPreviews(); - - $this->assertSame(false, $this->rootView->is_dir($thumbCacheFolder)); - } - - public function txtBlacklist() { - $txt = 'random text file'; - - return [ - ['txt', $txt, false], - ]; - } - - /** - * @dataProvider txtBlacklist - * - * @param $extension - * @param $data - * @param $expectedResult - */ - public function testIsTransparent($extension, $data, $expectedResult) { - - $x = 32; - $y = 32; - - $sample = '/' . self::TEST_PREVIEW_USER1 . '/files/test.' . $extension; - $this->rootView->file_put_contents($sample, $data); - $preview = new Preview( - self::TEST_PREVIEW_USER1, 'files/', 'test.' . $extension, $x, - $y - ); - $image = $preview->getPreview(); - $resource = $image->resource(); - - //http://stackoverflow.com/questions/5702953/imagecolorat-and-transparency - $colorIndex = imagecolorat($resource, 1, 1); - $colorInfo = imagecolorsforindex($resource, $colorIndex); - $this->assertSame( - $expectedResult, - $colorInfo['alpha'] === 127, - 'Failed asserting that only previews for text files are transparent.' - ); - } - - /** - * Tests if unsupported previews return an empty object - */ - public function testUnsupportedPreviewsReturnEmptyObject() { - $width = 400; - $height = 200; - - // Previews for odt files are not enabled - $imgData = file_get_contents(\OC::$SERVERROOT . '/tests/data/testimage.odt'); - $imgPath = '/' . self::TEST_PREVIEW_USER1 . '/files/testimage.odt'; - $this->rootView->file_put_contents($imgPath, $imgData); - - $preview = - new Preview(self::TEST_PREVIEW_USER1, 'files/', 'testimage.odt', $width, $height); - $preview->getPreview(); - $image = $preview->getPreview(); - - $this->assertSame(false, $image->valid()); - } - - /** - * We generate the data to use as it makes it easier to adjust in case we need to test - * something different - * - * @return array - */ - public static function dimensionsDataProvider() { - $data = []; - $samples = [ - [200, 800], - [200, 800], - [50, 400], - [4, 60], - ]; - $keepAspect = false; - $scalingUp = false; - - for ($a = 0; $a < sizeof($samples); $a++) { - for ($b = 0; $b < 2; $b++) { - for ($c = 0; $c < 2; $c++) { - for ($d = 0; $d < 4; $d++) { - $coordinates = [ - [ - -rand($samples[$a][0], $samples[$a][1]), - -rand($samples[$a][0], $samples[$a][1]) - ], - [ - rand($samples[$a][0], $samples[$a][1]), - rand($samples[$a][0], $samples[$a][1]) - ], - [ - -rand($samples[$a][0], $samples[$a][1]), - rand($samples[$a][0], $samples[$a][1]) - ], - [ - rand($samples[$a][0], $samples[$a][1]), - -rand($samples[$a][0], $samples[$a][1]) - ] - ]; - $row = [$a]; - $row[] = $coordinates[$d][0]; - $row[] = $coordinates[$d][1]; - $row[] = $keepAspect; - $row[] = $scalingUp; - $data[] = $row; - } - $scalingUp = !$scalingUp; - } - $keepAspect = !$keepAspect; - } - } - - return $data; - } - - /** - * Tests if a preview of max dimensions gets created - * - * @requires extension imagick - * @dataProvider dimensionsDataProvider - * - * @param int $sampleId - * @param int $widthAdjustment - * @param int $heightAdjustment - * @param bool $keepAspect - * @param bool $scalingUp - */ - public function testCreateMaxAndNormalPreviewsAtFirstRequest( - $sampleId, $widthAdjustment, $heightAdjustment, $keepAspect = false, $scalingUp = false - ) { - // Get the right sample for the experiment - $this->getSample($sampleId); - $sampleWidth = $this->sampleWidth; - $sampleHeight = $this->sampleHeight; - $sampleFileId = $this->sampleFileId; - - // Adjust the requested size so that we trigger various test cases - $previewWidth = $sampleWidth + $widthAdjustment; - $previewHeight = $sampleHeight + $heightAdjustment; - $this->keepAspect = $keepAspect; - $this->scalingUp = $scalingUp; - - // Generates the max preview - $preview = $this->createPreview($previewWidth, $previewHeight); - - // There should be no cached thumbnails - $thumbnailFolder = '/' . self::TEST_PREVIEW_USER1 . '/' . Preview::THUMBNAILS_FOLDER . - '/' . $sampleFileId; - $this->assertSame(false, $this->rootView->is_dir($thumbnailFolder)); - - $image = $preview->getPreview(); - $this->assertNotSame(false, $image); - - $maxThumbCacheFile = $this->buildCachePath( - $sampleFileId, $this->maxPreviewWidth, $this->maxPreviewHeight, true, '-max' - ); - - $this->assertSame( - true, $this->rootView->file_exists($maxThumbCacheFile), "$maxThumbCacheFile \n" - ); - - // We check the dimensions of the file we've just stored - $maxPreview = imagecreatefromstring($this->rootView->file_get_contents($maxThumbCacheFile)); - - $this->assertEquals($this->maxPreviewWidth, imagesx($maxPreview)); - $this->assertEquals($this->maxPreviewHeight, imagesy($maxPreview)); - - // A thumbnail of the asked dimensions should also have been created (within the constraints of the max preview) - list($limitedPreviewWidth, $limitedPreviewHeight) = - $this->simulatePreviewDimensions($previewWidth, $previewHeight); - - $actualWidth = $image->width(); - $actualHeight = $image->height(); - - $this->assertEquals( - (int)$limitedPreviewWidth, $image->width(), "$actualWidth x $actualHeight \n" - ); - $this->assertEquals((int)$limitedPreviewHeight, $image->height()); - - // And it should be cached - $this->checkCache($sampleFileId, $limitedPreviewWidth, $limitedPreviewHeight); - - $preview->deleteAllPreviews(); - } - - /** - * Tests if the second preview will be based off the cached max preview - * - * @requires extension imagick - * @dataProvider dimensionsDataProvider - * - * @param int $sampleId - * @param int $widthAdjustment - * @param int $heightAdjustment - * @param bool $keepAspect - * @param bool $scalingUp - */ - public function testSecondPreviewsGetCachedMax( - $sampleId, $widthAdjustment, $heightAdjustment, $keepAspect = false, $scalingUp = false - ) { - //$this->markTestSkipped('Not testing this at this time'); - - $this->getSample($sampleId); - $sampleWidth = $this->sampleWidth; - $sampleHeight = $this->sampleHeight; - $sampleFileId = $this->sampleFileId; - - //Creates the Max preview which will be used in the rest of the test - $this->createMaxPreview(); - - // Adjust the requested size so that we trigger various test cases - $previewWidth = $sampleWidth + $widthAdjustment; - $previewHeight = $sampleHeight + $heightAdjustment; - $this->keepAspect = $keepAspect; - $this->scalingUp = $scalingUp; - - $preview = $this->createPreview($previewWidth, $previewHeight); - - // A cache query should return the thumbnail of max dimension - $isCached = $preview->isCached($sampleFileId); - $cachedMaxPreview = $this->buildCachePath( - $sampleFileId, $this->maxPreviewWidth, $this->maxPreviewHeight, false, '-max' - ); - $this->assertSame($cachedMaxPreview, $isCached); - } - - /** - * Make sure that the max preview can never be deleted - * - * For this test to work, the preview we generate first has to be the size of max preview - */ - public function testMaxPreviewCannotBeDeleted() { - //$this->markTestSkipped('Not testing this at this time'); - - $this->keepAspect = true; - $this->getSample(0); - $fileId = $this->sampleFileId; - - //Creates the Max preview which we will try to delete - $preview = $this->createMaxPreview(); - - // We try to deleted the preview - $preview->deletePreview(); - $this->assertNotSame(false, $preview->isCached($fileId)); - - $preview->deleteAllPreviews(); - } - - public static function aspectDataProvider() { - $data = []; - $samples = 4; - $keepAspect = false; - $scalingUp = false; - for ($a = 0; $a < $samples; $a++) { - for ($b = 0; $b < 2; $b++) { - for ($c = 0; $c < 2; $c++) { - $row = [$a]; - $row[] = $keepAspect; - $row[] = $scalingUp; - $data[] = $row; - $scalingUp = !$scalingUp; - } - $keepAspect = !$keepAspect; - } - } - - return $data; - } - - /** - * We ask for a preview larger than what is set in the configuration, - * so we should be getting either the max preview or a preview the size - * of the dimensions set in the config - * - * @requires extension imagick - * @dataProvider aspectDataProvider - * - * @param int $sampleId - * @param bool $keepAspect - * @param bool $scalingUp - */ - public function testDoNotCreatePreviewsLargerThanConfigMax( - $sampleId, $keepAspect = false, $scalingUp = false - ) { - //$this->markTestSkipped('Not testing this at this time'); - - $this->getSample($sampleId); - - //Creates the Max preview which will be used in the rest of the test - $this->createMaxPreview(); - - // Now we will create the real preview - $previewWidth = 4000; - $previewHeight = 4000; - $this->keepAspect = $keepAspect; - $this->scalingUp = $scalingUp; - - // Tries to create the very large preview - $preview = $this->createPreview($previewWidth, $previewHeight); - - $image = $preview->getPreview(); - $this->assertNotSame(false, $image); - - list($expectedWidth, $expectedHeight) = - $this->simulatePreviewDimensions($previewWidth, $previewHeight); - $this->assertEquals($expectedWidth, $image->width()); - $this->assertEquals($expectedHeight, $image->height()); - - // A preview of the asked size should not have been created since it's larger that our max dimensions - $postfix = $this->getThumbnailPostfix($previewWidth, $previewHeight); - $thumbCacheFile = $this->buildCachePath( - $this->sampleFileId, $previewWidth, $previewHeight, false, $postfix - ); - $this->assertSame( - false, $this->rootView->file_exists($thumbCacheFile), "$thumbCacheFile \n" - ); - - $preview->deleteAllPreviews(); - } - - /** - * Makes sure we're getting the proper cached thumbnail - * - * When we start by generating a preview which keeps the aspect ratio - * 200-125-with-aspect - * 300-300 ✓ - * - * When we start by generating a preview of exact dimensions - * 200-200 ✓ - * 300-188-with-aspect - * - * @requires extension imagick - * @dataProvider aspectDataProvider - * - * @param int $sampleId - * @param bool $keepAspect - * @param bool $scalingUp - */ - public function testIsBiggerWithAspectRatioCached( - $sampleId, $keepAspect = false, $scalingUp = false - ) { - //$this->markTestSkipped('Not testing this at this time'); - - $previewWidth = 400; - $previewHeight = 400; - $this->getSample($sampleId); - $fileId = $this->sampleFileId; - $this->keepAspect = $keepAspect; - $this->scalingUp = $scalingUp; - - // Caching the max preview in our preview array for the test - $this->cachedBigger[] = $this->buildCachePath( - $fileId, $this->maxPreviewWidth, $this->maxPreviewHeight, false, '-max' - ); - - $this->getSmallerThanMaxPreview($fileId, $previewWidth, $previewHeight); - // We switch the aspect ratio, to generate a thumbnail we should not be picked up - $this->keepAspect = !$keepAspect; - $this->getSmallerThanMaxPreview($fileId, $previewWidth + 100, $previewHeight + 100); - - // Small thumbnails are always cropped - $this->keepAspect = false; - // Smaller previews should be based on the previous, larger preview, with the correct aspect ratio - $this->createThumbnailFromBiggerCachedPreview($fileId, 32, 32); - - // 2nd cache query should indicate that we have a cached copy of the exact dimension - $this->getCachedSmallThumbnail($fileId, 32, 32); - - // We create a preview in order to be able to delete the cache - $preview = $this->createPreview(rand(), rand()); - $preview->deleteAllPreviews(); - $this->cachedBigger = []; - } - - /** - * Initialises the preview - * - * @param int $width - * @param int $height - * - * @return Preview - */ - private function createPreview($width, $height) { - $preview = new Preview( - self::TEST_PREVIEW_USER1, 'files/', $this->sampleFilename, $width, - $height - ); - - $this->assertSame(true, $preview->isFileValid()); - - $preview->setKeepAspect($this->keepAspect); - $preview->setScalingup($this->scalingUp); - - return $preview; - } - - /** - * Creates the Max preview which will be used in the rest of the test - * - * @return Preview - */ - private function createMaxPreview() { - $this->keepAspect = true; - $preview = $this->createPreview($this->maxPreviewWidth, $this->maxPreviewHeight); - $preview->getPreview(); - - return $preview; - } - - /** - * Makes sure the preview which was just created has been saved to disk - * - * @param int $fileId - * @param int $previewWidth - * @param int $previewHeight - */ - private function checkCache($fileId, $previewWidth, $previewHeight) { - $postfix = $this->getThumbnailPostfix($previewWidth, $previewHeight); - - $thumbCacheFile = $this->buildCachePath( - $fileId, $previewWidth, $previewHeight, true, $postfix - ); - - $this->assertSame( - true, $this->rootView->file_exists($thumbCacheFile), "$thumbCacheFile \n" - ); - } - - /** - * Computes special filename postfixes - * - * @param int $width - * @param int $height - * - * @return string - */ - private function getThumbnailPostfix($width, $height) { - // Need to take care of special postfix added to the dimensions - $postfix = ''; - $isMaxPreview = ($width === $this->maxPreviewWidth - && $height === $this->maxPreviewHeight) ? true : false; - if ($isMaxPreview) { - $postfix = '-max'; - } - if ($this->keepAspect && !$isMaxPreview) { - $postfix = '-with-aspect'; - } - - return $postfix; - } - - private function getSmallerThanMaxPreview($fileId, $previewWidth, $previewHeight) { - $preview = $this->createPreview($previewWidth, $previewHeight); - - $image = $preview->getPreview(); - $this->assertNotSame(false, $image); - - // A thumbnail of the asked dimensions should also have been created (within the constraints of the max preview) - list($limitedPreviewWidth, $limitedPreviewHeight) = - $this->simulatePreviewDimensions($previewWidth, $previewHeight); - - $this->assertEquals($limitedPreviewWidth, $image->width()); - $this->assertEquals($limitedPreviewHeight, $image->height()); - - // And it should be cached - $this->checkCache($fileId, $limitedPreviewWidth, $limitedPreviewHeight); - - $this->cachedBigger[] = $preview->isCached($fileId); - } - - private function createThumbnailFromBiggerCachedPreview($fileId, $width, $height) { - $preview = $this->createPreview($width, $height); - - // A cache query should return a thumbnail of slightly larger dimensions - // and with the proper aspect ratio - $isCached = $preview->isCached($fileId); - $expectedCachedBigger = $this->getExpectedCachedBigger(); - - $this->assertSame($expectedCachedBigger, $isCached); - - $image = $preview->getPreview(); - $this->assertNotSame(false, $image); - } - - /** - * Picks the bigger cached preview with the correct aspect ratio or the max preview if it's - * smaller than that - * - * For non-upscaled images, we pick the only picture without aspect ratio - * - * @return string - */ - private function getExpectedCachedBigger() { - $foundPreview = null; - $foundWidth = null; - $foundHeight = null; - $maxPreview = null; - $maxWidth = null; - $maxHeight = null; - - foreach ($this->cachedBigger as $cached) { - $size = explode('-', basename($cached)); - $width = (int)$size[0]; - $height = (int)$size[1]; - - if (strpos($cached, 'max')) { - $maxWidth = $width; - $maxHeight = $height; - $maxPreview = $cached; - continue; - } - - // We pick the larger preview with no aspect ratio - if (!strpos($cached, 'aspect') && !strpos($cached, 'max')) { - $foundPreview = $cached; - $foundWidth = $width; - $foundHeight = $height; - } - } - if ($foundWidth > $maxWidth && $foundHeight > $maxHeight) { - $foundPreview = $maxPreview; - } - - return $foundPreview; - } - - /** - * A small thumbnail of exact dimensions should be in the cache - * - * @param int $fileId - * @param int $width - * @param int $height - */ - private function getCachedSmallThumbnail($fileId, $width, $height) { - $preview = $this->createPreview($width, $height); - - $isCached = $preview->isCached($fileId); - $thumbCacheFile = $this->buildCachePath($fileId, $width, $height); - - $this->assertSame($thumbCacheFile, $isCached, "$thumbCacheFile \n"); - } - - /** - * Builds the complete path to a cached thumbnail starting from the user folder - * - * @param int $fileId - * @param int $width - * @param int $height - * @param bool $user - * @param string $postfix - * - * @return string - */ - private function buildCachePath($fileId, $width, $height, $user = false, $postfix = '') { - $userPath = ''; - if ($user) { - $userPath = '/' . self::TEST_PREVIEW_USER1 . '/'; - } - - return $userPath . Preview::THUMBNAILS_FOLDER . '/' . $fileId - . '/' . $width . '-' . $height . $postfix . '.png'; - } - - /** - * Stores the sample in the filesystem and stores it in the $samples array - * - * @param string $fileName - * @param int $sampleWidth - * @param int $sampleHeight - */ - private function prepareSample($fileName, $sampleWidth, $sampleHeight) { - $imgData = file_get_contents(\OC::$SERVERROOT . '/tests/data/' . $fileName); - $imgPath = '/' . self::TEST_PREVIEW_USER1 . '/files/' . $fileName; - $this->rootView->file_put_contents($imgPath, $imgData); - $fileInfo = $this->rootView->getFileInfo($imgPath); - - list($maxPreviewWidth, $maxPreviewHeight) = - $this->setMaxPreview($sampleWidth, $sampleHeight); - - $this->samples[] = - [ - 'sampleFileId' => $fileInfo['fileid'], - 'sampleFileName' => $fileName, - 'sampleWidth' => $sampleWidth, - 'sampleHeight' => $sampleHeight, - 'maxPreviewWidth' => $maxPreviewWidth, - 'maxPreviewHeight' => $maxPreviewHeight - ]; - } - - /** - * Sets the variables used to define the boundaries which need to be respected when using a - * specific sample - * - * @param $sampleId - */ - private function getSample($sampleId) { - // Corrects a rounding difference when using the EPS (Imagick converted) sample - $filename = $this->samples[$sampleId]['sampleFileName']; - $splitFileName = pathinfo($filename); - $extension = $splitFileName['extension']; - $correction = ($extension === 'eps' && PHP_MAJOR_VERSION < 7) ? 1 : 0; - $maxPreviewHeight = $this->samples[$sampleId]['maxPreviewHeight']; - $maxPreviewHeight = $maxPreviewHeight - $correction; - - $this->sampleFileId = $this->samples[$sampleId]['sampleFileId']; - $this->sampleFilename = $this->samples[$sampleId]['sampleFileName']; - $this->sampleWidth = $this->samples[$sampleId]['sampleWidth']; - $this->sampleHeight = $this->samples[$sampleId]['sampleHeight']; - $this->maxPreviewWidth = $this->samples[$sampleId]['maxPreviewWidth']; - $this->maxPreviewHeight = $maxPreviewHeight; - $ratio = $this->maxPreviewWidth / $this->maxPreviewHeight; - $this->maxPreviewRatio = $ratio; - } - - /** - * Defines the size of the max preview - * - * @fixme the Imagick previews don't have the exact same size on disk as they're calculated here - * - * @param int $sampleWidth - * @param int $sampleHeight - * - * @return array - */ - private function setMaxPreview($sampleWidth, $sampleHeight) { - // Max previews are never scaled up - $this->scalingUp = false; - // Max previews always keep the aspect ratio - $this->keepAspect = true; - // We set this variable in order to be able to calculate the max preview with the proper aspect ratio - $this->maxPreviewRatio = $sampleWidth / $sampleHeight; - $maxPreviewWidth = min($sampleWidth, $this->configMaxWidth); - $maxPreviewHeight = min($sampleHeight, $this->configMaxHeight); - list($maxPreviewWidth, $maxPreviewHeight) = - $this->applyAspectRatio($maxPreviewWidth, $maxPreviewHeight); - - return [$maxPreviewWidth, $maxPreviewHeight]; - } - - /** - * Calculates the expected dimensions of the preview to be able to assess if we've got the - * right result - * - * @param int $askedWidth - * @param int $askedHeight - * - * @return array - */ - private function simulatePreviewDimensions($askedWidth, $askedHeight) { - $askedWidth = min($askedWidth, $this->configMaxWidth); - $askedHeight = min($askedHeight, $this->configMaxHeight); - - if ($this->keepAspect) { - // Defines the box in which the preview has to fit - $scaleFactor = $this->scalingUp ? $this->maxScaleFactor : 1; - $newPreviewWidth = min($askedWidth, $this->maxPreviewWidth * $scaleFactor); - $newPreviewHeight = min($askedHeight, $this->maxPreviewHeight * $scaleFactor); - list($newPreviewWidth, $newPreviewHeight) = - $this->applyAspectRatio($newPreviewWidth, $newPreviewHeight); - } else { - list($newPreviewWidth, $newPreviewHeight) = - $this->fixSize($askedWidth, $askedHeight); - } - - return [(int)$newPreviewWidth, (int)$newPreviewHeight]; - } - - /** - * Resizes the boundaries to match the aspect ratio - * - * @param int $askedWidth - * @param int $askedHeight - * - * @return \int[] - */ - private function applyAspectRatio($askedWidth, $askedHeight) { - $originalRatio = $this->maxPreviewRatio; - if ($askedWidth / $originalRatio < $askedHeight) { - $askedHeight = round($askedWidth / $originalRatio); - } else { - $askedWidth = round($askedHeight * $originalRatio); - } - - return [(int)$askedWidth, (int)$askedHeight]; - } - - /** - * Clips or stretches the dimensions so that they fit in the boundaries - * - * @param int $askedWidth - * @param int $askedHeight - * - * @return array - */ - private function fixSize($askedWidth, $askedHeight) { - if ($this->scalingUp) { - $askedWidth = min($this->configMaxWidth, $askedWidth); - $askedHeight = min($this->configMaxHeight, $askedHeight); - } - - return [(int)$askedWidth, (int)$askedHeight]; - } - - public function testKeepAspectRatio() { - $originalWidth = 1680; - $originalHeight = 1050; - $originalAspectRation = $originalWidth / $originalHeight; - - $preview = new Preview( - self::TEST_PREVIEW_USER1, 'files/', 'testimage.jpg', - 150, - 150 - ); - $preview->setKeepAspect(true); - $image = $preview->getPreview(); - - $aspectRatio = $image->width() / $image->height(); - $this->assertEquals(round($originalAspectRation, 2), round($aspectRatio, 2)); - - $this->assertLessThanOrEqual(150, $image->width()); - $this->assertLessThanOrEqual(150, $image->height()); - } - - public function testKeepAspectRatioCover() { - $originalWidth = 1680; - $originalHeight = 1050; - $originalAspectRation = $originalWidth / $originalHeight; - - $preview = new Preview( - self::TEST_PREVIEW_USER1, 'files/', 'testimage.jpg', - 150, - 150 - ); - $preview->setKeepAspect(true); - $preview->setMode(Preview::MODE_COVER); - $image = $preview->getPreview(); - - $aspectRatio = $image->width() / $image->height(); - $this->assertEquals(round($originalAspectRation, 2), round($aspectRatio, 2)); - - $this->assertGreaterThanOrEqual(150, $image->width()); - $this->assertGreaterThanOrEqual(150, $image->height()); - } - - public function testSetFileWithInfo() { - $info = new FileInfo('/foo', null, '/foo', ['mimetype' => 'foo/bar'], null); - $preview = new Preview(); - $preview->setFile('/foo', $info); - $this->assertEquals($info, $this->invokePrivate($preview, 'getFileInfo')); - } - - public function testIsCached() { - $sourceFile = __DIR__ . '/../data/testimage.png'; - $userId = $this->getUniqueID(); - $this->createUser($userId, 'pass'); - - $storage = new Temporary(); - $storage->mkdir('files'); - $this->registerMount($userId, $storage, '/' . $userId); - - \OC_Util::tearDownFS(); - \OC_Util::setupFS($userId); - $preview = new Preview($userId, 'files'); - $view = new View('/' . $userId . '/files'); - $view->file_put_contents('test.png', file_get_contents($sourceFile)); - $info = $view->getFileInfo('test.png'); - $preview->setFile('test.png', $info); - - $preview->setMaxX(64); - $preview->setMaxY(64); - - $this->assertFalse($preview->isCached($info->getId())); - - $preview->getPreview(); - - $this->assertEquals('thumbnails/' . $info->getId() . '/64-64.png', $preview->isCached($info->getId())); - } -} diff --git a/tests/lib/Repair/RepairInvalidSharesTest.php b/tests/lib/Repair/RepairInvalidSharesTest.php index 1ac42e53bf6..83dbed7d202 100644 --- a/tests/lib/Repair/RepairInvalidSharesTest.php +++ b/tests/lib/Repair/RepairInvalidSharesTest.php @@ -278,6 +278,73 @@ class RepairInvalidSharesTest extends TestCase { $result->closeCursor(); } + public function fileSharePermissionsProvider() { + return [ + // unchanged for folder + [ + 'folder', + 31, + 31, + ], + // unchanged for read-write + share + [ + 'file', + \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_SHARE, + \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_SHARE, + ], + // fixed for all perms + [ + 'file', + \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_SHARE, + \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_SHARE, + ], + ]; + } + + /** + * Test adjusting file share permissions + * + * @dataProvider fileSharePermissionsProvider + */ + public function testFileSharePermissions($itemType, $testPerms, $expectedPerms) { + $qb = $this->connection->getQueryBuilder(); + $qb->insert('share') + ->values([ + 'share_type' => $qb->expr()->literal(Constants::SHARE_TYPE_LINK), + 'uid_owner' => $qb->expr()->literal('user1'), + 'item_type' => $qb->expr()->literal($itemType), + 'item_source' => $qb->expr()->literal(123), + 'item_target' => $qb->expr()->literal('/123'), + 'file_source' => $qb->expr()->literal(123), + 'file_target' => $qb->expr()->literal('/test'), + 'permissions' => $qb->expr()->literal($testPerms), + 'stime' => $qb->expr()->literal(time()), + ]) + ->execute(); + + $shareId = $this->getLastShareId(); + + /** @var IOutput | \PHPUnit_Framework_MockObject_MockObject $outputMock */ + $outputMock = $this->getMockBuilder('\OCP\Migration\IOutput') + ->disableOriginalConstructor() + ->getMock(); + + $this->repair->run($outputMock); + + $results = $this->connection->getQueryBuilder() + ->select('*') + ->from('share') + ->orderBy('permissions', 'ASC') + ->execute() + ->fetchAll(); + + $this->assertCount(1, $results); + + $updatedShare = $results[0]; + + $this->assertEquals($expectedPerms, $updatedShare['permissions']); + } + /** * @return int */ diff --git a/tests/lib/Security/CertificateManagerTest.php b/tests/lib/Security/CertificateManagerTest.php index 92a063d47c7..408e65c6766 100644 --- a/tests/lib/Security/CertificateManagerTest.php +++ b/tests/lib/Security/CertificateManagerTest.php @@ -8,8 +8,10 @@ namespace Test\Security; +use OC\Files\Storage\Temporary; use \OC\Security\CertificateManager; use OCP\IConfig; +use OCP\ILogger; /** * Class CertificateManagerTest @@ -43,7 +45,7 @@ class CertificateManagerTest extends \Test\TestCase { $config->expects($this->any())->method('getSystemValue') ->with('installed', false)->willReturn(true); - $this->certificateManager = new CertificateManager($this->username, new \OC\Files\View(), $config); + $this->certificateManager = new CertificateManager($this->username, new \OC\Files\View(), $config, $this->createMock(ILogger::class)); } protected function tearDown() { @@ -143,7 +145,7 @@ class CertificateManagerTest extends \Test\TestCase { /** @var CertificateManager | \PHPUnit_Framework_MockObject_MockObject $certificateManager */ $certificateManager = $this->getMockBuilder('OC\Security\CertificateManager') - ->setConstructorArgs([$uid, $view, $config]) + ->setConstructorArgs([$uid, $view, $config, $this->createMock(ILogger::class)]) ->setMethods(['getFilemtimeOfCaBundle', 'getCertificateBundle']) ->getMock(); @@ -210,5 +212,4 @@ class CertificateManagerTest extends \Test\TestCase { [null, 10, 5, 8, false, true], ]; } - } diff --git a/tests/lib/Share20/ManagerTest.php b/tests/lib/Share20/ManagerTest.php index 00009a73b0e..7b01a8f9e70 100644 --- a/tests/lib/Share20/ManagerTest.php +++ b/tests/lib/Share20/ManagerTest.php @@ -1768,6 +1768,116 @@ class ManagerTest extends \Test\TestCase { $this->assertEquals('hashed', $share->getPassword()); } + public function testCreateShareMail() { + $manager = $this->createManagerMock() + ->setMethods([ + 'canShare', + 'generalCreateChecks', + 'linkCreateChecks', + 'pathCreateChecks', + 'validateExpirationDate', + 'verifyPassword', + 'setLinkParent', + ]) + ->getMock(); + + $shareOwner = $this->createMock(IUser::class); + $shareOwner->method('getUID')->willReturn('shareOwner'); + + $storage = $this->createMock(Storage::class); + $path = $this->createMock(File::class); + $path->method('getOwner')->willReturn($shareOwner); + $path->method('getName')->willReturn('target'); + $path->method('getId')->willReturn(1); + $path->method('getStorage')->willReturn($storage); + + $share = $this->manager->newShare(); + $share->setShareType(\OCP\Share::SHARE_TYPE_EMAIL) + ->setNode($path) + ->setSharedBy('sharedBy') + ->setPermissions(\OCP\Constants::PERMISSION_ALL); + + $manager->expects($this->once()) + ->method('canShare') + ->with($share) + ->willReturn(true); + $manager->expects($this->once()) + ->method('generalCreateChecks') + ->with($share);; + $manager->expects($this->never()) + ->method('linkCreateChecks'); + $manager->expects($this->once()) + ->method('pathCreateChecks') + ->with($path); + $manager->expects($this->never()) + ->method('validateExpirationDate'); + $manager->expects($this->never()) + ->method('verifyPassword'); + $manager->expects($this->never()) + ->method('setLinkParent'); + + $this->secureRandom->method('getMediumStrengthGenerator') + ->will($this->returnSelf()); + $this->secureRandom->method('generate') + ->willReturn('token'); + + $this->defaultProvider + ->expects($this->once()) + ->method('create') + ->with($share) + ->will($this->returnCallback(function(Share $share) { + return $share->setId(42); + })); + + $hookListner = $this->getMockBuilder('Dummy')->setMethods(['pre', 'post'])->getMock(); + \OCP\Util::connectHook('OCP\Share', 'pre_shared', $hookListner, 'pre'); + \OCP\Util::connectHook('OCP\Share', 'post_shared', $hookListner, 'post'); + + $hookListnerExpectsPre = [ + 'itemType' => 'file', + 'itemSource' => 1, + 'shareType' => \OCP\Share::SHARE_TYPE_EMAIL, + 'uidOwner' => 'sharedBy', + 'permissions' => 31, + 'fileSource' => 1, + 'expiration' => null, + 'token' => 'token', + 'run' => true, + 'error' => '', + 'itemTarget' => '/target', + 'shareWith' => null, + ]; + + $hookListnerExpectsPost = [ + 'itemType' => 'file', + 'itemSource' => 1, + 'shareType' => \OCP\Share::SHARE_TYPE_EMAIL, + 'uidOwner' => 'sharedBy', + 'permissions' => 31, + 'fileSource' => 1, + 'expiration' => null, + 'token' => 'token', + 'id' => 42, + 'itemTarget' => '/target', + 'fileTarget' => '/target', + 'shareWith' => null, + ]; + + $hookListner->expects($this->once()) + ->method('pre') + ->with($this->equalTo($hookListnerExpectsPre)); + $hookListner->expects($this->once()) + ->method('post') + ->with($this->equalTo($hookListnerExpectsPost)); + + /** @var IShare $share */ + $share = $manager->createShare($share); + + $this->assertSame('shareOwner', $share->getShareOwner()); + $this->assertEquals('/target', $share->getTarget()); + $this->assertEquals('token', $share->getToken()); + } + /** * @expectedException Exception * @expectedExceptionMessage I won't let you share diff --git a/tests/lib/StreamWrappersTest.php b/tests/lib/StreamWrappersTest.php deleted file mode 100644 index ee2f6a8b0b2..00000000000 --- a/tests/lib/StreamWrappersTest.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php -/** - * ownCloud - * - * @author Robin Appelman - * @copyright 2012 Robin Appelman icewind@owncloud.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 library. If not, see <http://www.gnu.org/licenses/>. - * - */ - -namespace Test; - -/** - * Class StreamWrappersTest - * - * @group DB - */ -class StreamWrappersTest extends \Test\TestCase { - - private static $trashBinStatus; - - public static function setUpBeforeClass() { - self::$trashBinStatus = \OC_App::isEnabled('files_trashbin'); - \OC_App::disable('files_trashbin'); - } - - public static function tearDownAfterClass() { - if (self::$trashBinStatus) { - (new \OC_App())->enable('files_trashbin'); - } - } - - public function testCloseStream() { - //ensure all basic stream stuff works - $sourceFile = \OC::$SERVERROOT . '/tests/data/lorem.txt'; - $tmpFile = \OC::$server->getTempManager()->getTemporaryFile('.txt'); - $file = 'close://' . $tmpFile; - $this->assertTrue(file_exists($file)); - file_put_contents($file, file_get_contents($sourceFile)); - $this->assertEquals(file_get_contents($sourceFile), file_get_contents($file)); - unlink($file); - clearstatcache(); - $this->assertFalse(file_exists($file)); - - //test callback - $tmpFile = \OC::$server->getTempManager()->getTemporaryFile('.txt'); - $file = 'close://' . $tmpFile; - $actual = false; - $callback = function($path) use (&$actual) { $actual = $path; }; - \OC\Files\Stream\Close::registerCallback($tmpFile, $callback); - $fh = fopen($file, 'w'); - fwrite($fh, 'asd'); - fclose($fh); - $this->assertSame($tmpFile, $actual); - } -} diff --git a/tests/lib/User/SessionTest.php b/tests/lib/User/SessionTest.php index 27cb92d6732..51560d78a6a 100644 --- a/tests/lib/User/SessionTest.php +++ b/tests/lib/User/SessionTest.php @@ -528,7 +528,7 @@ class SessionTest extends \Test\TestCase { ->getMock(); $userSession = $this->getMockBuilder(Session::class) //override, otherwise tests will fail because of setcookie() - ->setMethods(['setMagicInCookie']) + ->setMethods(['setMagicInCookie', 'setLoginName']) ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random]) ->getMock(); @@ -566,6 +566,15 @@ class SessionTest extends \Test\TestCase { ->with($oldSessionId, $sessionId) ->will($this->returnValue(true)); + $tokenObject = $this->createMock(IToken::class); + $tokenObject->expects($this->once()) + ->method('getLoginName') + ->willReturn('foobar'); + $this->tokenProvider->expects($this->once()) + ->method('getToken') + ->with($sessionId) + ->willReturn($tokenObject); + $user->expects($this->any()) ->method('getUID') ->will($this->returnValue('foo')); @@ -576,6 +585,9 @@ class SessionTest extends \Test\TestCase { $session->expects($this->once()) ->method('set') ->with('user_id', 'foo'); + $userSession->expects($this->once()) + ->method('setLoginName') + ->willReturn('foobar'); $granted = $userSession->loginWithCookie('foo', $token, $oldSessionId); diff --git a/tests/lib/UserTest.php b/tests/lib/UserTest.php index 7a033c2921e..2a477522dea 100644 --- a/tests/lib/UserTest.php +++ b/tests/lib/UserTest.php @@ -25,7 +25,7 @@ class UserTest extends TestCase { protected function setUp(){ parent::setUp(); - $this->backend = $this->getMock('\Test\Util\User\Dummy'); + $this->backend = $this->createMock(\Test\Util\User\Dummy::class); $manager = \OC::$server->getUserManager(); $manager->registerBackend($this->backend); } diff --git a/version.php b/version.php index 6db26c8c66c..90f4e65055a 100644 --- a/version.php +++ b/version.php @@ -26,7 +26,7 @@ // between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel // when updating major/minor version number. -$OC_Version = array(12, 0, 0, 11); +$OC_Version = array(12, 0, 0, 12); // The human readable string $OC_VersionString = '12.0 alpha'; |