diff options
Diffstat (limited to 'apps')
100 files changed, 1746 insertions, 325 deletions
diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php index e80f9ae125a..9058548489c 100644 --- a/apps/dav/lib/Server.php +++ b/apps/dav/lib/Server.php @@ -173,6 +173,9 @@ class Server { ) ) ); + $this->server->addPlugin( + new \OCA\DAV\Connector\Sabre\QuotaPlugin($view)); + } }); } diff --git a/apps/federation/l10n/fr.js b/apps/federation/l10n/fr.js index 95029368394..9113e44017b 100644 --- a/apps/federation/l10n/fr.js +++ b/apps/federation/l10n/fr.js @@ -8,6 +8,8 @@ OC.L10N.register( "Federation" : "Fédération", "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." : "La « fédération » vous permet de vous connecter avec d'autres serveurs de confiance pour échanger la liste des utilisateurs. Par exemple, ce sera utilisé pour auto-compléter les utilisateurs externes lors du partage fédéré.", "Add server automatically once a federated share was created successfully" : "Ajouter un serveur automatiquement une fois que le partage a été créé avec succès .", - "Trusted Servers" : "Serveurs de confiance" + "Trusted Servers" : "Serveurs de confiance", + "+ Add Nextcloud server" : "+ Ajouter un serveur Nextcloud", + "Nextcloud Server" : "Serveur Nextcloud" }, "nplurals=2; plural=(n > 1);"); diff --git a/apps/federation/l10n/fr.json b/apps/federation/l10n/fr.json index 57487dec6d8..88667336936 100644 --- a/apps/federation/l10n/fr.json +++ b/apps/federation/l10n/fr.json @@ -6,6 +6,8 @@ "Federation" : "Fédération", "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." : "La « fédération » vous permet de vous connecter avec d'autres serveurs de confiance pour échanger la liste des utilisateurs. Par exemple, ce sera utilisé pour auto-compléter les utilisateurs externes lors du partage fédéré.", "Add server automatically once a federated share was created successfully" : "Ajouter un serveur automatiquement une fois que le partage a été créé avec succès .", - "Trusted Servers" : "Serveurs de confiance" + "Trusted Servers" : "Serveurs de confiance", + "+ Add Nextcloud server" : "+ Ajouter un serveur Nextcloud", + "Nextcloud Server" : "Serveur Nextcloud" },"pluralForm" :"nplurals=2; plural=(n > 1);" }
\ No newline at end of file diff --git a/apps/federation/l10n/is.js b/apps/federation/l10n/is.js index 970656835fd..e7016e9b154 100644 --- a/apps/federation/l10n/is.js +++ b/apps/federation/l10n/is.js @@ -1,15 +1,15 @@ OC.L10N.register( "federation", { - "Server added to the list of trusted ownClouds" : "Þjóninum bætt við listann yfir treyst ownCloud-ský", + "Added to the list of trusted servers" : "Bætt á lista yfir treysta þjóna", "Server is already in the list of trusted servers." : "Þjónninn er nú þegar á listanum yfir treysta þjóna.", - "No ownCloud server found" : "Enginn ownCloud-þjónn fannst", + "No server to federate found" : "Enginn sambandsþjónn fannst", "Could not add server" : "Gat ekki bætt við þjóni", "Federation" : "Samband", - "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." : "ownCloud-samband (federation) gerir þér kleift að tengjast öðrumtreystum ownCloud-skýjum til að skiptast á notendaskrám. Þetta er til dæmis notað til að sjálfklára nöfn ytri notenda við deilingu sambandssameigna.", + "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." : "Þjónasamband (federation) gerir þér kleift að tengjast öðrumtreystum skýjum til að skiptast á notendaskrám. Þetta er til dæmis notað til að sjálfklára nöfn ytri notenda við deilingu sambandssameigna.", "Add server automatically once a federated share was created successfully" : "Bæta þjóni við sjálfkrafa, hafi tekist að búa til sambandssameign", - "Trusted ownCloud Servers" : "Treystir ownCloud-þjónar", - "+ Add ownCloud server" : "+ Bæta við ownCloud-þjóni", - "ownCloud Server" : "ownCloud-þjónn" + "Trusted Servers" : "Treystir þjónar", + "+ Add Nextcloud server" : "+ Bæta við Nextcloud þjóni", + "Nextcloud Server" : "Nextcloud þjónn" }, "nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);"); diff --git a/apps/federation/l10n/is.json b/apps/federation/l10n/is.json index 3754dc12ed7..4f61cb1258b 100644 --- a/apps/federation/l10n/is.json +++ b/apps/federation/l10n/is.json @@ -1,13 +1,13 @@ { "translations": { - "Server added to the list of trusted ownClouds" : "Þjóninum bætt við listann yfir treyst ownCloud-ský", + "Added to the list of trusted servers" : "Bætt á lista yfir treysta þjóna", "Server is already in the list of trusted servers." : "Þjónninn er nú þegar á listanum yfir treysta þjóna.", - "No ownCloud server found" : "Enginn ownCloud-þjónn fannst", + "No server to federate found" : "Enginn sambandsþjónn fannst", "Could not add server" : "Gat ekki bætt við þjóni", "Federation" : "Samband", - "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." : "ownCloud-samband (federation) gerir þér kleift að tengjast öðrumtreystum ownCloud-skýjum til að skiptast á notendaskrám. Þetta er til dæmis notað til að sjálfklára nöfn ytri notenda við deilingu sambandssameigna.", + "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." : "Þjónasamband (federation) gerir þér kleift að tengjast öðrumtreystum skýjum til að skiptast á notendaskrám. Þetta er til dæmis notað til að sjálfklára nöfn ytri notenda við deilingu sambandssameigna.", "Add server automatically once a federated share was created successfully" : "Bæta þjóni við sjálfkrafa, hafi tekist að búa til sambandssameign", - "Trusted ownCloud Servers" : "Treystir ownCloud-þjónar", - "+ Add ownCloud server" : "+ Bæta við ownCloud-þjóni", - "ownCloud Server" : "ownCloud-þjónn" + "Trusted Servers" : "Treystir þjónar", + "+ Add Nextcloud server" : "+ Bæta við Nextcloud þjóni", + "Nextcloud Server" : "Nextcloud þjónn" },"pluralForm" :"nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);" }
\ No newline at end of file diff --git a/apps/files/l10n/fr.js b/apps/files/l10n/fr.js index 49f5204c977..cd725b01164 100644 --- a/apps/files/l10n/fr.js +++ b/apps/files/l10n/fr.js @@ -21,6 +21,7 @@ OC.L10N.register( "Invalid directory." : "Dossier non valide.", "Files" : "Fichiers", "All files" : "Tous les fichiers", + "Recent" : "Récent", "File could not be found" : "Ce fichier n'a pu être trouvé.", "Home" : "Mes fichiers", "Close" : "Fermer", diff --git a/apps/files/l10n/fr.json b/apps/files/l10n/fr.json index e0b5dba2186..60c7a374bd8 100644 --- a/apps/files/l10n/fr.json +++ b/apps/files/l10n/fr.json @@ -19,6 +19,7 @@ "Invalid directory." : "Dossier non valide.", "Files" : "Fichiers", "All files" : "Tous les fichiers", + "Recent" : "Récent", "File could not be found" : "Ce fichier n'a pu être trouvé.", "Home" : "Mes fichiers", "Close" : "Fermer", diff --git a/apps/files/l10n/is.js b/apps/files/l10n/is.js index 579ac88f97f..989dc468f0c 100644 --- a/apps/files/l10n/is.js +++ b/apps/files/l10n/is.js @@ -21,6 +21,8 @@ OC.L10N.register( "Invalid directory." : "Ógild mappa.", "Files" : "Skrár", "All files" : "Allar skrár", + "Recent" : "Nýlegt", + "File could not be found" : "Skrá finnst ekki", "Home" : "Heim", "Close" : "Loka", "Favorites" : "Eftirlæti", @@ -82,6 +84,7 @@ OC.L10N.register( "Storage of {owner} is almost full ({usedSpacePercent}%)" : "Geymslupláss {owner} er næstum fullt ({usedSpacePercent}%)", "Your storage is almost full ({usedSpacePercent}%)" : "Geymsluplássið þitt er næstum fullt ({usedSpacePercent}%)", "_matches '{filter}'_::_match '{filter}'_" : ["samsvarar '{filter}'","samsvara '{filter}'"], + "View in folder" : "Skoða í möppu", "Path" : "Slóð", "_%n byte_::_%n bytes_" : ["%n bæti","%n bæti"], "Favorited" : "Sett í eftirlæti", diff --git a/apps/files/l10n/is.json b/apps/files/l10n/is.json index 2b4f980ca6d..65da27622f8 100644 --- a/apps/files/l10n/is.json +++ b/apps/files/l10n/is.json @@ -19,6 +19,8 @@ "Invalid directory." : "Ógild mappa.", "Files" : "Skrár", "All files" : "Allar skrár", + "Recent" : "Nýlegt", + "File could not be found" : "Skrá finnst ekki", "Home" : "Heim", "Close" : "Loka", "Favorites" : "Eftirlæti", @@ -80,6 +82,7 @@ "Storage of {owner} is almost full ({usedSpacePercent}%)" : "Geymslupláss {owner} er næstum fullt ({usedSpacePercent}%)", "Your storage is almost full ({usedSpacePercent}%)" : "Geymsluplássið þitt er næstum fullt ({usedSpacePercent}%)", "_matches '{filter}'_::_match '{filter}'_" : ["samsvarar '{filter}'","samsvara '{filter}'"], + "View in folder" : "Skoða í möppu", "Path" : "Slóð", "_%n byte_::_%n bytes_" : ["%n bæti","%n bæti"], "Favorited" : "Sett í eftirlæti", diff --git a/apps/files_external/l10n/is.js b/apps/files_external/l10n/is.js index 80d39a2ba9a..ab73dbece96 100644 --- a/apps/files_external/l10n/is.js +++ b/apps/files_external/l10n/is.js @@ -7,6 +7,8 @@ OC.L10N.register( "Step 1 failed. Exception: %s" : "Skref 1 mistókst. Undantekning: %s", "Step 2 failed. Exception: %s" : "Skref 2 mistókst. Undantekning: %s", "External storage" : "Ytri gagnageymsla", + "Dropbox App Configuration" : "Uppsetning Dropbox forrits", + "Google Drive App Configuration" : "Uppsetning Google Drive forrits", "Personal" : "Einka", "System" : "Kerfi", "Grant access" : "Veita aðgengi", @@ -16,8 +18,10 @@ OC.L10N.register( "Error generating key pair" : "Villa við að útbúa nýtt lyklapar", "All users. Type to select user or group." : "Allir notendur. Skrifaðu til að velja notanda eða hóp.", "(group)" : "(hópur)", + "Compatibility with Mac NFD encoding (slow)" : "Samhæfni við Mac NFD kóðun (hægvirkt)", "Admin defined" : "Skilgreindur kerfisstjóri", "Saved" : "Vistað", + "Saving..." : "Er að vista ...", "Save" : "Vista", "Empty response from the server" : "Tómt svar frá þjóni móttekið", "Couldn't access. Please logout and login to activate this mount point" : "Náði ekki aðgangi. Skráðu þig út og svo aftur inn til að virkja þennan tengipunkt", @@ -61,8 +65,11 @@ OC.L10N.register( "Identity endpoint URL" : "Endapunktur auðkennisslóðar (identity endpoint URL)", "Rackspace" : "Rackspace", "API key" : "API-lykill", + "Global Credentials" : "Víðvær innskráningarauðkenni", + "Log-in credentials, save in database" : "Innskráningarauðkenni, vista í gagnagrunni", "Username and password" : "Notandanafn og lykilorð", "Log-in credentials, save in session" : "Innskráningarauðkenni, vista í setu", + "User entered, store in database" : "Innskráður notandi, geyma í gagnagrunni", "RSA public key" : "RSA-dreifilykill", "Public key" : "Dreifilykill", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/is.json b/apps/files_external/l10n/is.json index f5b3a1b1d33..5738ff738fb 100644 --- a/apps/files_external/l10n/is.json +++ b/apps/files_external/l10n/is.json @@ -5,6 +5,8 @@ "Step 1 failed. Exception: %s" : "Skref 1 mistókst. Undantekning: %s", "Step 2 failed. Exception: %s" : "Skref 2 mistókst. Undantekning: %s", "External storage" : "Ytri gagnageymsla", + "Dropbox App Configuration" : "Uppsetning Dropbox forrits", + "Google Drive App Configuration" : "Uppsetning Google Drive forrits", "Personal" : "Einka", "System" : "Kerfi", "Grant access" : "Veita aðgengi", @@ -14,8 +16,10 @@ "Error generating key pair" : "Villa við að útbúa nýtt lyklapar", "All users. Type to select user or group." : "Allir notendur. Skrifaðu til að velja notanda eða hóp.", "(group)" : "(hópur)", + "Compatibility with Mac NFD encoding (slow)" : "Samhæfni við Mac NFD kóðun (hægvirkt)", "Admin defined" : "Skilgreindur kerfisstjóri", "Saved" : "Vistað", + "Saving..." : "Er að vista ...", "Save" : "Vista", "Empty response from the server" : "Tómt svar frá þjóni móttekið", "Couldn't access. Please logout and login to activate this mount point" : "Náði ekki aðgangi. Skráðu þig út og svo aftur inn til að virkja þennan tengipunkt", @@ -59,8 +63,11 @@ "Identity endpoint URL" : "Endapunktur auðkennisslóðar (identity endpoint URL)", "Rackspace" : "Rackspace", "API key" : "API-lykill", + "Global Credentials" : "Víðvær innskráningarauðkenni", + "Log-in credentials, save in database" : "Innskráningarauðkenni, vista í gagnagrunni", "Username and password" : "Notandanafn og lykilorð", "Log-in credentials, save in session" : "Innskráningarauðkenni, vista í setu", + "User entered, store in database" : "Innskráður notandi, geyma í gagnagrunni", "RSA public key" : "RSA-dreifilykill", "Public key" : "Dreifilykill", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/pt_BR.js b/apps/files_external/l10n/pt_BR.js index 39a6e766677..99aa844fb5b 100644 --- a/apps/files_external/l10n/pt_BR.js +++ b/apps/files_external/l10n/pt_BR.js @@ -31,7 +31,7 @@ OC.L10N.register( "External mount error" : "Erro de montagem externa", "external-storage" : "armazenamento-externo", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Não foi possível obter a lista unidades de pontos de montagem da rede do Windows: resposta vazia a partir do servidor", - "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alguns dos pontos de montagem externos configurados não estão conectados. Por favor clique na linha vermelha(s) para mais informações", + "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alguns dos pontos de montagem externos configurados não estão conectados. Clique na(s) linha(s) vermelha(s) para mais informações", "Please enter the credentials for the {mount} mount" : "Por favor, insira as credenciais para montar {mount}", "Username" : "Nome de Usuário", "Password" : "Senha", diff --git a/apps/files_external/l10n/pt_BR.json b/apps/files_external/l10n/pt_BR.json index 7d6a3b5b10d..aee79057b4d 100644 --- a/apps/files_external/l10n/pt_BR.json +++ b/apps/files_external/l10n/pt_BR.json @@ -29,7 +29,7 @@ "External mount error" : "Erro de montagem externa", "external-storage" : "armazenamento-externo", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Não foi possível obter a lista unidades de pontos de montagem da rede do Windows: resposta vazia a partir do servidor", - "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alguns dos pontos de montagem externos configurados não estão conectados. Por favor clique na linha vermelha(s) para mais informações", + "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alguns dos pontos de montagem externos configurados não estão conectados. Clique na(s) linha(s) vermelha(s) para mais informações", "Please enter the credentials for the {mount} mount" : "Por favor, insira as credenciais para montar {mount}", "Username" : "Nome de Usuário", "Password" : "Senha", diff --git a/apps/files_sharing/css/public.css b/apps/files_sharing/css/public.css index c998501dad6..4c5f847f9ff 100644 --- a/apps/files_sharing/css/public.css +++ b/apps/files_sharing/css/public.css @@ -30,10 +30,15 @@ margin:0 auto; } + #imgframe img, #imgframe video { - max-height:100%; - max-width:100%; + max-height: 100% !important; + max-width: 100% !important; +} +#imgframe video { + width: 854px; + height: 480px; } #imgframe .text-preview { diff --git a/apps/files_sharing/l10n/is.js b/apps/files_sharing/l10n/is.js index c598282e23d..edd18ec47be 100644 --- a/apps/files_sharing/l10n/is.js +++ b/apps/files_sharing/l10n/is.js @@ -70,14 +70,20 @@ OC.L10N.register( "No entries found in this folder" : "Engar skrár fundust í þessari möppu", "Name" : "Nafn", "Share time" : "Deilingartími", + "Expiration date" : "Gildir til", "Sorry, this link doesn’t seem to work anymore." : "Því miður, þessi tengill virðist ekki virka lengur.", "Reasons might be:" : "Mögulegar ástæður gætu verið:", "the item was removed" : "atriðið var fjarlægt", "the link expired" : "tengillinn er útrunninn", "sharing is disabled" : "slökkt er á skráadeilingu", "For more info, please ask the person who sent this link." : "Til að vita meira skaltu hafa samband við þann sem sendi þér þennan tengil.", + "Add to your Nextcloud" : "Bæta í þitt eigið Nextcloud", "Download" : "Niðurhal", "Download %s" : "Sækja %s", - "Direct link" : "Beinn tengill" + "Direct link" : "Beinn tengill", + "Upload files to %s" : "Senda inn skrár á %s", + "Select or drop files" : "Veldu eða slepptu skrám", + "Uploading files…" : "Sendi inn skrár…", + "Uploaded files:" : "Innsendar skrár:" }, "nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);"); diff --git a/apps/files_sharing/l10n/is.json b/apps/files_sharing/l10n/is.json index 24b446ec9e8..6a64dd5f292 100644 --- a/apps/files_sharing/l10n/is.json +++ b/apps/files_sharing/l10n/is.json @@ -68,14 +68,20 @@ "No entries found in this folder" : "Engar skrár fundust í þessari möppu", "Name" : "Nafn", "Share time" : "Deilingartími", + "Expiration date" : "Gildir til", "Sorry, this link doesn’t seem to work anymore." : "Því miður, þessi tengill virðist ekki virka lengur.", "Reasons might be:" : "Mögulegar ástæður gætu verið:", "the item was removed" : "atriðið var fjarlægt", "the link expired" : "tengillinn er útrunninn", "sharing is disabled" : "slökkt er á skráadeilingu", "For more info, please ask the person who sent this link." : "Til að vita meira skaltu hafa samband við þann sem sendi þér þennan tengil.", + "Add to your Nextcloud" : "Bæta í þitt eigið Nextcloud", "Download" : "Niðurhal", "Download %s" : "Sækja %s", - "Direct link" : "Beinn tengill" + "Direct link" : "Beinn tengill", + "Upload files to %s" : "Senda inn skrár á %s", + "Select or drop files" : "Veldu eða slepptu skrám", + "Uploading files…" : "Sendi inn skrár…", + "Uploaded files:" : "Innsendar skrár:" },"pluralForm" :"nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);" }
\ No newline at end of file diff --git a/apps/systemtags/admin.php b/apps/systemtags/admin.php new file mode 100644 index 00000000000..45ea577e8ab --- /dev/null +++ b/apps/systemtags/admin.php @@ -0,0 +1,23 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.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/>. + * + */ + +$template = new \OCP\Template('systemtags', 'admin'); +return $template->fetchPage(); diff --git a/apps/systemtags/appinfo/app.php b/apps/systemtags/appinfo/app.php index af91e5fdbcd..5a365c4ef15 100644 --- a/apps/systemtags/appinfo/app.php +++ b/apps/systemtags/appinfo/app.php @@ -78,6 +78,9 @@ $mapperListener = function(MapperEvent $event) use ($activityManager) { $eventDispatcher->addListener(MapperEvent::EVENT_ASSIGN, $mapperListener); $eventDispatcher->addListener(MapperEvent::EVENT_UNASSIGN, $mapperListener); +$app = new \OCA\SystemTags\AppInfo\Application(); +$app->registerAdminPage(); + $l = \OC::$server->getL10N('systemtags'); \OCA\Files\App::getNavigationManager()->add( diff --git a/apps/systemtags/appinfo/info.xml b/apps/systemtags/appinfo/info.xml index 1d75610f545..3521658ac20 100644 --- a/apps/systemtags/appinfo/info.xml +++ b/apps/systemtags/appinfo/info.xml @@ -5,7 +5,7 @@ <description>Collaborative tagging functionality which shares tags among users. Great for teams. (If you are a provider with a multi-tenancy installation, it is advised to deactivate this app as tags are shared.)</description> <licence>AGPL</licence> - <author>Vincent Petry</author> + <author>Vincent Petry, Joas Schilling</author> <default_enable/> <version>1.0.0</version> <dependencies> diff --git a/apps/systemtags/js/admin.js b/apps/systemtags/js/admin.js new file mode 100644 index 00000000000..ed21f82f3ba --- /dev/null +++ b/apps/systemtags/js/admin.js @@ -0,0 +1,164 @@ +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.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/>. + * + */ + +(function() { + if (!OCA.SystemTags) { + /** + * @namespace + */ + OCA.SystemTags = {}; + } + + OCA.SystemTags.Admin = { + + collection: null, + + init: function() { + var self = this; + + this.collection = OC.SystemTags.collection; + this.collection.fetch({ + success: function() { + $('#systemtag').select2(_.extend(self.select2)); + } + }); + + $('#systemtag_submit').on('click', _.bind(this._onClickSubmit, this)); + $('#systemtag_delete').on('click', _.bind(this._onClickDelete, this)); + $('#systemtag_reset').on('click', _.bind(this._onClickReset, this)); + }, + + /** + * Selecting a systemtag in select2 + * + * @param {OC.SystemTags.SystemTagModel} tag + */ + onSelectTag: function (tag) { + var level = 0; + if (tag.get('userVisible')) { + level += 2; + if (tag.get('userAssignable')) { + level += 1; + } + } + + $('#systemtag_name').val(tag.get('name')); + $('#systemtag_level').val(level); + + this._prepareForm(tag.get('id')); + }, + + /** + * Clicking the "Create"/"Update" button + */ + _onClickSubmit: function () { + var level = parseInt($('#systemtag_level').val(), 10), + tagId = $('#systemtags').attr('data-systemtag-id'); + var data = { + name: $('#systemtag_name').val(), + userVisible: level === 2 || level === 3, + userAssignable: level === 3 + }; + + if (tagId) { + var model = this.collection.get(tagId); + model.save(data); + } else { + this.collection.create(data); + } + + this._onClickReset(); + }, + + /** + * Clicking the "Delete" button + */ + _onClickDelete: function () { + var tagId = $('#systemtags').attr('data-systemtag-id'); + var model = this.collection.get(tagId); + model.destroy(); + + this._onClickReset(); + }, + + /** + * Clicking the "Reset" button + */ + _onClickReset: function () { + $('#systemtag_name').val(''); + $('#systemtag_level').val(3); + this._prepareForm(0); + }, + + /** + * Prepare the form for create/update + * + * @param {int} tagId + */ + _prepareForm: function (tagId) { + if (tagId > 0) { + $('#systemtags').attr('data-systemtag-id', tagId); + $('#systemtag_delete').removeClass('hidden'); + $('#systemtag_submit').val(t('systemtags_manager', 'Update')); + } else { + $('#systemtag').select2('val', ''); + $('#systemtags').attr('data-systemtag-id', ''); + $('#systemtag_delete').addClass('hidden'); + $('#systemtag_submit').val(t('systemtags_manager', 'Create')); + } + }, + + /** + * Select2 options for the SystemTag dropdown + */ + select2: { + allowClear: false, + multiple: false, + placeholder: t('systemtags_manager', 'Select tag…'), + query: _.debounce(function(query) { + query.callback({ + results: OCA.SystemTags.Admin.collection.filterByName(query.term) + }); + }, 100, true), + id: function(element) { + return element; + }, + initSelection: function(element, callback) { + var selection = ($(element).val() || []).split('|').sort(); + callback(selection); + }, + formatResult: function (tag) { + return OC.SystemTags.getDescriptiveTag(tag); + }, + formatSelection: function (tag) { + OCA.SystemTags.Admin.onSelectTag(tag); + return OC.SystemTags.getDescriptiveTag(tag); + }, + escapeMarkup: function(m) { + return m; + } + } + }; +})(); + +$(document).ready(function() { + OCA.SystemTags.Admin.init(); +}); + diff --git a/apps/systemtags/l10n/ca.js b/apps/systemtags/l10n/ca.js index 46d9d409662..3506940d2ee 100644 --- a/apps/systemtags/l10n/ca.js +++ b/apps/systemtags/l10n/ca.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s ha des-assignat les marques de sistema de la %3$s a la %2$s", "%s (restricted)" : "%s (restringit)", "%s (invisible)" : "%s (invisible)", + "Name" : "Nom", "No files in here" : "No hi ha arxius", "No entries found in this folder" : "No hi ha entrades en aquesta carpeta", - "Name" : "Nom", "Size" : "Mida", "Modified" : "Modificat" }, diff --git a/apps/systemtags/l10n/ca.json b/apps/systemtags/l10n/ca.json index 9bb1d6fba83..46bdaabfacc 100644 --- a/apps/systemtags/l10n/ca.json +++ b/apps/systemtags/l10n/ca.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s ha des-assignat les marques de sistema de la %3$s a la %2$s", "%s (restricted)" : "%s (restringit)", "%s (invisible)" : "%s (invisible)", + "Name" : "Nom", "No files in here" : "No hi ha arxius", "No entries found in this folder" : "No hi ha entrades en aquesta carpeta", - "Name" : "Nom", "Size" : "Mida", "Modified" : "Modificat" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/systemtags/l10n/cs_CZ.js b/apps/systemtags/l10n/cs_CZ.js index 2af8db9f183..d67754eb67e 100644 --- a/apps/systemtags/l10n/cs_CZ.js +++ b/apps/systemtags/l10n/cs_CZ.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s odebral systémový tag %3$s ze %2$s", "%s (restricted)" : "%s (omezeno)", "%s (invisible)" : "%s (neviditelný)", + "Name" : "Název", "No files in here" : "Žádné soubory", "No entries found in this folder" : "V tomto adresáři nebylo nic nalezeno", - "Name" : "Název", "Size" : "Velikost", "Modified" : "Upraveno" }, diff --git a/apps/systemtags/l10n/cs_CZ.json b/apps/systemtags/l10n/cs_CZ.json index c2c6536daa5..31e4b4bc1ed 100644 --- a/apps/systemtags/l10n/cs_CZ.json +++ b/apps/systemtags/l10n/cs_CZ.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s odebral systémový tag %3$s ze %2$s", "%s (restricted)" : "%s (omezeno)", "%s (invisible)" : "%s (neviditelný)", + "Name" : "Název", "No files in here" : "Žádné soubory", "No entries found in this folder" : "V tomto adresáři nebylo nic nalezeno", - "Name" : "Název", "Size" : "Velikost", "Modified" : "Upraveno" },"pluralForm" :"nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;" diff --git a/apps/systemtags/l10n/de.js b/apps/systemtags/l10n/de.js index 47b60136500..349d1562c77 100644 --- a/apps/systemtags/l10n/de.js +++ b/apps/systemtags/l10n/de.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s hat den System-Tag %3$s von %2$s entfernt", "%s (restricted)" : "%s (eingeschränkt)", "%s (invisible)" : "%s (unsichtbar)", + "Name" : "Name", "No files in here" : "Keine Dateien vorhanden", "No entries found in this folder" : "Keine Einträge in diesem Ordner gefunden", - "Name" : "Name", "Size" : "Größe", "Modified" : "Geändert" }, diff --git a/apps/systemtags/l10n/de.json b/apps/systemtags/l10n/de.json index 100b3b32398..c74987a4722 100644 --- a/apps/systemtags/l10n/de.json +++ b/apps/systemtags/l10n/de.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s hat den System-Tag %3$s von %2$s entfernt", "%s (restricted)" : "%s (eingeschränkt)", "%s (invisible)" : "%s (unsichtbar)", + "Name" : "Name", "No files in here" : "Keine Dateien vorhanden", "No entries found in this folder" : "Keine Einträge in diesem Ordner gefunden", - "Name" : "Name", "Size" : "Größe", "Modified" : "Geändert" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/systemtags/l10n/de_DE.js b/apps/systemtags/l10n/de_DE.js index 9a5566f7572..769a31911c6 100644 --- a/apps/systemtags/l10n/de_DE.js +++ b/apps/systemtags/l10n/de_DE.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s hat den System-Tag %3$s von %2$s entfernt", "%s (restricted)" : "%s (eingeschränkt)", "%s (invisible)" : "%s (unsichtbar)", + "Name" : "Name", "No files in here" : "Keine Dateien vorhanden", "No entries found in this folder" : "Keine Einträge in diesem Ordner gefunden", - "Name" : "Name", "Size" : "Größe", "Modified" : "Geändert" }, diff --git a/apps/systemtags/l10n/de_DE.json b/apps/systemtags/l10n/de_DE.json index 77a5bbf0354..9e97b6c2459 100644 --- a/apps/systemtags/l10n/de_DE.json +++ b/apps/systemtags/l10n/de_DE.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s hat den System-Tag %3$s von %2$s entfernt", "%s (restricted)" : "%s (eingeschränkt)", "%s (invisible)" : "%s (unsichtbar)", + "Name" : "Name", "No files in here" : "Keine Dateien vorhanden", "No entries found in this folder" : "Keine Einträge in diesem Ordner gefunden", - "Name" : "Name", "Size" : "Größe", "Modified" : "Geändert" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/systemtags/l10n/en_GB.js b/apps/systemtags/l10n/en_GB.js index 09487413b04..2e64bf6720f 100644 --- a/apps/systemtags/l10n/en_GB.js +++ b/apps/systemtags/l10n/en_GB.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s unassigned system tag %3$s from %2$s", "%s (restricted)" : "%s (restricted)", "%s (invisible)" : "%s (invisible)", + "Name" : "Name", "No files in here" : "No files in here", "No entries found in this folder" : "No entries found in this folder", - "Name" : "Name", "Size" : "Size", "Modified" : "Modified" }, diff --git a/apps/systemtags/l10n/en_GB.json b/apps/systemtags/l10n/en_GB.json index a6b50bd65c2..865bd76d220 100644 --- a/apps/systemtags/l10n/en_GB.json +++ b/apps/systemtags/l10n/en_GB.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s unassigned system tag %3$s from %2$s", "%s (restricted)" : "%s (restricted)", "%s (invisible)" : "%s (invisible)", + "Name" : "Name", "No files in here" : "No files in here", "No entries found in this folder" : "No entries found in this folder", - "Name" : "Name", "Size" : "Size", "Modified" : "Modified" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/systemtags/l10n/es.js b/apps/systemtags/l10n/es.js index d9701e07a42..a261ab3a162 100644 --- a/apps/systemtags/l10n/es.js +++ b/apps/systemtags/l10n/es.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s eliminó la asignación de etiqueta de sistema %3$s de %2$s", "%s (restricted)" : "%s (restringido)", "%s (invisible)" : "%s (invisible)", + "Name" : "Nombre", "No files in here" : "Aquí no hay archivos", "No entries found in this folder" : "No hay entradas en esta carpeta", - "Name" : "Nombre", "Size" : "Tamaño", "Modified" : "Modificado" }, diff --git a/apps/systemtags/l10n/es.json b/apps/systemtags/l10n/es.json index c310a231202..6c0a32910ea 100644 --- a/apps/systemtags/l10n/es.json +++ b/apps/systemtags/l10n/es.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s eliminó la asignación de etiqueta de sistema %3$s de %2$s", "%s (restricted)" : "%s (restringido)", "%s (invisible)" : "%s (invisible)", + "Name" : "Nombre", "No files in here" : "Aquí no hay archivos", "No entries found in this folder" : "No hay entradas en esta carpeta", - "Name" : "Nombre", "Size" : "Tamaño", "Modified" : "Modificado" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/systemtags/l10n/fr.js b/apps/systemtags/l10n/fr.js index 387bc45643c..d4b9fa83147 100644 --- a/apps/systemtags/l10n/fr.js +++ b/apps/systemtags/l10n/fr.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s a retiré l'étiquette collaborative %3$s à %2$s", "%s (restricted)" : "%s (restreint)", "%s (invisible)" : "%s (invisible)", + "Name" : "Nom", "No files in here" : "Aucun fichier", "No entries found in this folder" : "Aucune entrée trouvée dans ce dossier", - "Name" : "Nom", "Size" : "Taille", "Modified" : "Modifié" }, diff --git a/apps/systemtags/l10n/fr.json b/apps/systemtags/l10n/fr.json index 66b4d246dac..564cac7dd08 100644 --- a/apps/systemtags/l10n/fr.json +++ b/apps/systemtags/l10n/fr.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s a retiré l'étiquette collaborative %3$s à %2$s", "%s (restricted)" : "%s (restreint)", "%s (invisible)" : "%s (invisible)", + "Name" : "Nom", "No files in here" : "Aucun fichier", "No entries found in this folder" : "Aucune entrée trouvée dans ce dossier", - "Name" : "Nom", "Size" : "Taille", "Modified" : "Modifié" },"pluralForm" :"nplurals=2; plural=(n > 1);" diff --git a/apps/systemtags/l10n/he.js b/apps/systemtags/l10n/he.js index 9077fb2dcde..6a7d32cde10 100644 --- a/apps/systemtags/l10n/he.js +++ b/apps/systemtags/l10n/he.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s הסיר/ה שיוך תגית מערכת %3$s מ- %2$s", "%s (restricted)" : "%s (מוגבל)", "%s (invisible)" : "%s (נסתר)", + "Name" : "שם", "No files in here" : "אין כאן קבצים", "No entries found in this folder" : "לא נמצאו כניסות לתיקייה זו", - "Name" : "שם", "Size" : "גודל", "Modified" : "זמן שינוי" }, diff --git a/apps/systemtags/l10n/he.json b/apps/systemtags/l10n/he.json index b67b25afbd9..0d05d252f7e 100644 --- a/apps/systemtags/l10n/he.json +++ b/apps/systemtags/l10n/he.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s הסיר/ה שיוך תגית מערכת %3$s מ- %2$s", "%s (restricted)" : "%s (מוגבל)", "%s (invisible)" : "%s (נסתר)", + "Name" : "שם", "No files in here" : "אין כאן קבצים", "No entries found in this folder" : "לא נמצאו כניסות לתיקייה זו", - "Name" : "שם", "Size" : "גודל", "Modified" : "זמן שינוי" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/systemtags/l10n/hu_HU.js b/apps/systemtags/l10n/hu_HU.js index 783b90ece8a..109175f6447 100644 --- a/apps/systemtags/l10n/hu_HU.js +++ b/apps/systemtags/l10n/hu_HU.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s elvette ezt a rendszer címkét %3$s tőle: %2$s", "%s (restricted)" : "%s (korlátozott)", "%s (invisible)" : "%s (láthatatlan)", + "Name" : "Név", "No files in here" : "Itt nincsenek fájlok", "No entries found in this folder" : "Nincsenek bejegyzések ebben a könyvtárban", - "Name" : "Név", "Size" : "Méret", "Modified" : "Módosítva" }, diff --git a/apps/systemtags/l10n/hu_HU.json b/apps/systemtags/l10n/hu_HU.json index f89da5d3dfd..681f4d14ef5 100644 --- a/apps/systemtags/l10n/hu_HU.json +++ b/apps/systemtags/l10n/hu_HU.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s elvette ezt a rendszer címkét %3$s tőle: %2$s", "%s (restricted)" : "%s (korlátozott)", "%s (invisible)" : "%s (láthatatlan)", + "Name" : "Név", "No files in here" : "Itt nincsenek fájlok", "No entries found in this folder" : "Nincsenek bejegyzések ebben a könyvtárban", - "Name" : "Név", "Size" : "Méret", "Modified" : "Módosítva" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/systemtags/l10n/is.js b/apps/systemtags/l10n/is.js index 079fb251f82..efdeb28fc86 100644 --- a/apps/systemtags/l10n/is.js +++ b/apps/systemtags/l10n/is.js @@ -7,17 +7,25 @@ OC.L10N.register( "Please select tags to filter by" : "Veldu merki til að sía eftir", "No files found for the selected tags" : "Engar skrár fundust með völdum merkjum", "<strong>System tags</strong> for a file have been modified" : "<strong>Kerfismerkjum</strong> á skrá hefur verið breytt", + "You assigned system tag %3$s" : "Þú úthlutaðir kerfismerkinu %3$s", "%1$s assigned system tag %3$s" : "%1$s úthlutaði kerfismerki %3$s", + "You unassigned system tag %3$s" : "Þú tókst af úthlutun kerfismerkisins %3$s", "%1$s unassigned system tag %3$s" : "%1$s tók af úthlutun kerfismerkis %3$s", + "You created system tag %2$s" : "Þú bjóst til kerfismerkið %2$s", "%1$s created system tag %2$s" : "%1$s bjó til kerfismerki %2$s", + "You deleted system tag %2$s" : "Þú eyddir kerfismerkinu %2$s", "%1$s deleted system tag %2$s" : "%1$s eyddi kerfismerki %2$s", + "You updated system tag %3$s to %2$s" : "Þú uppfærðir kerfismerki %3$s í %2$s", "%1$s updated system tag %3$s to %2$s" : "%1$s uppfærði kerfismerki %3$s í %2$s", + "You assigned system tag %3$s to %2$s" : "Þú úthlutaðir kerfismerki %3$s á %2$s", "%1$s assigned system tag %3$s to %2$s" : "%1$s úthlutaði kerfismerki %3$s á %2$s", + "You unassigned system tag %3$s from %2$s" : "Þú tókst kerfismerkið %3$s af %2$s", "%1$s unassigned system tag %3$s from %2$s" : "%1$s tók kerfismerki %3$s af %2$s", + "%s (restricted)" : "%s (takmarkaður aðgangur)", "%s (invisible)" : "%s (ósýnilegt)", + "Name" : "Heiti", "No files in here" : "Engar skrár hér", "No entries found in this folder" : "Engar skrár fundust í þessari möppu", - "Name" : "Heiti", "Size" : "Stærð", "Modified" : "Breytt" }, diff --git a/apps/systemtags/l10n/is.json b/apps/systemtags/l10n/is.json index c51cc851932..d47891dabd1 100644 --- a/apps/systemtags/l10n/is.json +++ b/apps/systemtags/l10n/is.json @@ -5,17 +5,25 @@ "Please select tags to filter by" : "Veldu merki til að sía eftir", "No files found for the selected tags" : "Engar skrár fundust með völdum merkjum", "<strong>System tags</strong> for a file have been modified" : "<strong>Kerfismerkjum</strong> á skrá hefur verið breytt", + "You assigned system tag %3$s" : "Þú úthlutaðir kerfismerkinu %3$s", "%1$s assigned system tag %3$s" : "%1$s úthlutaði kerfismerki %3$s", + "You unassigned system tag %3$s" : "Þú tókst af úthlutun kerfismerkisins %3$s", "%1$s unassigned system tag %3$s" : "%1$s tók af úthlutun kerfismerkis %3$s", + "You created system tag %2$s" : "Þú bjóst til kerfismerkið %2$s", "%1$s created system tag %2$s" : "%1$s bjó til kerfismerki %2$s", + "You deleted system tag %2$s" : "Þú eyddir kerfismerkinu %2$s", "%1$s deleted system tag %2$s" : "%1$s eyddi kerfismerki %2$s", + "You updated system tag %3$s to %2$s" : "Þú uppfærðir kerfismerki %3$s í %2$s", "%1$s updated system tag %3$s to %2$s" : "%1$s uppfærði kerfismerki %3$s í %2$s", + "You assigned system tag %3$s to %2$s" : "Þú úthlutaðir kerfismerki %3$s á %2$s", "%1$s assigned system tag %3$s to %2$s" : "%1$s úthlutaði kerfismerki %3$s á %2$s", + "You unassigned system tag %3$s from %2$s" : "Þú tókst kerfismerkið %3$s af %2$s", "%1$s unassigned system tag %3$s from %2$s" : "%1$s tók kerfismerki %3$s af %2$s", + "%s (restricted)" : "%s (takmarkaður aðgangur)", "%s (invisible)" : "%s (ósýnilegt)", + "Name" : "Heiti", "No files in here" : "Engar skrár hér", "No entries found in this folder" : "Engar skrár fundust í þessari möppu", - "Name" : "Heiti", "Size" : "Stærð", "Modified" : "Breytt" },"pluralForm" :"nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);" diff --git a/apps/systemtags/l10n/it.js b/apps/systemtags/l10n/it.js index 4d6ae6b8a72..05901799052 100644 --- a/apps/systemtags/l10n/it.js +++ b/apps/systemtags/l10n/it.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s ha rimosso l'etichetta di sistema %3$s da %2$s", "%s (restricted)" : "%s (limitato)", "%s (invisible)" : "%s (invisibile)", + "Name" : "Nome", "No files in here" : "Qui non c'è alcun file", "No entries found in this folder" : "Nessuna voce trovata in questa cartella", - "Name" : "Nome", "Size" : "Dimensione", "Modified" : "Modificato" }, diff --git a/apps/systemtags/l10n/it.json b/apps/systemtags/l10n/it.json index fc7204f35a8..98c94b1d39a 100644 --- a/apps/systemtags/l10n/it.json +++ b/apps/systemtags/l10n/it.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s ha rimosso l'etichetta di sistema %3$s da %2$s", "%s (restricted)" : "%s (limitato)", "%s (invisible)" : "%s (invisibile)", + "Name" : "Nome", "No files in here" : "Qui non c'è alcun file", "No entries found in this folder" : "Nessuna voce trovata in questa cartella", - "Name" : "Nome", "Size" : "Dimensione", "Modified" : "Modificato" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/systemtags/l10n/ja.js b/apps/systemtags/l10n/ja.js index a5d2d2d0b38..e9b8d5aab4f 100644 --- a/apps/systemtags/l10n/ja.js +++ b/apps/systemtags/l10n/ja.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s は%2$s から タグ %3$s を解除", "%s (restricted)" : "%s (制限)", "%s (invisible)" : "%s (不可視)", + "Name" : "名前", "No files in here" : "ファイルがありません", "No entries found in this folder" : "このフォルダーにはエントリーがありません", - "Name" : "名前", "Size" : "サイズ", "Modified" : "更新日時" }, diff --git a/apps/systemtags/l10n/ja.json b/apps/systemtags/l10n/ja.json index b0d947b981f..874969fdd00 100644 --- a/apps/systemtags/l10n/ja.json +++ b/apps/systemtags/l10n/ja.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s は%2$s から タグ %3$s を解除", "%s (restricted)" : "%s (制限)", "%s (invisible)" : "%s (不可視)", + "Name" : "名前", "No files in here" : "ファイルがありません", "No entries found in this folder" : "このフォルダーにはエントリーがありません", - "Name" : "名前", "Size" : "サイズ", "Modified" : "更新日時" },"pluralForm" :"nplurals=1; plural=0;" diff --git a/apps/systemtags/l10n/nl.js b/apps/systemtags/l10n/nl.js index 5d9b6ee97c2..765ec8679e2 100644 --- a/apps/systemtags/l10n/nl.js +++ b/apps/systemtags/l10n/nl.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s verwijderde systeemtag %3$s van %2$s", "%s (restricted)" : "%s (beperkt)", "%s (invisible)" : "%s (onzichtbaar)", + "Name" : "Naam", "No files in here" : "Hier geen bestanden", "No entries found in this folder" : "Niets gevonden in deze map", - "Name" : "Naam", "Size" : "Grootte", "Modified" : "Aangepast" }, diff --git a/apps/systemtags/l10n/nl.json b/apps/systemtags/l10n/nl.json index 14a8bfacf8a..1b5fcb1cdf4 100644 --- a/apps/systemtags/l10n/nl.json +++ b/apps/systemtags/l10n/nl.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s verwijderde systeemtag %3$s van %2$s", "%s (restricted)" : "%s (beperkt)", "%s (invisible)" : "%s (onzichtbaar)", + "Name" : "Naam", "No files in here" : "Hier geen bestanden", "No entries found in this folder" : "Niets gevonden in deze map", - "Name" : "Naam", "Size" : "Grootte", "Modified" : "Aangepast" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/systemtags/l10n/pt_BR.js b/apps/systemtags/l10n/pt_BR.js index 0b7bafaab49..cae092c39e1 100644 --- a/apps/systemtags/l10n/pt_BR.js +++ b/apps/systemtags/l10n/pt_BR.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s etiqueta de sistema não atribuída %3$s de %2$s", "%s (restricted)" : "%s (restrito)", "%s (invisible)" : "%s (invisivel)", + "Name" : "Nome", "No files in here" : "Nenhum arquivo aqui", "No entries found in this folder" : "Nenhuma entrada foi encontrada nesta pasta", - "Name" : "Nome", "Size" : "Tamanho", "Modified" : "Modificado" }, diff --git a/apps/systemtags/l10n/pt_BR.json b/apps/systemtags/l10n/pt_BR.json index cb38dfd1de3..27ee74fd8a2 100644 --- a/apps/systemtags/l10n/pt_BR.json +++ b/apps/systemtags/l10n/pt_BR.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s etiqueta de sistema não atribuída %3$s de %2$s", "%s (restricted)" : "%s (restrito)", "%s (invisible)" : "%s (invisivel)", + "Name" : "Nome", "No files in here" : "Nenhum arquivo aqui", "No entries found in this folder" : "Nenhuma entrada foi encontrada nesta pasta", - "Name" : "Nome", "Size" : "Tamanho", "Modified" : "Modificado" },"pluralForm" :"nplurals=2; plural=(n > 1);" diff --git a/apps/systemtags/l10n/pt_PT.js b/apps/systemtags/l10n/pt_PT.js index bf33c8e9af9..d5844a64985 100644 --- a/apps/systemtags/l10n/pt_PT.js +++ b/apps/systemtags/l10n/pt_PT.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s removeu a etiqueta do sistema %3$s de %2$s", "%s (restricted)" : "%s (limitado)", "%s (invisible)" : "%s (invisível)", + "Name" : "Nome", "No files in here" : "Nenhuns ficheiros aqui", "No entries found in this folder" : "Não foram encontradas entradas nesta pasta", - "Name" : "Nome", "Size" : "Tamanho", "Modified" : "Modificado" }, diff --git a/apps/systemtags/l10n/pt_PT.json b/apps/systemtags/l10n/pt_PT.json index 1d6f99bfceb..59565b8053e 100644 --- a/apps/systemtags/l10n/pt_PT.json +++ b/apps/systemtags/l10n/pt_PT.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s removeu a etiqueta do sistema %3$s de %2$s", "%s (restricted)" : "%s (limitado)", "%s (invisible)" : "%s (invisível)", + "Name" : "Nome", "No files in here" : "Nenhuns ficheiros aqui", "No entries found in this folder" : "Não foram encontradas entradas nesta pasta", - "Name" : "Nome", "Size" : "Tamanho", "Modified" : "Modificado" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/systemtags/l10n/ro.js b/apps/systemtags/l10n/ro.js index 762e9406d89..4a2ee02f325 100644 --- a/apps/systemtags/l10n/ro.js +++ b/apps/systemtags/l10n/ro.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s a înlăturat eticheta de sistem %3$s de la %2$s", "%s (restricted)" : "%s (restricționat)", "%s (invisible)" : "%s (invizibil)", + "Name" : "Nume", "No files in here" : "Niciun fișier aici", "No entries found in this folder" : "Niciun element găsit în acest director", - "Name" : "Nume", "Size" : "Mărime", "Modified" : "Modificat" }, diff --git a/apps/systemtags/l10n/ro.json b/apps/systemtags/l10n/ro.json index ef1c127d145..20517cf41a2 100644 --- a/apps/systemtags/l10n/ro.json +++ b/apps/systemtags/l10n/ro.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s a înlăturat eticheta de sistem %3$s de la %2$s", "%s (restricted)" : "%s (restricționat)", "%s (invisible)" : "%s (invizibil)", + "Name" : "Nume", "No files in here" : "Niciun fișier aici", "No entries found in this folder" : "Niciun element găsit în acest director", - "Name" : "Nume", "Size" : "Mărime", "Modified" : "Modificat" },"pluralForm" :"nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));" diff --git a/apps/systemtags/l10n/ru.js b/apps/systemtags/l10n/ru.js index 97e27549a01..6f148ee51c7 100644 --- a/apps/systemtags/l10n/ru.js +++ b/apps/systemtags/l10n/ru.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s убрал системную метку %3$s с %2$s", "%s (restricted)" : "%s (ограничено)", "%s (invisible)" : "%s (невидимые)", + "Name" : "Имя", "No files in here" : "Здесь нет файлов", "No entries found in this folder" : "Нет элементов в этом каталоге", - "Name" : "Имя", "Size" : "Размер", "Modified" : "Изменён" }, diff --git a/apps/systemtags/l10n/ru.json b/apps/systemtags/l10n/ru.json index 39a27781fcf..718390ce639 100644 --- a/apps/systemtags/l10n/ru.json +++ b/apps/systemtags/l10n/ru.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s убрал системную метку %3$s с %2$s", "%s (restricted)" : "%s (ограничено)", "%s (invisible)" : "%s (невидимые)", + "Name" : "Имя", "No files in here" : "Здесь нет файлов", "No entries found in this folder" : "Нет элементов в этом каталоге", - "Name" : "Имя", "Size" : "Размер", "Modified" : "Изменён" },"pluralForm" :"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);" diff --git a/apps/systemtags/l10n/sl.js b/apps/systemtags/l10n/sl.js index 727f03b8eb0..eda8054c9bf 100644 --- a/apps/systemtags/l10n/sl.js +++ b/apps/systemtags/l10n/sl.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "Uporabnik %1$s je prevzel sistemsko oznako %3$s od %2$s", "%s (restricted)" : "%s (omejeno)", "%s (invisible)" : "%s (nevidno)", + "Name" : "Ime", "No files in here" : "V mapi ni datotek", "No entries found in this folder" : "V tej mapi ni najdenih predmetov.", - "Name" : "Ime", "Size" : "Velikost", "Modified" : "Spremenjeno" }, diff --git a/apps/systemtags/l10n/sl.json b/apps/systemtags/l10n/sl.json index 6ec3bf25957..9231aeabf9c 100644 --- a/apps/systemtags/l10n/sl.json +++ b/apps/systemtags/l10n/sl.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "Uporabnik %1$s je prevzel sistemsko oznako %3$s od %2$s", "%s (restricted)" : "%s (omejeno)", "%s (invisible)" : "%s (nevidno)", + "Name" : "Ime", "No files in here" : "V mapi ni datotek", "No entries found in this folder" : "V tej mapi ni najdenih predmetov.", - "Name" : "Ime", "Size" : "Velikost", "Modified" : "Spremenjeno" },"pluralForm" :"nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);" diff --git a/apps/systemtags/l10n/sq.js b/apps/systemtags/l10n/sq.js index 284bb09fb6e..9312d671bd1 100644 --- a/apps/systemtags/l10n/sq.js +++ b/apps/systemtags/l10n/sq.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s hoqi prej %2$s etiketën e sistemit %3$s", "%s (restricted)" : "%s (e kufizuar)", "%s (invisible)" : "%s (e padukshme)", + "Name" : "Emër", "No files in here" : "S’ka kartela këtu", "No entries found in this folder" : "S’u gjetën zëra në këtë dosje", - "Name" : "Emër", "Size" : "Madhësi", "Modified" : "Ndryshuar më" }, diff --git a/apps/systemtags/l10n/sq.json b/apps/systemtags/l10n/sq.json index 8d2b6b6a33e..eed88992e5a 100644 --- a/apps/systemtags/l10n/sq.json +++ b/apps/systemtags/l10n/sq.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s hoqi prej %2$s etiketën e sistemit %3$s", "%s (restricted)" : "%s (e kufizuar)", "%s (invisible)" : "%s (e padukshme)", + "Name" : "Emër", "No files in here" : "S’ka kartela këtu", "No entries found in this folder" : "S’u gjetën zëra në këtë dosje", - "Name" : "Emër", "Size" : "Madhësi", "Modified" : "Ndryshuar më" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/systemtags/l10n/sv.js b/apps/systemtags/l10n/sv.js index 0a2be892f0b..be11b9f9ad3 100644 --- a/apps/systemtags/l10n/sv.js +++ b/apps/systemtags/l10n/sv.js @@ -23,9 +23,9 @@ OC.L10N.register( "%1$s unassigned system tag %3$s from %2$s" : "%1$s tog bort tilldelad systemtagg %3$s från %2$s", "%s (restricted)" : "%s (begränsad)", "%s (invisible)" : "%s (osynlig)", + "Name" : "Namn", "No files in here" : "Inga filer kunde hittas", "No entries found in this folder" : "nga Filer hittades i denna mapp", - "Name" : "Namn", "Size" : "Storlek", "Modified" : "Ändrad" }, diff --git a/apps/systemtags/l10n/sv.json b/apps/systemtags/l10n/sv.json index ac18892d320..214bd3b100a 100644 --- a/apps/systemtags/l10n/sv.json +++ b/apps/systemtags/l10n/sv.json @@ -21,9 +21,9 @@ "%1$s unassigned system tag %3$s from %2$s" : "%1$s tog bort tilldelad systemtagg %3$s från %2$s", "%s (restricted)" : "%s (begränsad)", "%s (invisible)" : "%s (osynlig)", + "Name" : "Namn", "No files in here" : "Inga filer kunde hittas", "No entries found in this folder" : "nga Filer hittades i denna mapp", - "Name" : "Namn", "Size" : "Storlek", "Modified" : "Ändrad" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/systemtags/lib/AppInfo/Application.php b/apps/systemtags/lib/AppInfo/Application.php new file mode 100644 index 00000000000..7cd49d6424b --- /dev/null +++ b/apps/systemtags/lib/AppInfo/Application.php @@ -0,0 +1,37 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.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 OCA\SystemTags\AppInfo; + +use OCP\AppFramework\App; + +class Application extends App { + public function __construct() { + parent::__construct('systemtags'); + } + + /** + * Register admin settings + */ + public function registerAdminPage() { + \OCP\App::registerAdmin($this->getContainer()->getAppName(), 'admin'); + } +} diff --git a/apps/systemtags/templates/admin.php b/apps/systemtags/templates/admin.php new file mode 100644 index 00000000000..883e998ed61 --- /dev/null +++ b/apps/systemtags/templates/admin.php @@ -0,0 +1,59 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.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/>. + * + */ + +vendor_script('core', 'select2/select2'); +vendor_style('core', 'select2/select2'); +script('core', [ + 'oc-backbone-webdav', + 'systemtags/systemtags', + 'systemtags/systemtagmodel', + 'systemtags/systemtagscollection', +]); + +script('systemtags', 'admin'); + +/** @var \OCP\IL10N $l */ +?> + +<form id="systemtags" class="section" data-systemtag-id=""> + <h2><?php p($l->t('Collaborative tags')); ?></h2> + + <input type="hidden" name="systemtag" id="systemtag" placeholder="<?php p($l->t('Select tag…')); ?>" style="width: 400px;" /> + + <br><br> + + <input type="text" id="systemtag_name" name="systemtag_name" placeholder="<?php p($l->t('Name')); ?>" style="width: 200px;"> + + <span id="systemtag_delete" class="hidden"> + <img src="<?php p(\OCP\Template::image_path('core', 'actions/delete.svg')); ?>" alt="<?php p($l->t('Delete')); ?>"> + </span> + + <br> + + <select id="systemtag_level"> + <option value="3"><?php p($l->t('Public')); ?></option> + <option value="2"><?php p($l->t('Restricted')); ?></option> + <option value="0"><?php p($l->t('Invisible')); ?></option> + </select> + + <input type="button" id="systemtag_submit" value="<?php p($l->t('Create')); ?>"> + <input type="button" id="systemtag_reset" value="<?php p($l->t('Reset')); ?>"> +</form> diff --git a/apps/theming/appinfo/app.php b/apps/theming/appinfo/app.php index 76580879ca1..5ef506e5acd 100644 --- a/apps/theming/appinfo/app.php +++ b/apps/theming/appinfo/app.php @@ -31,7 +31,7 @@ $linkToCSS = \OC::$server->getURLGenerator()->linkToRoute( 'v' => \OC::$server->getConfig()->getAppValue('theming', 'cachebuster', '0'), ] ); -\OC_Util::addHeader( +\OCP\Util::addHeader( 'link', [ 'rel' => 'stylesheet', diff --git a/apps/theming/appinfo/routes.php b/apps/theming/appinfo/routes.php index 073dacb789c..e062a68d69d 100644 --- a/apps/theming/appinfo/routes.php +++ b/apps/theming/appinfo/routes.php @@ -24,9 +24,7 @@ * */ -namespace OCA\Theming\AppInfo; - -(new \OCP\AppFramework\App('theming'))->registerRoutes($this, array('routes' => array( +return ['routes' => [ [ 'name' => 'Theming#updateStylesheet', 'url' => '/ajax/updateStylesheet', @@ -57,5 +55,5 @@ namespace OCA\Theming\AppInfo; 'url' => '/loginbackground', 'verb' => 'GET', ], -))); +]]; diff --git a/apps/theming/js/settings-admin.js b/apps/theming/js/settings-admin.js index 941ec5c711b..01ff9123842 100644 --- a/apps/theming/js/settings-admin.js +++ b/apps/theming/js/settings-admin.js @@ -46,29 +46,46 @@ function calculateLuminance(rgb) { return (0.299*r + 0.587*g + 0.114*b)/255; } +function generateRadioButton(color) { + var radioButton = '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">' + + '<path d="M8 1a7 7 0 0 0-7 7 7 7 0 0 0 7 7 7 7 0 0 0 7-7 7 7 0 0 0-7-7zm0 1a6 6 0 0 1 6 6 6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6zm0 2a4 4 0 1 0 0 8 4 4 0 0 0 0-8z" fill="' + color + '"/></svg>'; + return btoa(radioButton); +} + function preview(setting, value) { if (setting === 'color') { var headerClass = document.getElementById('header'); var expandDisplayNameClass = document.getElementById('expandDisplayName'); var headerAppName = headerClass.getElementsByClassName('header-appname')[0]; var textColor, icon; + var luminance = calculateLuminance(value); + var elementColor = value; - if (calculateLuminance(value) > 0.5) { + if (luminance > 0.5) { textColor = "#000000"; icon = 'caret-dark'; } else { textColor = "#ffffff"; icon = 'caret'; } + if (luminance>0.8) { + elementColor = '#555555'; + } headerClass.style.background = value; headerClass.style.backgroundImage = '../img/logo-icon.svg'; expandDisplayNameClass.style.color = textColor; headerAppName.style.color = textColor; - $(headerClass).find('.icon-caret').each(function() { - $(this).css('background-image', "url('" + OC.getRootPath() + '/core/img/actions/' + icon + ".svg')"); - }); + $('#previewStyles').html( + '#header .icon-caret { background-image: url(\'' + OC.getRootPath() + '/core/img/actions/' + icon + '.svg\') }' + + 'input[type="checkbox"].checkbox:checked:enabled:not(.checkbox--white) + label:before {' + + 'background-image:url(\'' + OC.getRootPath() + '/core/img/actions/checkmark-white.svg\');' + + 'background-color: ' + elementColor + '; background-position: center center; background-size:contain;' + + 'width:12px; height:12px; padding:0; margin:2px 6px 6px 2px; border-radius:1px;}' + + 'input[type="radio"].radio:checked:not(.radio--white):not(:disabled) + label:before {' + + 'background-image: url(\'data:image/svg+xml;base64,' + generateRadioButton(elementColor) + '\'); }' + ); } if (setting === 'logoMime') { console.log(setting); @@ -87,6 +104,8 @@ function preview(setting, value) { $(document).ready(function () { $('#theming [data-toggle="tooltip"]').tooltip(); + $('html > head').append($('<style type="text/css" id="previewStyles"></style>')); + var uploadParamsLogo = { pasteZone: null, dropZone: null, diff --git a/apps/theming/lib/controller/themingcontroller.php b/apps/theming/lib/Controller/ThemingController.php index 3e5d6f3e0d1..55391619f3c 100644 --- a/apps/theming/lib/controller/themingcontroller.php +++ b/apps/theming/lib/Controller/ThemingController.php @@ -30,7 +30,10 @@ namespace OCA\Theming\Controller; use OCA\Theming\Template; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataDownloadResponse; use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\Http\StreamResponse; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\Files\IRootFolder; use OCP\IConfig; use OCP\IL10N; @@ -47,6 +50,10 @@ use OCA\Theming\Util; class ThemingController extends Controller { /** @var Template */ private $template; + /** @var Util */ + private $util; + /** @var ITimeFactory */ + private $timeFactory; /** @var IL10N */ private $l; /** @var IConfig */ @@ -61,6 +68,8 @@ class ThemingController extends Controller { * @param IRequest $request * @param IConfig $config * @param Template $template + * @param Util $util + * @param ITimeFactory $timeFactory * @param IL10N $l * @param IRootFolder $rootFolder */ @@ -69,12 +78,16 @@ class ThemingController extends Controller { IRequest $request, IConfig $config, Template $template, + Util $util, + ITimeFactory $timeFactory, IL10N $l, IRootFolder $rootFolder ) { parent::__construct($appName, $request); $this->template = $template; + $this->util = $util; + $this->timeFactory = $timeFactory; $this->l = $l; $this->config = $config; $this->rootFolder = $rootFolder; @@ -166,7 +179,7 @@ class ThemingController extends Controller { * @PublicPage * @NoCSRFRequired * - * @return Http\StreamResponse + * @return StreamResponse|DataResponse */ public function getLogo() { $pathToLogo = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data/') . '/themedinstancelogo'; @@ -174,10 +187,9 @@ class ThemingController extends Controller { return new DataResponse(); } - \OC_Response::setExpiresHeader(gmdate('D, d M Y H:i:s', time() + (60*60*24*45)) . ' GMT'); - \OC_Response::enableCaching(); $response = new Http\StreamResponse($pathToLogo); $response->cacheFor(3600); + $response->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime())); $response->addHeader('Content-Disposition', 'attachment'); $response->addHeader('Content-Type', $this->config->getAppValue($this->appName, 'logoMime', '')); return $response; @@ -187,7 +199,7 @@ class ThemingController extends Controller { * @PublicPage * @NoCSRFRequired * - * @return Http\StreamResponse + * @return StreamResponse|DataResponse */ public function getLoginBackground() { $pathToLogo = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data/') . '/themedbackgroundlogo'; @@ -195,10 +207,9 @@ class ThemingController extends Controller { return new DataResponse(); } - \OC_Response::setExpiresHeader(gmdate('D, d M Y H:i:s', time() + (60*60*24*45)) . ' GMT'); - \OC_Response::enableCaching(); - $response = new Http\StreamResponse($pathToLogo); + $response = new StreamResponse($pathToLogo); $response->cacheFor(3600); + $response->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime())); $response->addHeader('Content-Disposition', 'attachment'); $response->addHeader('Content-Type', $this->config->getAppValue($this->appName, 'backgroundMime', '')); return $response; @@ -208,46 +219,72 @@ class ThemingController extends Controller { * @NoCSRFRequired * @PublicPage * - * @return Http\DataDownloadResponse + * @return DataDownloadResponse */ public function getStylesheet() { $cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0'); $responseCss = ''; $color = $this->config->getAppValue($this->appName, 'color'); + $elementColor = $this->util->elementColor($color); if($color !== '') { $responseCss .= sprintf( - '#body-user #header,#body-settings #header,#body-public #header,#body-login,.searchbox input[type="search"]:focus,.searchbox input[type="search"]:active,.searchbox input[type="search"]:valid {background-color: %s}', + '#body-user #header,#body-settings #header,#body-public #header,#body-login,.searchbox input[type="search"]:focus,.searchbox input[type="search"]:active,.searchbox input[type="search"]:valid {background-color: %s}' . "\n", $color ); + $responseCss .= sprintf('input[type="checkbox"].checkbox:checked:enabled:not(.checkbox--white) + label:before {' . + 'background-image:url(\'%s/core/img/actions/checkmark-white.svg\');' . + 'background-color: %s; background-position: center center; background-size:contain;' . + 'width:12px; height:12px; padding:0; margin:2px 6px 6px 2px; border-radius:1px;' . + "}\n", + \OC::$WEBROOT, + $elementColor + ); + $responseCss .= 'input[type="radio"].radio:checked:not(.radio--white):not(:disabled) + label:before {' . + 'background-image: url(\'data:image/svg+xml;base64,'.$this->util->generateRadioButton($elementColor).'\');' . + "}\n"; + $responseCss .= ' + #firstrunwizard .firstrunwizard-header { + background-color: ' . $color . '; + } + #firstrunwizard p a { + color: ' . $color . '; + } + '; + } $logo = $this->config->getAppValue($this->appName, 'logoMime'); if($logo !== '') { - $responseCss .= sprintf('#header .logo { - background-image: url(\'./logo?v='.$cacheBusterValue.'\'); - background-size: contain; - } - #header .logo-icon { - background-image: url(\'./logo?v='.$cacheBusterValue.'\'); - background-size: contain; - }' + $responseCss .= sprintf( + '#header .logo {' . + 'background-image: url(\'./logo?v='.$cacheBusterValue.'\')' . + 'background-size: contain;' . + '}' . "\n" . + '#header .logo-icon {' . + 'background-image: url(\'./logo?v='.$cacheBusterValue.'\');' . + 'background-size: contain;' . + '}' . "\n" . + '#firstrunwizard .firstrunwizard-header .logo {' . + 'background-image: url(\'./logo?v='.$cacheBusterValue.'\');' . + 'background-size: contain;' . + '}' . "\n" ); } $backgroundLogo = $this->config->getAppValue($this->appName, 'backgroundMime'); if($backgroundLogo !== '') { - $responseCss .= '#body-login { - background-image: url(\'./loginbackground?v='.$cacheBusterValue.'\'); - }'; + $responseCss .= '#body-login {background-image: url(\'./loginbackground?v='.$cacheBusterValue.'\');}' . "\n"; + $responseCss .= '#firstrunwizard .firstrunwizard-header {' . + 'background-image: url(\'./loginbackground?v='.$cacheBusterValue.'\');' . + '}' . "\n"; } - if(Util::invertTextColor($color)) { - $responseCss .= '#header .header-appname, #expandDisplayName { color: #000000; } '; - $responseCss .= '#header .icon-caret { background-image: url(\'' . \OC::$WEBROOT . '/core/img/actions/caret-dark.svg\'); } '; - $responseCss .= '.searchbox input[type="search"] { background: transparent url(\'' . \OC::$WEBROOT . '/core/img/actions/search.svg\') no-repeat 6px center; color: #000; }'; - $responseCss .= '.searchbox input[type="search"]:focus,.searchbox input[type="search"]:active,.searchbox input[type="search"]:valid { color: #000; border: 1px solid rgba(0, 0, 0, .5); }'; + if($this->util->invertTextColor($color)) { + $responseCss .= '#header .header-appname, #expandDisplayName { color: #000000; }' . "\n"; + $responseCss .= '#header .icon-caret { background-image: url(\'' . \OC::$WEBROOT . '/core/img/actions/caret-dark.svg\'); }' . "\n"; + $responseCss .= '.searchbox input[type="search"] { background: transparent url(\'' . \OC::$WEBROOT . '/core/img/actions/search.svg\') no-repeat 6px center; color: #000; }' . "\n"; + $responseCss .= '.searchbox input[type="search"]:focus,.searchbox input[type="search"]:active,.searchbox input[type="search"]:valid { color: #000; border: 1px solid rgba(0, 0, 0, .5); }' . "\n"; } - \OC_Response::setExpiresHeader(gmdate('D, d M Y H:i:s', time() + (60*60*24*45)) . ' GMT'); - \OC_Response::enableCaching(); - $response = new Http\DataDownloadResponse($responseCss, 'style', 'text/css'); + $response = new DataDownloadResponse($responseCss, 'style', 'text/css'); + $response->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime())); $response->cacheFor(3600); return $response; } diff --git a/apps/theming/lib/template.php b/apps/theming/lib/Template.php index 8cd2befc1d1..25730aad95b 100644 --- a/apps/theming/lib/template.php +++ b/apps/theming/lib/Template.php @@ -40,7 +40,7 @@ use OCP\IURLGenerator; class Template extends \OC_Defaults { /** @var IConfig */ private $config; - /** @var IL10N */ + /** @var IL10N */ private $l; /** @var IURLGenerator */ private $urlGenerator; diff --git a/apps/theming/lib/util.php b/apps/theming/lib/Util.php index 2088650b19d..71ed0958e42 100644 --- a/apps/theming/lib/util.php +++ b/apps/theming/lib/Util.php @@ -29,8 +29,8 @@ class Util { * @param string $color rgb color value * @return bool */ - public static function invertTextColor($color) { - $l = self::calculateLuminance($color); + public function invertTextColor($color) { + $l = $this->calculateLuminance($color); if($l>0.5) { return true; } else { @@ -39,10 +39,25 @@ class Util { } /** + * get color for on-page elements: + * theme color by default, grey if theme color is to bright + * @param $color + * @return string + */ + public function elementColor($color) { + $l = $this->calculateLuminance($color); + if($l>0.8) { + return '#555555'; + } else { + return $color; + } + } + + /** * @param string $color rgb color value * @return float */ - public static function calculateLuminance($color) { + public function calculateLuminance($color) { $hex = preg_replace("/[^0-9A-Fa-f]/", '', $color); if (strlen($hex) === 3) { $hex = $hex{0} . $hex{0} . $hex{1} . $hex{1} . $hex{2} . $hex{2}; @@ -56,4 +71,14 @@ class Util { return (0.299 * $r + 0.587 * $g + 0.114 * $b)/255; } + /** + * @param $color + * @return string base64 encoded radio button svg + */ + public function generateRadioButton($color) { + $radioButtonIcon = '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">' . + '<path d="M8 1a7 7 0 0 0-7 7 7 7 0 0 0 7 7 7 7 0 0 0 7-7 7 7 0 0 0-7-7zm0 1a6 6 0 0 1 6 6 6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6zm0 2a4 4 0 1 0 0 8 4 4 0 0 0 0-8z" fill="'.$color.'"/></svg>'; + return base64_encode($radioButtonIcon); + } + } diff --git a/apps/theming/settings/settings-admin.php b/apps/theming/settings/settings-admin.php index d9aa05cfca0..8ef499789e8 100644 --- a/apps/theming/settings/settings-admin.php +++ b/apps/theming/settings/settings-admin.php @@ -23,8 +23,6 @@ * */ -\OC_Util::checkAdminUser(); - $config = \OC::$server->getConfig(); $l = \OC::$server->getL10N('theming'); $urlGenerator = \OC::$server->getURLGenerator(); @@ -40,7 +38,7 @@ if ($theme !== '') { $errorMessage = $l->t('You already use a custom theme'); } -$template = new OCP\Template('theming', 'settings-admin'); +$template = new \OCP\Template('theming', 'settings-admin'); $template->assign('themable', $themable); $template->assign('errorMessage', $errorMessage); diff --git a/apps/theming/tests/lib/controller/ThemingControllerTest.php b/apps/theming/tests/Controller/ThemingControllerTest.php index 24eb0510f99..933faf8a0a1 100644 --- a/apps/theming/tests/lib/controller/ThemingControllerTest.php +++ b/apps/theming/tests/Controller/ThemingControllerTest.php @@ -26,6 +26,7 @@ namespace OCA\Theming\Tests\Controller; use OCA\Theming\Controller\ThemingController; use OCA\Theming\Template; +use OCA\Theming\Util; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\Files\IRootFolder; @@ -41,6 +42,10 @@ class ThemingControllerTest extends TestCase { private $config; /** @var Template */ private $template; + /** @var Util */ + private $util; + /** @var \OCP\AppFramework\Utility\ITimeFactory */ + private $timeFactory; /** @var IL10N */ private $l10n; /** @var ThemingController */ @@ -53,14 +58,24 @@ class ThemingControllerTest extends TestCase { $this->config = $this->getMock('\\OCP\\IConfig'); $this->template = $this->getMockBuilder('\\OCA\\Theming\\Template') ->disableOriginalConstructor()->getMock(); + $this->util = new Util(); + $this->timeFactory = $this->getMockBuilder('OCP\AppFramework\Utility\ITimeFactory') + ->disableOriginalConstructor() + ->getMock(); $this->l10n = $this->getMock('\\OCP\\IL10N'); $this->rootFolder = $this->getMock('\\OCP\\Files\\IRootFolder'); + $this->timeFactory->expects($this->any()) + ->method('getTime') + ->willReturn(123); + $this->themingController = new ThemingController( 'theming', $this->request, $this->config, $this->template, + $this->util, + $this->timeFactory, $this->l10n, $this->rootFolder ); @@ -272,6 +287,7 @@ class ThemingControllerTest extends TestCase { @$expected = new Http\StreamResponse($tmpLogo); $expected->cacheFor(3600); + $expected->addHeader('Expires', date(\DateTime::RFC2822, 123)); $expected->addHeader('Content-Disposition', 'attachment'); $expected->addHeader('Content-Type', 'text/svg'); @$this->assertEquals($expected, $this->themingController->getLogo()); @@ -300,12 +316,16 @@ class ThemingControllerTest extends TestCase { @$expected = new Http\StreamResponse($tmpLogo); $expected->cacheFor(3600); + $expected->addHeader('Expires', date(\DateTime::RFC2822, 123)); $expected->addHeader('Content-Disposition', 'attachment'); $expected->addHeader('Content-Type', 'image/png'); @$this->assertEquals($expected, $this->themingController->getLoginBackground()); } public function testGetStylesheetWithOnlyColor() { + + $color = '#000'; + $this->config ->expects($this->at(0)) ->method('getAppValue') @@ -315,7 +335,7 @@ class ThemingControllerTest extends TestCase { ->expects($this->at(1)) ->method('getAppValue') ->with('theming', 'color', '') - ->willReturn('#000'); + ->willReturn($color); $this->config ->expects($this->at(2)) ->method('getAppValue') @@ -327,12 +347,42 @@ class ThemingControllerTest extends TestCase { ->with('theming', 'backgroundMime', '') ->willReturn(''); - $expected = new Http\DataDownloadResponse('#body-user #header,#body-settings #header,#body-public #header,#body-login,.searchbox input[type="search"]:focus,.searchbox input[type="search"]:active,.searchbox input[type="search"]:valid {background-color: #000}', 'style', 'text/css'); + $expectedData = sprintf( + '#body-user #header,#body-settings #header,#body-public #header,#body-login,.searchbox input[type="search"]:focus,.searchbox input[type="search"]:active,.searchbox input[type="search"]:valid {background-color: %s}' . "\n", + $color + ); + $expectedData .= sprintf('input[type="checkbox"].checkbox:checked:enabled:not(.checkbox--white) + label:before {' . + 'background-image:url(\'%s/core/img/actions/checkmark-white.svg\');' . + 'background-color: %s; background-position: center center; background-size:contain;' . + 'width:12px; height:12px; padding:0; margin:2px 6px 6px 2px; border-radius:1px;' . + "}\n", + \OC::$WEBROOT, + $color + ); + $expectedData .= 'input[type="radio"].radio:checked:not(.radio--white):not(:disabled) + label:before {' . + 'background-image: url(\'data:image/svg+xml;base64,'.$this->util->generateRadioButton($color).'\');' . + "}\n"; + + $expectedData .= ' + #firstrunwizard .firstrunwizard-header { + background-color: ' . $color . '; + } + #firstrunwizard p a { + color: ' . $color . '; + } + '; + + $expected = new Http\DataDownloadResponse($expectedData, 'style', 'text/css'); + $expected->cacheFor(3600); + $expected->addHeader('Expires', date(\DateTime::RFC2822, 123)); @$this->assertEquals($expected, $this->themingController->getStylesheet()); } public function testGetStylesheetWithOnlyColorInvert() { + + $color = '#fff'; + $this->config ->expects($this->at(0)) ->method('getAppValue') @@ -342,7 +392,7 @@ class ThemingControllerTest extends TestCase { ->expects($this->at(1)) ->method('getAppValue') ->with('theming', 'color', '') - ->willReturn('#fff'); + ->willReturn($color); $this->config ->expects($this->at(2)) ->method('getAppValue') @@ -354,8 +404,39 @@ class ThemingControllerTest extends TestCase { ->with('theming', 'backgroundMime', '') ->willReturn(''); - $expected = new Http\DataDownloadResponse('#body-user #header,#body-settings #header,#body-public #header,#body-login,.searchbox input[type="search"]:focus,.searchbox input[type="search"]:active,.searchbox input[type="search"]:valid {background-color: #fff}#header .header-appname, #expandDisplayName { color: #000000; } #header .icon-caret { background-image: url(\'' . \OC::$WEBROOT . '/core/img/actions/caret-dark.svg\'); } .searchbox input[type="search"] { background: transparent url(\'' . \OC::$WEBROOT . '/core/img/actions/search.svg\') no-repeat 6px center; color: #000; }.searchbox input[type="search"]:focus,.searchbox input[type="search"]:active,.searchbox input[type="search"]:valid { color: #000; border: 1px solid rgba(0, 0, 0, .5); }', 'style', 'text/css'); + $expectedData = sprintf( + '#body-user #header,#body-settings #header,#body-public #header,#body-login,.searchbox input[type="search"]:focus,.searchbox input[type="search"]:active,.searchbox input[type="search"]:valid {background-color: %s}' . "\n", + $color + ); + $expectedData .= sprintf('input[type="checkbox"].checkbox:checked:enabled:not(.checkbox--white) + label:before {' . + 'background-image:url(\'%s/core/img/actions/checkmark-white.svg\');' . + 'background-color: #555555; background-position: center center; background-size:contain;' . + 'width:12px; height:12px; padding:0; margin:2px 6px 6px 2px; border-radius:1px;' . + "}\n", + \OC::$WEBROOT + ); + $expectedData .= 'input[type="radio"].radio:checked:not(.radio--white):not(:disabled) + label:before {' . + 'background-image: url(\'data:image/svg+xml;base64,'.$this->util->generateRadioButton('#555555').'\');' . + "}\n"; + + $expectedData .= ' + #firstrunwizard .firstrunwizard-header { + background-color: ' . $color . '; + } + #firstrunwizard p a { + color: ' . $color . '; + } + '; + $expectedData .= '#header .header-appname, #expandDisplayName { color: #000000; }' . "\n"; + $expectedData .= '#header .icon-caret { background-image: url(\'' . \OC::$WEBROOT . '/core/img/actions/caret-dark.svg\'); }' . "\n"; + $expectedData .= '.searchbox input[type="search"] { background: transparent url(\'' . \OC::$WEBROOT . '/core/img/actions/search.svg\') no-repeat 6px center; color: #000; }' . "\n"; + $expectedData .= '.searchbox input[type="search"]:focus,.searchbox input[type="search"]:active,.searchbox input[type="search"]:valid { color: #000; border: 1px solid rgba(0, 0, 0, .5); }' . "\n"; + + + $expected = new Http\DataDownloadResponse($expectedData, 'style', 'text/css'); + $expected->cacheFor(3600); + $expected->addHeader('Expires', date(\DateTime::RFC2822, 123)); @$this->assertEquals($expected, $this->themingController->getStylesheet()); } @@ -381,15 +462,23 @@ class ThemingControllerTest extends TestCase { ->with('theming', 'backgroundMime', '') ->willReturn(''); - $expected = new Http\DataDownloadResponse('#header .logo { - background-image: url(\'./logo?v=0\'); - background-size: contain; - } - #header .logo-icon { - background-image: url(\'./logo?v=0\'); - background-size: contain; - }', 'style', 'text/css'); + $expectedData = '#header .logo {' . + 'background-image: url(\'./logo?v=0\')' . + 'background-size: contain;' . + '}' . "\n" . + '#header .logo-icon {' . + 'background-image: url(\'./logo?v=0\');' . + 'background-size: contain;' . + '}' . "\n" . + '#firstrunwizard .firstrunwizard-header .logo {' . + 'background-image: url(\'./logo?v=0\');' . + 'background-size: contain;' . + '}' . "\n"; + + $expected = new Http\DataDownloadResponse($expectedData, 'style', 'text/css'); + $expected->cacheFor(3600); + $expected->addHeader('Expires', date(\DateTime::RFC2822, 123)); @$this->assertEquals($expected, $this->themingController->getStylesheet()); } @@ -415,14 +504,22 @@ class ThemingControllerTest extends TestCase { ->with('theming', 'backgroundMime', '') ->willReturn('text/svg'); - $expected = new Http\DataDownloadResponse('#body-login { - background-image: url(\'./loginbackground?v=0\'); - }', 'style', 'text/css'); + $expectedData = '#body-login {background-image: url(\'./loginbackground?v=0\');}' . "\n"; + $expectedData .= '#firstrunwizard .firstrunwizard-header {' . + 'background-image: url(\'./loginbackground?v=0\');' . + '}' . "\n"; + + $expected = new Http\DataDownloadResponse($expectedData, 'style', 'text/css'); + $expected->cacheFor(3600); + $expected->addHeader('Expires', date(\DateTime::RFC2822, 123)); @$this->assertEquals($expected, $this->themingController->getStylesheet()); } public function testGetStylesheetWithAllCombined() { + + $color = '#000'; + $this->config ->expects($this->at(0)) ->method('getAppValue') @@ -432,7 +529,7 @@ class ThemingControllerTest extends TestCase { ->expects($this->at(1)) ->method('getAppValue') ->with('theming', 'color', '') - ->willReturn('#000'); + ->willReturn($color); $this->config ->expects($this->at(2)) ->method('getAppValue') @@ -444,20 +541,58 @@ class ThemingControllerTest extends TestCase { ->with('theming', 'backgroundMime', '') ->willReturn('image/png'); - $expected = new Http\DataDownloadResponse('#body-user #header,#body-settings #header,#body-public #header,#body-login,.searchbox input[type="search"]:focus,.searchbox input[type="search"]:active,.searchbox input[type="search"]:valid {background-color: #000}#header .logo { - background-image: url(\'./logo?v=0\'); - background-size: contain; - } - #header .logo-icon { - background-image: url(\'./logo?v=0\'); - background-size: contain; - }#body-login { - background-image: url(\'./loginbackground?v=0\'); - }', 'style', 'text/css'); + $expectedData = sprintf( + '#body-user #header,#body-settings #header,#body-public #header,#body-login,.searchbox input[type="search"]:focus,.searchbox input[type="search"]:active,.searchbox input[type="search"]:valid {background-color: %s}' . "\n", + $color); + + $expectedData .= sprintf('input[type="checkbox"].checkbox:checked:enabled:not(.checkbox--white) + label:before {' . + 'background-image:url(\'%s/core/img/actions/checkmark-white.svg\');' . + 'background-color: %s; background-position: center center; background-size:contain;' . + 'width:12px; height:12px; padding:0; margin:2px 6px 6px 2px; border-radius:1px;' . + "}\n", + \OC::$WEBROOT, + $color + ); + $expectedData .= 'input[type="radio"].radio:checked:not(.radio--white):not(:disabled) + label:before {' . + 'background-image: url(\'data:image/svg+xml;base64,'.$this->util->generateRadioButton($color).'\');' . + "}\n"; + $expectedData .= ' + #firstrunwizard .firstrunwizard-header { + background-color: ' . $color . '; + } + #firstrunwizard p a { + color: ' . $color . '; + } + '; + $expectedData .= sprintf( + '#header .logo {' . + 'background-image: url(\'./logo?v=0\')' . + 'background-size: contain;' . + '}' . "\n" . + '#header .logo-icon {' . + 'background-image: url(\'./logo?v=0\');' . + 'background-size: contain;' . + '}' . "\n" . + '#firstrunwizard .firstrunwizard-header .logo {' . + 'background-image: url(\'./logo?v=0\');' . + 'background-size: contain;' . + '}' . "\n" + ); + $expectedData .= '#body-login {background-image: url(\'./loginbackground?v=0\');}' . "\n"; + $expectedData .= '#firstrunwizard .firstrunwizard-header {' . + 'background-image: url(\'./loginbackground?v=0\');' . + '}' . "\n"; + $expected = new Http\DataDownloadResponse($expectedData, 'style', 'text/css'); + $expected->cacheFor(3600); + $expected->addHeader('Expires', date(\DateTime::RFC2822, 123)); @$this->assertEquals($expected, $this->themingController->getStylesheet()); } + public function testGetStylesheetWithAllCombinedInverted() { + + $color = '#fff'; + $this->config ->expects($this->at(0)) ->method('getAppValue') @@ -479,17 +614,55 @@ class ThemingControllerTest extends TestCase { ->with('theming', 'backgroundMime', '') ->willReturn('image/png'); - $expected = new Http\DataDownloadResponse('#body-user #header,#body-settings #header,#body-public #header,#body-login,.searchbox input[type="search"]:focus,.searchbox input[type="search"]:active,.searchbox input[type="search"]:valid {background-color: #fff}#header .logo { - background-image: url(\'./logo?v=0\'); - background-size: contain; - } - #header .logo-icon { - background-image: url(\'./logo?v=0\'); - background-size: contain; - }#body-login { - background-image: url(\'./loginbackground?v=0\'); - }#header .header-appname, #expandDisplayName { color: #000000; } #header .icon-caret { background-image: url(\'' . \OC::$WEBROOT . '/core/img/actions/caret-dark.svg\'); } .searchbox input[type="search"] { background: transparent url(\'' . \OC::$WEBROOT . '/core/img/actions/search.svg\') no-repeat 6px center; color: #000; }.searchbox input[type="search"]:focus,.searchbox input[type="search"]:active,.searchbox input[type="search"]:valid { color: #000; border: 1px solid rgba(0, 0, 0, .5); }', 'style', 'text/css'); + + $expectedData = sprintf( + '#body-user #header,#body-settings #header,#body-public #header,#body-login,.searchbox input[type="search"]:focus,.searchbox input[type="search"]:active,.searchbox input[type="search"]:valid {background-color: %s}' . "\n", + $color); + + $expectedData .= sprintf('input[type="checkbox"].checkbox:checked:enabled:not(.checkbox--white) + label:before {' . + 'background-image:url(\'%s/core/img/actions/checkmark-white.svg\');' . + 'background-color: #555555; background-position: center center; background-size:contain;' . + 'width:12px; height:12px; padding:0; margin:2px 6px 6px 2px; border-radius:1px;' . + "}\n", + \OC::$WEBROOT + ); + $expectedData .= 'input[type="radio"].radio:checked:not(.radio--white):not(:disabled) + label:before {' . + 'background-image: url(\'data:image/svg+xml;base64,'.$this->util->generateRadioButton('#555555').'\');' . + "}\n"; + $expectedData .= ' + #firstrunwizard .firstrunwizard-header { + background-color: ' . $color . '; + } + #firstrunwizard p a { + color: ' . $color . '; + } + '; + $expectedData .= sprintf( + '#header .logo {' . + 'background-image: url(\'./logo?v=0\')' . + 'background-size: contain;' . + '}' . "\n" . + '#header .logo-icon {' . + 'background-image: url(\'./logo?v=0\');' . + 'background-size: contain;' . + '}' . "\n" . + '#firstrunwizard .firstrunwizard-header .logo {' . + 'background-image: url(\'./logo?v=0\');' . + 'background-size: contain;' . + '}' . "\n" + ); + $expectedData .= '#body-login {background-image: url(\'./loginbackground?v=0\');}' . "\n"; + $expectedData .= '#firstrunwizard .firstrunwizard-header {' . + 'background-image: url(\'./loginbackground?v=0\');' . + '}' . "\n"; + $expectedData .= '#header .header-appname, #expandDisplayName { color: #000000; }' . "\n"; + $expectedData .= '#header .icon-caret { background-image: url(\'' . \OC::$WEBROOT . '/core/img/actions/caret-dark.svg\'); }' . "\n"; + $expectedData .= '.searchbox input[type="search"] { background: transparent url(\'' . \OC::$WEBROOT . '/core/img/actions/search.svg\') no-repeat 6px center; color: #000; }' . "\n"; + $expectedData .= '.searchbox input[type="search"]:focus,.searchbox input[type="search"]:active,.searchbox input[type="search"]:valid { color: #000; border: 1px solid rgba(0, 0, 0, .5); }' . "\n"; + $expected = new Http\DataDownloadResponse($expectedData, 'style', 'text/css'); + $expected->cacheFor(3600); + $expected->addHeader('Expires', date(\DateTime::RFC2822, 123)); @$this->assertEquals($expected, $this->themingController->getStylesheet()); } diff --git a/apps/theming/tests/lib/TemplateTest.php b/apps/theming/tests/TemplateTest.php index c3c792657ec..c3c792657ec 100644 --- a/apps/theming/tests/lib/TemplateTest.php +++ b/apps/theming/tests/TemplateTest.php diff --git a/apps/theming/tests/UtilTest.php b/apps/theming/tests/UtilTest.php new file mode 100644 index 00000000000..c7fc385d25d --- /dev/null +++ b/apps/theming/tests/UtilTest.php @@ -0,0 +1,97 @@ +<?php +/** + * @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net> + * + * @author Julius Haertl <jus@bitgrid.net> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\Theming\Tests; + +use OCA\Theming\Util; +use Test\TestCase; + +class UtilTest extends TestCase { + + /** @var Util */ + protected $util; + + protected function setUp() { + parent::setUp(); + $this->util = new Util(); + } + + public function testInvertTextColorLight() { + $invert = $this->util->invertTextColor('#ffffff'); + $this->assertEquals(true, $invert); + } + + public function testInvertTextColorDark() { + $invert = $this->util->invertTextColor('#000000'); + $this->assertEquals(false, $invert); + } + + public function testCalculateLuminanceLight() { + $luminance = $this->util->calculateLuminance('#ffffff'); + $this->assertEquals(1, $luminance); + } + + public function testCalculateLuminanceDark() { + $luminance = $this->util->calculateLuminance('#000000'); + $this->assertEquals(0, $luminance); + } + + public function testCalculateLuminanceLightShorthand() { + $luminance = $this->util->calculateLuminance('#fff'); + $this->assertEquals(1, $luminance); + } + + public function testCalculateLuminanceDarkShorthand() { + $luminance = $this->util->calculateLuminance('#000'); + $this->assertEquals(0, $luminance); + } + public function testInvertTextColorInvalid() { + $invert = $this->util->invertTextColor('aaabbbcccddd123'); + $this->assertEquals(false, $invert); + } + + public function testInvertTextColorEmpty() { + $invert = $this->util->invertTextColor(''); + $this->assertEquals(false, $invert); + } + + public function testElementColorDefault() { + $elementColor = $this->util->elementColor("#000000"); + $this->assertEquals('#000000', $elementColor); + } + + public function testElementColorOnBrightBackground() { + $elementColor = $this->util->elementColor('#ffffff'); + $this->assertEquals('#555555', $elementColor); + } + + public function testGenerateRadioButtonWhite() { + $button = $this->util->generateRadioButton('#ffffff'); + $expected = 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTYiIHdpZHRoPSIxNiI+PHBhdGggZD0iTTggMWE3IDcgMCAwIDAtNyA3IDcgNyAwIDAgMCA3IDcgNyA3IDAgMCAwIDctNyA3IDcgMCAwIDAtNy03em0wIDFhNiA2IDAgMCAxIDYgNiA2IDYgMCAwIDEtNiA2IDYgNiAwIDAgMS02LTYgNiA2IDAgMCAxIDYtNnptMCAyYTQgNCAwIDEgMCAwIDggNCA0IDAgMCAwIDAtOHoiIGZpbGw9IiNmZmZmZmYiLz48L3N2Zz4='; + $this->assertEquals($expected, $button); + } + public function testGenerateRadioButtonBlack() { + $button = $this->util->generateRadioButton('#000000'); + $expected = 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTYiIHdpZHRoPSIxNiI+PHBhdGggZD0iTTggMWE3IDcgMCAwIDAtNyA3IDcgNyAwIDAgMCA3IDcgNyA3IDAgMCAwIDctNyA3IDcgMCAwIDAtNy03em0wIDFhNiA2IDAgMCAxIDYgNiA2IDYgMCAwIDEtNiA2IDYgNiAwIDAgMS02LTYgNiA2IDAgMCAxIDYtNnptMCAyYTQgNCAwIDEgMCAwIDggNCA0IDAgMCAwIDAtOHoiIGZpbGw9IiMwMDAwMDAiLz48L3N2Zz4='; + $this->assertEquals($expected, $button); + } +} diff --git a/apps/theming/tests/lib/UtilTest.php b/apps/theming/tests/lib/UtilTest.php deleted file mode 100644 index 9ebb11d6288..00000000000 --- a/apps/theming/tests/lib/UtilTest.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net> - * - * @author Julius Haertl <jus@bitgrid.net> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ -namespace OCA\Theming\Tests; - -use OCA\Theming\Util; -use Test\TestCase; - -class UtilTest extends TestCase { - - public function testInvertTextColorLight() { - $invert = Util::invertTextColor('#ffffff'); - $this->assertEquals(true, $invert); - } - - public function testInvertTextColorDark() { - $invert = Util::invertTextColor('#000000'); - $this->assertEquals(false, $invert); - } - - public function testCalculateLuminanceLight() { - $luminance = Util::calculateLuminance('#ffffff'); - $this->assertEquals(1, $luminance); - } - - public function testCalculateLuminanceDark() { - $luminance = Util::calculateLuminance('#000000'); - $this->assertEquals(0, $luminance); - } - - public function testCalculateLuminanceLightShorthand() { - $luminance = Util::calculateLuminance('#fff'); - $this->assertEquals(1, $luminance); - } - - public function testCalculateLuminanceDarkShorthand() { - $luminance = Util::calculateLuminance('#000'); - $this->assertEquals(0, $luminance); - } - public function testInvertTextColorInvalid() { - $invert = Util::invertTextColor('aaabbbcccddd123'); - $this->assertEquals(false, $invert); - } - - public function testInvertTextColorEmpty() { - $invert = Util::invertTextColor(''); - $this->assertEquals(false, $invert); - } -} diff --git a/apps/updatenotification/l10n/is.js b/apps/updatenotification/l10n/is.js index a2bd2757d97..c1961755c78 100644 --- a/apps/updatenotification/l10n/is.js +++ b/apps/updatenotification/l10n/is.js @@ -1,14 +1,18 @@ OC.L10N.register( "updatenotification", { + "Update notifications" : "Tilkynningar um uppfærslu", "{version} is available. Get more information on how to update." : "{version} er í boði. Fáðu frekari upplýsingar um hvernig á að uppfæra.", "Updated channel" : "Uppfærði rás", + "Nextcloud core" : "Nextcloud kjarni", + "Update for %1$s to version %2$s is available." : "Upfærsla %1$s í útgáfu %2$s er tiltæk.", "Updater" : "Uppfærslustýring", "A new version is available: %s" : "Ný útgáfa er tiltæk: %s", "Open updater" : "Opna uppfærslustýringu", "Your version is up to date." : "Útgáfan þín er af nýjustu gerð.", "Checked on %s" : "Athugað þann %s", "Update channel:" : "Uppfærslurás:", - "You can always update to a newer version / experimental channel. But you can never downgrade to a more stable channel." : "Þú getur alltaf uppfært í nýrri útgáfu eða tilraunaútgáfurás. En þú getur aldrei niðurfært í stöðugri rás." + "You can always update to a newer version / experimental channel. But you can never downgrade to a more stable channel." : "Þú getur alltaf uppfært í nýrri útgáfu eða tilraunaútgáfurás. En þú getur aldrei niðurfært í stöðugri rás.", + "Notify members of the following groups about available updates:" : "Tilkynna meðlimum eftirfarandi hópa um tiltækar uppfærslur:" }, "nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);"); diff --git a/apps/updatenotification/l10n/is.json b/apps/updatenotification/l10n/is.json index 45df407065f..044131460fc 100644 --- a/apps/updatenotification/l10n/is.json +++ b/apps/updatenotification/l10n/is.json @@ -1,12 +1,16 @@ { "translations": { + "Update notifications" : "Tilkynningar um uppfærslu", "{version} is available. Get more information on how to update." : "{version} er í boði. Fáðu frekari upplýsingar um hvernig á að uppfæra.", "Updated channel" : "Uppfærði rás", + "Nextcloud core" : "Nextcloud kjarni", + "Update for %1$s to version %2$s is available." : "Upfærsla %1$s í útgáfu %2$s er tiltæk.", "Updater" : "Uppfærslustýring", "A new version is available: %s" : "Ný útgáfa er tiltæk: %s", "Open updater" : "Opna uppfærslustýringu", "Your version is up to date." : "Útgáfan þín er af nýjustu gerð.", "Checked on %s" : "Athugað þann %s", "Update channel:" : "Uppfærslurás:", - "You can always update to a newer version / experimental channel. But you can never downgrade to a more stable channel." : "Þú getur alltaf uppfært í nýrri útgáfu eða tilraunaútgáfurás. En þú getur aldrei niðurfært í stöðugri rás." + "You can always update to a newer version / experimental channel. But you can never downgrade to a more stable channel." : "Þú getur alltaf uppfært í nýrri útgáfu eða tilraunaútgáfurás. En þú getur aldrei niðurfært í stöðugri rás.", + "Notify members of the following groups about available updates:" : "Tilkynna meðlimum eftirfarandi hópa um tiltækar uppfærslur:" },"pluralForm" :"nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);" }
\ No newline at end of file diff --git a/apps/user_ldap/ajax/wizard.php b/apps/user_ldap/ajax/wizard.php index b5eab90af48..654fb70ced5 100644 --- a/apps/user_ldap/ajax/wizard.php +++ b/apps/user_ldap/ajax/wizard.php @@ -60,7 +60,7 @@ $userManager = new \OCA\User_LDAP\User\Manager( \OC::$server->getDatabaseConnection(), \OC::$server->getUserManager()); -$access = new \OCA\User_LDAP\Access($con, $ldapWrapper, $userManager); +$access = new \OCA\User_LDAP\Access($con, $ldapWrapper, $userManager, new \OCA\User_LDAP\Helper()); $wizard = new \OCA\User_LDAP\Wizard($configuration, $ldapWrapper, $access); diff --git a/apps/user_ldap/appinfo/app.php b/apps/user_ldap/appinfo/app.php index 18987614bdb..10cc003a3f5 100644 --- a/apps/user_ldap/appinfo/app.php +++ b/apps/user_ldap/appinfo/app.php @@ -44,7 +44,7 @@ if(count($configPrefixes) === 1) { \OC::$server->getUserManager() ); $connector = new OCA\User_LDAP\Connection($ldapWrapper, $configPrefixes[0]); - $ldapAccess = new OCA\User_LDAP\Access($connector, $ldapWrapper, $userManager); + $ldapAccess = new OCA\User_LDAP\Access($connector, $ldapWrapper, $userManager, $helper); $ldapAccess->setUserMapper(new OCA\User_LDAP\Mapping\UserMapping($dbc)); $ldapAccess->setGroupMapper(new OCA\User_LDAP\Mapping\GroupMapping($dbc)); diff --git a/apps/user_ldap/appinfo/install.php b/apps/user_ldap/appinfo/install.php index b3c92b0024a..c16a1f4a039 100644 --- a/apps/user_ldap/appinfo/install.php +++ b/apps/user_ldap/appinfo/install.php @@ -4,6 +4,7 @@ * * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Christopher Schäpers <kondou@ts.unde.re> + * @author Roger Szabo <roger.szabo@web.de> * * @license AGPL-3.0 * @@ -24,3 +25,6 @@ $state = OCP\Config::getSystemValue('ldapIgnoreNamingRules', 'doSet'); if($state === 'doSet') { OCP\Config::setSystemValue('ldapIgnoreNamingRules', false); } + +$helper = new \OCA\User_LDAP\Helper(); +$helper->setLDAPProvider(); diff --git a/apps/user_ldap/appinfo/update.php b/apps/user_ldap/appinfo/update.php new file mode 100644 index 00000000000..3c9745338e6 --- /dev/null +++ b/apps/user_ldap/appinfo/update.php @@ -0,0 +1,24 @@ +<?php +/** + * + * @copyright Copyright (c) 2016, Roger Szabo (roger.szabo@web.de) + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +$helper = new \OCA\User_LDAP\Helper(); +$helper->setLDAPProvider(); diff --git a/apps/user_ldap/lib/Access.php b/apps/user_ldap/lib/Access.php index dabf243eda1..299ad581644 100644 --- a/apps/user_ldap/lib/Access.php +++ b/apps/user_ldap/lib/Access.php @@ -20,6 +20,7 @@ * @author Ralph Krimmel <rkrimme1@gwdg.de> * @author Renaud Fortier <Renaud.Fortier@fsaa.ulaval.ca> * @author Robin McCorkell <robin@mccorkell.me.uk> + * @author Roger Szabo <roger.szabo@web.de> * * @license AGPL-3.0 * @@ -77,13 +78,19 @@ class Access extends LDAPUtility implements IUserTools { * @var AbstractMapping $userMapper */ protected $groupMapper; + + /** + * @var \OCA\User_LDAP\Helper + */ + private $helper; public function __construct(Connection $connection, ILDAPWrapper $ldap, - Manager $userManager) { + Manager $userManager, Helper $helper) { parent::__construct($ldap); $this->connection = $connection; $this->userManager = $userManager; $this->userManager->setLdapAccess($this); + $this->helper = $helper; } /** @@ -173,7 +180,7 @@ class Access extends LDAPUtility implements IUserTools { // (cf. #12306), 500 is default for paging and should work everywhere. $maxResults = $pagingSize > 20 ? $pagingSize : 500; $this->initPagedSearch($filter, array($dn), array($attr), $maxResults, 0); - $dn = $this->DNasBaseParameter($dn); + $dn = $this->helper->DNasBaseParameter($dn); $rr = @$this->ldap->read($cr, $dn, $filter, array($attr)); if(!$this->ldap->isResource($rr)) { if(!empty($attr)) { @@ -201,7 +208,7 @@ class Access extends LDAPUtility implements IUserTools { $values = array(); for($i=0;$i<$result[$attr]['count'];$i++) { if($this->resemblesDN($attr)) { - $values[] = $this->sanitizeDN($result[$attr][$i]); + $values[] = $this->helper->sanitizeDN($result[$attr][$i]); } elseif(strtolower($attr) === 'objectguid' || strtolower($attr) === 'guid') { $values[] = $this->convertObjectGUID2Str($result[$attr][$i]); } else { @@ -243,49 +250,6 @@ class Access extends LDAPUtility implements IUserTools { } /** - * sanitizes a DN received from the LDAP server - * @param array $dn the DN in question - * @return array the sanitized DN - */ - private function sanitizeDN($dn) { - //treating multiple base DNs - if(is_array($dn)) { - $result = array(); - foreach($dn as $singleDN) { - $result[] = $this->sanitizeDN($singleDN); - } - return $result; - } - - //OID sometimes gives back DNs with whitespace after the comma - // a la "uid=foo, cn=bar, dn=..." We need to tackle this! - $dn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn); - - //make comparisons and everything work - $dn = mb_strtolower($dn, 'UTF-8'); - - //escape DN values according to RFC 2253 – this is already done by ldap_explode_dn - //to use the DN in search filters, \ needs to be escaped to \5c additionally - //to use them in bases, we convert them back to simple backslashes in readAttribute() - $replacements = array( - '\,' => '\5c2C', - '\=' => '\5c3D', - '\+' => '\5c2B', - '\<' => '\5c3C', - '\>' => '\5c3E', - '\;' => '\5c3B', - '\"' => '\5c22', - '\#' => '\5c23', - '(' => '\28', - ')' => '\29', - '*' => '\2A', - ); - $dn = str_replace(array_keys($replacements), array_values($replacements), $dn); - - return $dn; - } - - /** * returns a DN-string that is cleaned from not domain parts, e.g. * cn=foo,cn=bar,dc=foobar,dc=server,dc=org * becomes dc=foobar,dc=server,dc=org @@ -1071,10 +1035,10 @@ class Access extends LDAPUtility implements IUserTools { } if($key !== 'dn') { $selection[$i][$key] = $this->resemblesDN($key) ? - $this->sanitizeDN($item[$key]) + $this->helper->sanitizeDN($item[$key]) : $item[$key]; } else { - $selection[$i][$key] = [$this->sanitizeDN($item[$key])]; + $selection[$i][$key] = [$this->helper->sanitizeDN($item[$key])]; } } @@ -1298,7 +1262,7 @@ class Access extends LDAPUtility implements IUserTools { * @return bool */ public function areCredentialsValid($name, $password) { - $name = $this->DNasBaseParameter($name); + $name = $this->helper->DNasBaseParameter($name); $testConnection = clone $this->connection; $credentials = array( 'ldapAgentName' => $name, @@ -1570,15 +1534,6 @@ class Access extends LDAPUtility implements IUserTools { } /** - * converts a stored DN so it can be used as base parameter for LDAP queries, internally we store them for usage in LDAP filters - * @param string $dn the DN - * @return string - */ - private function DNasBaseParameter($dn) { - return str_ireplace('\\5c', '\\', $dn); - } - - /** * checks if the given DN is part of the given base DN(s) * @param string $dn the DN * @param string[] $bases array containing the allowed base DN or DNs @@ -1586,7 +1541,7 @@ class Access extends LDAPUtility implements IUserTools { */ public function isDNPartOfBase($dn, $bases) { $belongsToBase = false; - $bases = $this->sanitizeDN($bases); + $bases = $this->helper->sanitizeDN($bases); foreach($bases as $base) { $belongsToBase = true; diff --git a/apps/user_ldap/lib/Connection.php b/apps/user_ldap/lib/Connection.php index 7bd5e97e4f4..7fb26526195 100644 --- a/apps/user_ldap/lib/Connection.php +++ b/apps/user_ldap/lib/Connection.php @@ -11,6 +11,7 @@ * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> + * @author Roger Szabo <roger.szabo@web.de> * * @license AGPL-3.0 * @@ -52,6 +53,8 @@ class Connection extends LDAPUtility { private $configID; private $configured = false; private $hasPagedResultSupport = true; + //whether connection should be kept on __destruct + private $dontDestruct = false; /** * @var bool runtime flag that indicates whether supported primary groups are available @@ -93,7 +96,7 @@ class Connection extends LDAPUtility { } public function __destruct() { - if($this->ldap->isResource($this->ldapConnectionRes)) { + if(!$this->dontDestruct && $this->ldap->isResource($this->ldapConnectionRes)) { @$this->ldap->unbind($this->ldapConnectionRes); }; } @@ -105,6 +108,7 @@ class Connection extends LDAPUtility { $this->configuration = new Configuration($this->configPrefix, !is_null($this->configID)); $this->ldapConnectionRes = null; + $this->dontDestruct = true; } /** diff --git a/apps/user_ldap/lib/Helper.php b/apps/user_ldap/lib/Helper.php index ccc1d2c0b44..90807a3c526 100644 --- a/apps/user_ldap/lib/Helper.php +++ b/apps/user_ldap/lib/Helper.php @@ -10,6 +10,7 @@ * @author Morris Jobke <hey@morrisjobke.de> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> + * @author Roger Szabo <roger.szabo@web.de> * * @license AGPL-3.0 * @@ -183,6 +184,70 @@ class Helper { return $domain; } + + /** + * + * Set the LDAPProvider in the config + * + */ + public function setLDAPProvider() { + $current = \OC::$server->getConfig()->getSystemValue('ldapProviderFactory', null); + if(is_null($current)) { + \OC::$server->getConfig()->setSystemValue('ldapProviderFactory', '\\OCA\\User_LDAP\\LDAPProviderFactory'); + } + } + + /** + * sanitizes a DN received from the LDAP server + * @param array $dn the DN in question + * @return array the sanitized DN + */ + public function sanitizeDN($dn) { + //treating multiple base DNs + if(is_array($dn)) { + $result = array(); + foreach($dn as $singleDN) { + $result[] = $this->sanitizeDN($singleDN); + } + return $result; + } + + //OID sometimes gives back DNs with whitespace after the comma + // a la "uid=foo, cn=bar, dn=..." We need to tackle this! + $dn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn); + + //make comparisons and everything work + $dn = mb_strtolower($dn, 'UTF-8'); + + //escape DN values according to RFC 2253 – this is already done by ldap_explode_dn + //to use the DN in search filters, \ needs to be escaped to \5c additionally + //to use them in bases, we convert them back to simple backslashes in readAttribute() + $replacements = array( + '\,' => '\5c2C', + '\=' => '\5c3D', + '\+' => '\5c2B', + '\<' => '\5c3C', + '\>' => '\5c3E', + '\;' => '\5c3B', + '\"' => '\5c22', + '\#' => '\5c23', + '(' => '\28', + ')' => '\29', + '*' => '\2A', + ); + $dn = str_replace(array_keys($replacements), array_values($replacements), $dn); + + return $dn; + } + + /** + * converts a stored DN so it can be used as base parameter for LDAP queries, internally we store them for usage in LDAP filters + * @param string $dn the DN + * @return string + */ + public function DNasBaseParameter($dn) { + return str_ireplace('\\5c', '\\', $dn); + } /** * listens to a hook thrown by server2server sharing and replaces the given diff --git a/apps/user_ldap/lib/IUserLDAP.php b/apps/user_ldap/lib/IUserLDAP.php new file mode 100644 index 00000000000..cb7d0138892 --- /dev/null +++ b/apps/user_ldap/lib/IUserLDAP.php @@ -0,0 +1,49 @@ +<?php +/** + * + * @copyright Copyright (c) 2016, Roger Szabo (roger.szabo@web.de) + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\User_LDAP; + +interface IUserLDAP { + + //Functions used by LDAPProvider + + /** + * Return access for LDAP interaction. + * @param string $uid + * @return Access instance of Access for LDAP interaction + */ + public function getLDAPAccess($uid); + + /** + * Return a new LDAP connection for the specified user. + * @param string $uid + * @return resource of the LDAP connection + */ + public function getNewLDAPConnection($uid); + + /** + * Return the username for the given LDAP DN, if available. + * @param string $dn + * @return string|false with the username + */ + public function dn2UserName($dn); +} diff --git a/apps/user_ldap/lib/Jobs/UpdateGroups.php b/apps/user_ldap/lib/Jobs/UpdateGroups.php index 91d40d58742..047b95a6d9b 100644 --- a/apps/user_ldap/lib/Jobs/UpdateGroups.php +++ b/apps/user_ldap/lib/Jobs/UpdateGroups.php @@ -188,7 +188,7 @@ class UpdateGroups extends \OC\BackgroundJob\TimedJob { $dbc, \OC::$server->getUserManager()); $connector = new Connection($ldapWrapper, $configPrefixes[0]); - $ldapAccess = new Access($connector, $ldapWrapper, $userManager); + $ldapAccess = new Access($connector, $ldapWrapper, $userManager, $helper); $groupMapper = new GroupMapping($dbc); $userMapper = new UserMapping($dbc); $ldapAccess->setGroupMapper($groupMapper); diff --git a/apps/user_ldap/lib/LDAPProvider.php b/apps/user_ldap/lib/LDAPProvider.php new file mode 100644 index 00000000000..5bd6a0fd08c --- /dev/null +++ b/apps/user_ldap/lib/LDAPProvider.php @@ -0,0 +1,188 @@ +<?php +/** + * + * @copyright Copyright (c) 2016, Roger Szabo (roger.szabo@web.de) + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\User_LDAP; + +use OCP\IUserBackend; +use OCP\LDAP\ILDAPProvider; +use OCP\LDAP\IDeletionFlagSupport; +use OCP\IServerContainer; +use OCA\User_LDAP\User\DeletedUsersIndex; +use OCA\User_LDAP\Mapping\UserMapping; + +/** + * LDAP provider for pulic access to the LDAP backend. + */ +class LDAPProvider implements ILDAPProvider, IDeletionFlagSupport { + + private $backend; + private $logger; + private $helper; + private $deletedUsersIndex; + + /** + * Create new LDAPProvider + * @param \OCP\IServerContainer $serverContainer + * @throws \Exception if user_ldap app was not enabled + */ + public function __construct(IServerContainer $serverContainer, Helper $helper, DeletedUsersIndex $deletedUsersIndex) { + $this->logger = $serverContainer->getLogger(); + $this->helper = $helper; + $this->deletedUsersIndex = $deletedUsersIndex; + foreach ($serverContainer->getUserManager()->getBackends() as $backend){ + $this->logger->debug('instance '.get_class($backend).' backend.', ['app' => 'user_ldap']); + if ($backend instanceof IUserLDAP) { + $this->backend = $backend; + return; + } + } + throw new \Exception('To use the LDAPProvider, user_ldap app must be enabled'); + } + + /** + * Translate an user id to LDAP DN + * @param string $uid user id + * @return string with the LDAP DN + * @throws \Exception if translation was unsuccessful + */ + public function getUserDN($uid) { + if(!$this->backend->userExists($uid)){ + throw new \Exception('User id not found in LDAP'); + } + $result = $this->backend->getLDAPAccess($uid)->username2dn($uid); + if(!$result){ + throw new \Exception('Translation to LDAP DN unsuccessful'); + } + return $result; + } + + /** + * Translate a LDAP DN to an internal user name. If there is no mapping between + * the DN and the user name, a new one will be created. + * @param string $dn LDAP DN + * @return string with the internal user name + * @throws \Exception if translation was unsuccessful + */ + public function getUserName($dn) { + $result = $this->backend->dn2UserName($dn); + if(!$result){ + throw new \Exception('Translation to internal user name unsuccessful'); + } + return $result; + } + + /** + * Convert a stored DN so it can be used as base parameter for LDAP queries. + * @param string $dn the DN in question + * @return string + */ + public function DNasBaseParameter($dn) { + return $this->helper->DNasBaseParameter($dn); + } + + /** + * Sanitize a DN received from the LDAP server. + * @param array $dn the DN in question + * @return array the sanitized DN + */ + public function sanitizeDN($dn) { + return $this->helper->sanitizeDN($dn); + } + + /** + * Return a new LDAP connection resource for the specified user. + * The connection must be closed manually. + * @param string $uid user id + * @return resource of the LDAP connection + * @throws \Exception if user id was not found in LDAP + */ + public function getLDAPConnection($uid) { + if(!$this->backend->userExists($uid)){ + throw new \Exception('User id not found in LDAP'); + } + return $this->backend->getNewLDAPConnection($uid); + } + + /** + * Get the LDAP base for users. + * @param string $uid user id + * @return string the base for users + * @throws \Exception if user id was not found in LDAP + */ + public function getLDAPBaseUsers($uid) { + if(!$this->backend->userExists($uid)){ + throw new \Exception('User id not found in LDAP'); + } + return $this->backend->getLDAPAccess($uid)->getConnection()->getConfiguration()['ldap_base_users']; + } + + /** + * Get the LDAP base for groups. + * @param string $uid user id + * @return string the base for groups + * @throws \Exception if user id was not found in LDAP + */ + public function getLDAPBaseGroups($uid) { + if(!$this->backend->userExists($uid)){ + throw new \Exception('User id not found in LDAP'); + } + return $this->backend->getLDAPAccess($uid)->getConnection()->getConfiguration()['ldap_base_groups']; + } + + /** + * Clear the cache if a cache is used, otherwise do nothing. + * @param string $uid user id + * @throws \Exception if user id was not found in LDAP + */ + public function clearCache($uid) { + if(!$this->backend->userExists($uid)){ + throw new \Exception('User id not found in LDAP'); + } + $this->backend->getLDAPAccess($uid)->getConnection()->clearCache(); + } + + /** + * Check whether a LDAP DN exists + * @param string $dn LDAP DN + * @return bool whether the DN exists + */ + public function dnExists($dn) { + $result = $this->backend->dn2UserName($dn); + return !$result ? false : true; + } + + /** + * Flag record for deletion. + * @param string $uid user id + */ + public function flagRecord($uid) { + $this->deletedUsersIndex->markUser($uid); + } + + /** + * Unflag record for deletion. + * @param string $uid user id + */ + public function unflagRecord($uid) { + //do nothing + } +} diff --git a/apps/user_ldap/lib/LDAPProviderFactory.php b/apps/user_ldap/lib/LDAPProviderFactory.php new file mode 100644 index 00000000000..528af001037 --- /dev/null +++ b/apps/user_ldap/lib/LDAPProviderFactory.php @@ -0,0 +1,59 @@ +<?php +/** + * + * @copyright Copyright (c) 2016, Roger Szabo (roger.szabo@web.de) + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\User_LDAP; + +use OCP\LDAP\ILDAPProviderFactory; +use OCP\IServerContainer; +use OCA\User_LDAP\User\DeletedUsersIndex; +use OCA\User_LDAP\Mapping\UserMapping; + +class LDAPProviderFactory implements ILDAPProviderFactory { + /** + * Server container + * + * @var IServerContainer + */ + private $serverContainer; + + /** + * Constructor for the LDAP provider factory + * + * @param IServerContainer $serverContainer server container + */ + public function __construct(IServerContainer $serverContainer) { + $this->serverContainer = $serverContainer; + } + + /** + * creates and returns an instance of the ILDAPProvider + * + * @return OCP\LDAP\ILDAPProvider + */ + public function getLDAPProvider() { + $dbConnection = $this->serverContainer->getDatabaseConnection(); + $userMapping = new UserMapping($dbConnection); + return new LDAPProvider($this->serverContainer, new Helper(), + new DeletedUsersIndex($this->serverContainer->getConfig(), + $dbConnection, $userMapping)); + } +} diff --git a/apps/user_ldap/lib/Proxy.php b/apps/user_ldap/lib/Proxy.php index 07cc1ea0e8c..db1c761656f 100644 --- a/apps/user_ldap/lib/Proxy.php +++ b/apps/user_ldap/lib/Proxy.php @@ -77,7 +77,7 @@ abstract class Proxy { $userManager = new Manager($ocConfig, $fs, $log, $avatarM, new \OCP\Image(), $db, $coreUserManager); $connector = new Connection($this->ldap, $configPrefix); - $access = new Access($connector, $this->ldap, $userManager); + $access = new Access($connector, $this->ldap, $userManager, new Helper()); $access->setUserMapper($userMap); $access->setGroupMapper($groupMap); self::$accesses[$configPrefix] = $access; diff --git a/apps/user_ldap/lib/User/User.php b/apps/user_ldap/lib/User/User.php index 56d881c5d5a..3d247663b3f 100644 --- a/apps/user_ldap/lib/User/User.php +++ b/apps/user_ldap/lib/User/User.php @@ -435,7 +435,10 @@ class User { if(!is_null($email)) { $user = $this->userManager->get($this->uid); if (!is_null($user)) { - $user->setEMailAddress($email); + $currentEmail = $user->getEMailAddress(); + if ($currentEmail !== $email) { + $user->setEMailAddress($email); + } } } } diff --git a/apps/user_ldap/lib/User_LDAP.php b/apps/user_ldap/lib/User_LDAP.php index a2a65bb8406..ce1aafc210e 100644 --- a/apps/user_ldap/lib/User_LDAP.php +++ b/apps/user_ldap/lib/User_LDAP.php @@ -15,6 +15,7 @@ * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Tom Needham <tom@owncloud.com> + * @author Roger Szabo <roger.szabo@web.de> * * @license AGPL-3.0 * @@ -39,7 +40,7 @@ use OCA\User_LDAP\User\OfflineUser; use OCA\User_LDAP\User\User; use OCP\IConfig; -class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserInterface { +class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserInterface, IUserLDAP { /** @var string[] $homesToKill */ protected $homesToKill = array(); @@ -90,6 +91,16 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn return false; } } + + /** + * returns the username for the given LDAP DN, if available + * + * @param string $dn + * @return string|false with the username + */ + public function dn2UserName($dn) { + return $this->access->dn2username($dn); + } /** * returns an LDAP record based on a given login name @@ -384,8 +395,14 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn } $user = $this->access->userManager->get($uid); - $displayName = $user->composeAndStoreDisplayName($displayName, $displayName2); - $this->access->connection->writeToCache($cacheKey, $displayName); + if ($user instanceof User) { + $displayName = $user->composeAndStoreDisplayName($displayName, $displayName2); + $this->access->connection->writeToCache($cacheKey, $displayName); + } + if ($user instanceof OfflineUser) { + /** @var OfflineUser $user*/ + $displayName = $user->getDisplayName(); + } return $displayName; } @@ -462,5 +479,25 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn public function getBackendName(){ return 'LDAP'; } - + + /** + * Return access for LDAP interaction. + * @param string $uid + * @return Access instance of Access for LDAP interaction + */ + public function getLDAPAccess($uid) { + return $this->access; + } + + /** + * Return LDAP connection resource from a cloned connection. + * The cloned connection needs to be closed manually. + * of the current access. + * @param string $uid + * @return resource of the LDAP connection + */ + public function getNewLDAPConnection($uid) { + $connection = clone $this->access->getConnection(); + return $connection->getConnectionResource(); + } } diff --git a/apps/user_ldap/lib/User_Proxy.php b/apps/user_ldap/lib/User_Proxy.php index c86d4f29ec4..cced469a7ae 100644 --- a/apps/user_ldap/lib/User_Proxy.php +++ b/apps/user_ldap/lib/User_Proxy.php @@ -9,6 +9,7 @@ * @author Morris Jobke <hey@morrisjobke.de> * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Thomas Müller <thomas.mueller@tmit.eu> + * @author Roger Szabo <roger.szabo@web.de> * * @license AGPL-3.0 * @@ -31,7 +32,7 @@ namespace OCA\User_LDAP; use OCA\User_LDAP\User\User; use OCP\IConfig; -class User_Proxy extends Proxy implements \OCP\IUserBackend, \OCP\UserInterface { +class User_Proxy extends Proxy implements \OCP\IUserBackend, \OCP\UserInterface, IUserLDAP { private $backends = array(); private $refBackend = null; @@ -193,6 +194,17 @@ class User_Proxy extends Proxy implements \OCP\IUserBackend, \OCP\UserInterface $id = 'LOGINNAME,' . $loginName; return $this->handleRequest($id, 'loginName2UserName', array($loginName)); } + + /** + * returns the username for the given LDAP DN, if available + * + * @param string $dn + * @return string|false with the username + */ + public function dn2UserName($dn) { + $id = 'DN,' . $dn; + return $this->handleRequest($id, 'dn2UserName', array($dn)); + } /** * get the user's home directory @@ -273,4 +285,22 @@ class User_Proxy extends Proxy implements \OCP\IUserBackend, \OCP\UserInterface return $users; } + /** + * Return access for LDAP interaction. + * @param string $uid + * @return Access instance of Access for LDAP interaction + */ + public function getLDAPAccess($uid) { + return $this->handleRequest($uid, 'getLDAPAccess', array($uid)); + } + + /** + * Return a new LDAP connection for the specified user. + * The connection needs to be closed manually. + * @param string $uid + * @return resource of the LDAP connection + */ + public function getNewLDAPConnection($uid) { + return $this->handleRequest($uid, 'getNewLDAPConnection', array($uid)); + } } diff --git a/apps/user_ldap/tests/AccessTest.php b/apps/user_ldap/tests/AccessTest.php index f96813ba711..2fddafa214b 100644 --- a/apps/user_ldap/tests/AccessTest.php +++ b/apps/user_ldap/tests/AccessTest.php @@ -60,21 +60,22 @@ class AccessTest extends \Test\TestCase { $this->getMock('\OCP\Image'), $this->getMock('\OCP\IDBConnection'), $this->getMock('\OCP\IUserManager'))); + $helper = new \OCA\User_LDAP\Helper(); - return array($lw, $connector, $um); + return array($lw, $connector, $um, $helper); } public function testEscapeFilterPartValidChars() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $input = 'okay'; $this->assertTrue($input === $access->escapeFilterPart($input)); } public function testEscapeFilterPartEscapeWildcard() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $input = '*'; $expected = '\\\\*'; @@ -82,8 +83,8 @@ class AccessTest extends \Test\TestCase { } public function testEscapeFilterPartEscapeWildcard2() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $input = 'foo*bar'; $expected = 'foo\\\\*bar'; @@ -92,8 +93,8 @@ class AccessTest extends \Test\TestCase { /** @dataProvider convertSID2StrSuccessData */ public function testConvertSID2StrSuccess(array $sidArray, $sidExpected) { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $sidBinary = implode('', $sidArray); $this->assertSame($sidExpected, $access->convertSID2Str($sidBinary)); @@ -127,8 +128,8 @@ class AccessTest extends \Test\TestCase { } public function testConvertSID2StrInputError() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $sidIllegal = 'foobar'; $sidExpected = ''; @@ -137,8 +138,8 @@ class AccessTest extends \Test\TestCase { } public function testGetDomainDNFromDNSuccess() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $inputDN = 'uid=zaphod,cn=foobar,dc=my,dc=server,dc=com'; $domainDN = 'dc=my,dc=server,dc=com'; @@ -152,8 +153,8 @@ class AccessTest extends \Test\TestCase { } public function testGetDomainDNFromDNError() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $inputDN = 'foobar'; $expected = ''; @@ -187,8 +188,8 @@ class AccessTest extends \Test\TestCase { } public function testStringResemblesDN() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $cases = $this->getResemblesDNInputData(); @@ -208,9 +209,9 @@ class AccessTest extends \Test\TestCase { } public function testStringResemblesDNLDAPmod() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); $lw = new \OCA\User_LDAP\LDAP(); - $access = new Access($con, $lw, $um); + $access = new Access($con, $lw, $um, $helper); if(!function_exists('ldap_explode_dn')) { $this->markTestSkipped('LDAP Module not available'); @@ -224,8 +225,8 @@ class AccessTest extends \Test\TestCase { } public function testCacheUserHome() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $con->expects($this->once()) ->method('writeToCache'); @@ -234,8 +235,8 @@ class AccessTest extends \Test\TestCase { } public function testBatchApplyUserAttributes() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $mapperMock = $this->getMockBuilder('\OCA\User_LDAP\Mapping\UserMapping') ->disableOriginalConstructor() ->getMock(); @@ -294,7 +295,7 @@ class AccessTest extends \Test\TestCase { * @dataProvider dNAttributeProvider */ public function testSanitizeDN($attribute) { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); $dnFromServer = 'cn=Mixed Cases,ou=Are Sufficient To,ou=Test,dc=example,dc=org'; @@ -309,7 +310,7 @@ class AccessTest extends \Test\TestCase { $attribute => array('count' => 1, $dnFromServer) ))); - $access = new Access($con, $lw, $um); + $access = new Access($con, $lw, $um, $helper); $values = $access->readAttribute('uid=whoever,dc=example,dc=org', $attribute); $this->assertSame($values[0], strtolower($dnFromServer)); } diff --git a/apps/user_ldap/tests/Group_LDAPTest.php b/apps/user_ldap/tests/Group_LDAPTest.php index 71120bdb838..83ec2dedf22 100644 --- a/apps/user_ldap/tests/Group_LDAPTest.php +++ b/apps/user_ldap/tests/Group_LDAPTest.php @@ -55,9 +55,10 @@ class Group_LDAPTest extends \Test\TestCase { $um = $this->getMockBuilder('\OCA\User_LDAP\User\Manager') ->disableOriginalConstructor() ->getMock(); + $helper = new \OCA\User_LDAP\Helper(); $access = $this->getMock('\OCA\User_LDAP\Access', $accMethods, - array($connector, $lw, $um)); + array($connector, $lw, $um, $helper)); $access->expects($this->any()) ->method('getConnection') diff --git a/apps/user_ldap/tests/Integration/AbstractIntegrationTest.php b/apps/user_ldap/tests/Integration/AbstractIntegrationTest.php index 4ec36617c14..bd56494eac0 100644 --- a/apps/user_ldap/tests/Integration/AbstractIntegrationTest.php +++ b/apps/user_ldap/tests/Integration/AbstractIntegrationTest.php @@ -26,6 +26,7 @@ namespace OCA\User_LDAP\Tests\Integration; use OCA\User_LDAP\Access; use OCA\User_LDAP\Connection; use OCA\User_LDAP\LDAP; +use OCA\User_LDAP\Helper; use OCA\User_LDAP\User\Manager; abstract class AbstractIntegrationTest { @@ -40,6 +41,9 @@ abstract class AbstractIntegrationTest { /** @var Manager */ protected $userManager; + + /** @var Helper */ + protected $helper; /** @var string */ protected $base; @@ -65,6 +69,7 @@ abstract class AbstractIntegrationTest { $this->initLDAPWrapper(); $this->initConnection(); $this->initUserManager(); + $this->initHelper(); $this->initAccess(); } @@ -103,12 +108,19 @@ abstract class AbstractIntegrationTest { protected function initUserManager() { $this->userManager = new FakeManager(); } + + /** + * initializes the test Helper + */ + protected function initHelper() { + $this->helper = new Helper(); + } /** * initializes the Access test instance */ protected function initAccess() { - $this->access = new Access($this->connection, $this->ldap, $this->userManager); + $this->access = new Access($this->connection, $this->ldap, $this->userManager, $this->helper); } /** diff --git a/apps/user_ldap/tests/LDAPProviderTest.php b/apps/user_ldap/tests/LDAPProviderTest.php new file mode 100644 index 00000000000..22dfb61e5fb --- /dev/null +++ b/apps/user_ldap/tests/LDAPProviderTest.php @@ -0,0 +1,338 @@ +<?php +/** + * + * @copyright Copyright (c) 2016, Roger Szabo (roger.szabo@web.de) + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\User_LDAP\Tests; + +use OCP\IServerContainer; +use OCA\User_LDAP\IUserLDAP; + +/** + * Class LDAPProviderTest + * + * @group DB + * + * @package OCA\User_LDAP\Tests + */ +class LDAPProviderTest extends \Test\TestCase { + + protected function setUp() { + parent::setUp(); + } + + private function getServerMock(IUserLDAP $backend) { + $server = $this->getMockBuilder('OC\Server') + ->setMethods(['getUserManager', 'getBackends']) + ->setConstructorArgs(['', new \OC\Config(\OC::$configDir)]) + ->getMock(); + $server->expects($this->at(1)) + ->method('getBackends') + ->willReturn([$backend]); + $server->expects($this->any()) + ->method($this->anything()) + ->willReturnSelf(); + + return $server; + } + + private function getLDAPProvider(IServerContainer $serverContainer) { + $factory = new \OCA\User_LDAP\LDAPProviderFactory($serverContainer); + return $factory->getLDAPProvider(); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage User id not found in LDAP + */ + public function testGetUserDNUserIDNotFound() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->any())->method('userExists')->willReturn(false); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $ldapProvider->getUserDN('nonexisting_user'); + } + + public function testGetUserDN() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists', 'getLDAPAccess', 'username2dn']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->at(0)) + ->method('userExists') + ->willReturn(true); + $backend->expects($this->at(2)) + ->method('username2dn') + ->willReturn('cn=existing_user,ou=Are Sufficient To,ou=Test,dc=example,dc=org'); + $backend->expects($this->any()) + ->method($this->anything()) + ->willReturnSelf(); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $this->assertEquals('cn=existing_user,ou=Are Sufficient To,ou=Test,dc=example,dc=org', + $ldapProvider->getUserDN('existing_user')); + } + + public function testGetUserName() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['dn2UserName']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->any()) + ->method('dn2UserName') + ->willReturn('existing_user'); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $this->assertEquals('existing_user', + $ldapProvider->getUserName('cn=existing_user,ou=Are Sufficient To,ou=Test,dc=example,dc=org')); + } + + public function testDNasBaseParameter() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods([]) + ->disableOriginalConstructor() + ->getMock(); + + $server = $this->getServerMock($backend); + + $helper = new \OCA\User_LDAP\Helper(); + + $ldapProvider = $this->getLDAPProvider($server); + $this->assertEquals( + $helper->DNasBaseParameter('cn=existing_user,ou=Are Sufficient To,ou=Test,dc=example,dc=org'), + $ldapProvider->DNasBaseParameter('cn=existing_user,ou=Are Sufficient To,ou=Test,dc=example,dc=org')); + } + + public function testSanitizeDN() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods([]) + ->disableOriginalConstructor() + ->getMock(); + + $server = $this->getServerMock($backend); + + $helper = new \OCA\User_LDAP\Helper(); + + $ldapProvider = $this->getLDAPProvider($server); + $this->assertEquals( + $helper->sanitizeDN('cn=existing_user,ou=Are Sufficient To,ou=Test,dc=example,dc=org'), + $ldapProvider->sanitizeDN('cn=existing_user,ou=Are Sufficient To,ou=Test,dc=example,dc=org')); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage User id not found in LDAP + */ + public function testGetLDAPConnectionUserIDNotFound() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->any())->method('userExists')->willReturn(false); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $ldapProvider->getLDAPConnection('nonexisting_user'); + } + + public function testGetLDAPConnection() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists', 'getNewLDAPConnection']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->any()) + ->method('userExists') + ->willReturn(true); + $backend->expects($this->any()) + ->method('getNewLDAPConnection') + ->willReturn(true); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $this->assertTrue($ldapProvider->getLDAPConnection('existing_user')); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage User id not found in LDAP + */ + public function testGetLDAPBaseUsersUserIDNotFound() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->any())->method('userExists')->willReturn(false); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $ldapProvider->getLDAPBaseUsers('nonexisting_user'); + } + + public function testGetLDAPBaseUsers() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists', 'getLDAPAccess', 'getConnection', 'getConfiguration']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->at(0)) + ->method('userExists') + ->willReturn(true); + $backend->expects($this->at(3)) + ->method('getConfiguration') + ->willReturn(array('ldap_base_users'=>'ou=users,dc=example,dc=org')); + $backend->expects($this->any()) + ->method($this->anything()) + ->willReturnSelf(); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $this->assertEquals('ou=users,dc=example,dc=org', $ldapProvider->getLDAPBaseUsers('existing_user')); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage User id not found in LDAP + */ + public function testGetLDAPBaseGroupsUserIDNotFound() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->any())->method('userExists')->willReturn(false); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $ldapProvider->getLDAPBaseGroups('nonexisting_user'); + } + + public function testGetLDAPBaseGroups() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists', 'getLDAPAccess', 'getConnection', 'getConfiguration']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->at(0)) + ->method('userExists') + ->willReturn(true); + $backend->expects($this->at(3)) + ->method('getConfiguration') + ->willReturn(array('ldap_base_groups'=>'ou=groups,dc=example,dc=org')); + $backend->expects($this->any()) + ->method($this->anything()) + ->willReturnSelf(); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $this->assertEquals('ou=groups,dc=example,dc=org', $ldapProvider->getLDAPBaseGroups('existing_user')); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage User id not found in LDAP + */ + public function testClearCacheUserIDNotFound() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->any())->method('userExists')->willReturn(false); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $ldapProvider->clearCache('nonexisting_user'); + } + + public function testClearCache() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists', 'getLDAPAccess', 'getConnection', 'clearCache']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->at(0)) + ->method('userExists') + ->willReturn(true); + $backend->expects($this->at(3)) + ->method('clearCache') + ->willReturn(true); + $backend->expects($this->any()) + ->method($this->anything()) + ->willReturnSelf(); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $ldapProvider->clearCache('existing_user'); + $this->assertTrue(TRUE); + } + + public function testDnExists() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['dn2UserName']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->any()) + ->method('dn2UserName') + ->willReturn('existing_user'); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $this->assertTrue($ldapProvider->dnExists('cn=existing_user,ou=Are Sufficient To,ou=Test,dc=example,dc=org')); + } + + public function testFlagRecord() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods([]) + ->disableOriginalConstructor() + ->getMock(); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $ldapProvider->flagRecord('existing_user'); + $this->assertTrue(TRUE); + } + + public function testUnflagRecord() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods([]) + ->disableOriginalConstructor() + ->getMock(); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $ldapProvider->unflagRecord('existing_user'); + $this->assertTrue(TRUE); + } +} diff --git a/apps/user_ldap/tests/User/UserTest.php b/apps/user_ldap/tests/User/UserTest.php index cf1514009d8..d9e43dee047 100644 --- a/apps/user_ldap/tests/User/UserTest.php +++ b/apps/user_ldap/tests/User/UserTest.php @@ -71,8 +71,9 @@ class UserTest extends \Test\TestCase { $umMethods, array($cfMock, $fsMock, $logMock, $avaMgr, $im, $dbc, $userMgr)); $connector = $this->getMock('\OCA\User_LDAP\Connection', $conMethods, array($lw, null, null)); + $helper = new \OCA\User_LDAP\Helper(); $access = $this->getMock('\OCA\User_LDAP\Access', - $accMethods, array($connector, $lw, $um)); + $accMethods, array($connector, $lw, $um, $helper)); return array($access, $connector); } diff --git a/apps/user_ldap/tests/User_LDAPTest.php b/apps/user_ldap/tests/User_LDAPTest.php index ae86e3cf3a2..47e789142e5 100644 --- a/apps/user_ldap/tests/User_LDAPTest.php +++ b/apps/user_ldap/tests/User_LDAPTest.php @@ -93,9 +93,11 @@ class User_LDAPTest extends \Test\TestCase { ->method('getDeletedUser') ->will($this->returnValue($offlineUser)); + $helper = new \OCA\User_LDAP\Helper(); + $access = $this->getMock('\OCA\User_LDAP\Access', $accMethods, - array($connector, $lw, $um)); + array($connector, $lw, $um, $helper)); $um->setLdapAccess($access); diff --git a/apps/user_ldap/tests/WizardTest.php b/apps/user_ldap/tests/WizardTest.php index cc110c6ee0d..e82cbcfc82a 100644 --- a/apps/user_ldap/tests/WizardTest.php +++ b/apps/user_ldap/tests/WizardTest.php @@ -69,8 +69,9 @@ class WizardTest extends \Test\TestCase { $um = $this->getMockBuilder('\OCA\User_LDAP\User\Manager') ->disableOriginalConstructor() ->getMock(); + $helper = new \OCA\User_LDAP\Helper(); $access = $this->getMock('\OCA\User_LDAP\Access', - $accMethods, array($connector, $lw, $um)); + $accMethods, array($connector, $lw, $um, $helper)); return array(new Wizard($conf, $lw, $access), $conf, $lw, $access); } diff --git a/apps/workflowengine/lib/Check/UserGroupMembership.php b/apps/workflowengine/lib/Check/UserGroupMembership.php index f437dbfc2d1..6390c57fbea 100644 --- a/apps/workflowengine/lib/Check/UserGroupMembership.php +++ b/apps/workflowengine/lib/Check/UserGroupMembership.php @@ -24,6 +24,7 @@ namespace OCA\WorkflowEngine\Check; use OCP\Files\Storage\IStorage; use OCP\IGroupManager; +use OCP\IL10N; use OCP\IUser; use OCP\IUserSession; use OCP\WorkflowEngine\ICheck; @@ -42,13 +43,18 @@ class UserGroupMembership implements ICheck { /** @var IGroupManager */ protected $groupManager; + /** @var IL10N */ + protected $l; + /** * @param IUserSession $userSession * @param IGroupManager $groupManager + * @param IL10N $l */ - public function __construct(IUserSession $userSession, IGroupManager $groupManager) { + public function __construct(IUserSession $userSession, IGroupManager $groupManager, IL10N $l) { $this->userSession = $userSession; $this->groupManager = $groupManager; + $this->l = $l; } /** @@ -83,11 +89,11 @@ class UserGroupMembership implements ICheck { */ public function validateCheck($operator, $value) { if (!in_array($operator, ['is', '!is'])) { - throw new \UnexpectedValueException('Invalid operator', 1); + throw new \UnexpectedValueException($this->l->t('Operator %s is invalid', $operator), 1); } if (!$this->groupManager->groupExists($value)) { - throw new \UnexpectedValueException('Group does not exist', 2); + throw new \UnexpectedValueException($this->l->t('Group %s does not exist', $value), 2); } } diff --git a/apps/workflowengine/lib/Manager.php b/apps/workflowengine/lib/Manager.php index f2a04dfb0fa..b72836a919c 100644 --- a/apps/workflowengine/lib/Manager.php +++ b/apps/workflowengine/lib/Manager.php @@ -26,6 +26,7 @@ use OCP\AppFramework\QueryException; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\Storage\IStorage; use OCP\IDBConnection; +use OCP\IL10N; use OCP\IServerContainer; use OCP\WorkflowEngine\ICheck; use OCP\WorkflowEngine\IManager; @@ -50,13 +51,18 @@ class Manager implements IManager { /** @var IServerContainer|\OC\Server */ protected $container; + /** @var IL10N */ + protected $l; + /** * @param IDBConnection $connection * @param IServerContainer $container + * @param IL10N $l */ - public function __construct(IDBConnection $connection, IServerContainer $container) { + public function __construct(IDBConnection $connection, IServerContainer $container, IL10N $l) { $this->connection = $connection; $this->container = $container; + $this->l = $l; } /** @@ -111,7 +117,7 @@ class Manager implements IManager { return $checkInstance->executeCheck($check['operator'], $check['value']); } else { // Check is invalid - throw new \RuntimeException('Check ' . htmlspecialchars($check['class']) . ' is invalid or does not exist'); + throw new \UnexpectedValueException($this->l->t('Check %s is invalid or does not exist', $check['class'])); } } @@ -158,7 +164,7 @@ class Manager implements IManager { return $row; } - throw new \UnexpectedValueException('Operation does not exist'); + throw new \UnexpectedValueException($this->l->t('Operation #%s does not exist', $id)); } /** @@ -262,7 +268,7 @@ class Manager implements IManager { if (!empty($checkIds)) { $missingCheck = array_pop($checkIds); - throw new \RuntimeException('Check #' . htmlspecialchars($missingCheck) . ' is invalid or does not exist'); + throw new \UnexpectedValueException($this->l->t('Check #%s does not exist', $missingCheck)); } return $checks; diff --git a/apps/workflowengine/templates/admin.php b/apps/workflowengine/templates/admin.php index 9e01f8359fa..4eabf783bba 100644 --- a/apps/workflowengine/templates/admin.php +++ b/apps/workflowengine/templates/admin.php @@ -20,13 +20,13 @@ */ /** @var array $_ */ -/** @var OC_L10N $l */ +/** @var \OCP\IL10N $l */ ?> <div id="<?php p($_['appid']); ?>" class="section workflowengine"> <h2 class="inlineblock"><?php p($_['heading']); ?></h2> <script type="text/template" id="operations-template"> <div class="operations"></div> - <button class="button-add-operation">Add operation</button> + <button class="button-add-operation"><?php p($l->t('Add operation')); ?></button> </script> <script type="text/template" id="operation-template"> @@ -56,17 +56,17 @@ </div> {{/each}} </div> - <button class="button-add">Add check</button> + <button class="button-add"><?php p($l->t('Add check')); ?></button> {{#if hasChanged}} {{! reset only makes sense if the operation is already saved }} {{#if operation.id}} - <button class="button-reset pull-right">Reset</button> + <button class="button-reset pull-right"><?php p($l->t('Reset')); ?></button> {{/if}} - <button class="button-save pull-right">Save</button> + <button class="button-save pull-right"><?php p($l->t('Save')); ?></button> {{/if}} {{#if saving}} <span class="icon-loading-small pull-right"></span> - <span class="pull-right">Saving ...</span> + <span class="pull-right"><?php p($l->t('Saving…')); ?></span> {{else}}{{#if message}} <span class="msg pull-right {{#if errorMessage}}error{{else}}success{{/if}}"> {{message}}{{#if errorMessage}} {{errorMessage}}{{/if}} @@ -75,5 +75,5 @@ </div> </script> - <div class="rules"><span class="icon-loading-small"></span> Loading ...</div> + <div class="rules"><span class="icon-loading-small"></span> <?php p($l->t('Loading…')); ?></div> </div> |