diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 6375f33..2ba2eea 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -36,8 +36,6 @@ checks: parameter_doc_comments: true return_doc_comments: true fix_doc_comments: true - return_doc_comments: true - parameter_doc_comments: true more_specific_types_in_doc_comments: true code_rating: true duplication: true @@ -56,7 +54,6 @@ checks: parameter_non_unique: true no_property_on_interface: true no_non_implemented_abstract_methods: true - deprecated_code_usage: true closure_use_not_conflicting: true closure_use_modifiable: true avoid_useless_overridden_methods: true diff --git a/appinfo/info.xml b/appinfo/info.xml index 93d394c..bfa7e6a 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -28,10 +28,14 @@ Core App of the full-text search framework for your Nextcloud. OCA\FullTextSearch\Cron\Index + OCA\FullTextSearch\Cron\Maintenance OCA\FullTextSearch\Command\Check + OCA\FullTextSearch\Command\CollectionInit + OCA\FullTextSearch\Command\CollectionDelete + OCA\FullTextSearch\Command\CollectionList OCA\FullTextSearch\Command\Configure OCA\FullTextSearch\Command\DocumentIndex OCA\FullTextSearch\Command\DocumentPlatform @@ -39,6 +43,7 @@ Core App of the full-text search framework for your Nextcloud. OCA\FullTextSearch\Command\DocumentStatus OCA\FullTextSearch\Command\Index OCA\FullTextSearch\Command\Live + OCA\FullTextSearch\Command\Migration24 OCA\FullTextSearch\Command\Reset OCA\FullTextSearch\Command\Search OCA\FullTextSearch\Command\Stop diff --git a/appinfo/routes.php b/appinfo/routes.php index 967ac82..5b56710 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -29,6 +29,21 @@ declare(strict_types=1); return [ + 'ocs' => [ + /** @see OCA\FullTextSearch\Controller\CollectionController */ + ['name' => 'Collection#getQueue', 'url' => '/collection/{collection}/index', 'verb' => 'GET'], + [ + 'name' => 'Collection#indexDocument', + 'url' => '/collection/{collection}/document/{providerId}/{documentId}', + 'verb' => 'GET' + ], + [ + 'name' => 'Collection#updateStatusDone', + 'url' => '/collection/{collection}/document/{providerId}/{documentId}/done', + 'verb' => 'POST' + ] + ], + 'routes' => [ ['name' => 'Navigation#navigate', 'url' => '/', 'verb' => 'GET'], ['name' => 'Settings#getSettingsAdmin', 'url' => '/admin/settings', 'verb' => 'GET'], diff --git a/l10n/bg.js b/l10n/bg.js index 1295715..1cf722b 100644 --- a/l10n/bg.js +++ b/l10n/bg.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Платформа за търсене", "Select the app to index content and answer search queries." : "Изберете приложението, за да индексирате съдържание и да отговорите на заявки за търсене.", "Navigation Icon" : "Икона за навигация", - "Enable global search within all your content." : "Активиране на глобално търсене в цялото ви съдържание.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "търсенето в {title} за '{search}' докладва {total} резултата за {time} мс " + "Enable global search within all your content." : "Активиране на глобално търсене в цялото ви съдържание." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/bg.json b/l10n/bg.json index d0114a5..87faec3 100644 --- a/l10n/bg.json +++ b/l10n/bg.json @@ -14,7 +14,6 @@ "Search Platform" : "Платформа за търсене", "Select the app to index content and answer search queries." : "Изберете приложението, за да индексирате съдържание и да отговорите на заявки за търсене.", "Navigation Icon" : "Икона за навигация", - "Enable global search within all your content." : "Активиране на глобално търсене в цялото ви съдържание.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "търсенето в {title} за '{search}' докладва {total} резултата за {time} мс " + "Enable global search within all your content." : "Активиране на глобално търсене в цялото ви съдържание." },"pluralForm" :"nplurals=2; plural=(n != 1);" } \ No newline at end of file diff --git a/l10n/br.js b/l10n/br.js index 5b32841..e610511 100644 --- a/l10n/br.js +++ b/l10n/br.js @@ -14,7 +14,6 @@ OC.L10N.register( "Search Platform" : "Klask pladenn", "Select the app to index content and answer search queries." : "Choazit ur meziant da lakaat er roll-gerioù ha respontit d'ar goulennoù enklask", "Navigation Icon" : "Skeudennig Merdeiñ", - "Enable global search within all your content." : "Aotreañ an enklask hollek e-barzh pep tra.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "an enklask e {title} evit '{search}' en deus roet {total} disoc'h e {time} ms" + "Enable global search within all your content." : "Aotreañ an enklask hollek e-barzh pep tra." }, "nplurals=5; plural=((n%10 == 1) && (n%100 != 11) && (n%100 !=71) && (n%100 !=91) ? 0 :(n%10 == 2) && (n%100 != 12) && (n%100 !=72) && (n%100 !=92) ? 1 :(n%10 ==3 || n%10==4 || n%10==9) && (n%100 < 10 || n% 100 > 19) && (n%100 < 70 || n%100 > 79) && (n%100 < 90 || n%100 > 99) ? 2 :(n != 0 && n % 1000000 == 0) ? 3 : 4);"); diff --git a/l10n/br.json b/l10n/br.json index ae9138c..3a6a2c1 100644 --- a/l10n/br.json +++ b/l10n/br.json @@ -12,7 +12,6 @@ "Search Platform" : "Klask pladenn", "Select the app to index content and answer search queries." : "Choazit ur meziant da lakaat er roll-gerioù ha respontit d'ar goulennoù enklask", "Navigation Icon" : "Skeudennig Merdeiñ", - "Enable global search within all your content." : "Aotreañ an enklask hollek e-barzh pep tra.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "an enklask e {title} evit '{search}' en deus roet {total} disoc'h e {time} ms" + "Enable global search within all your content." : "Aotreañ an enklask hollek e-barzh pep tra." },"pluralForm" :"nplurals=5; plural=((n%10 == 1) && (n%100 != 11) && (n%100 !=71) && (n%100 !=91) ? 0 :(n%10 == 2) && (n%100 != 12) && (n%100 !=72) && (n%100 !=92) ? 1 :(n%10 ==3 || n%10==4 || n%10==9) && (n%100 < 10 || n% 100 > 19) && (n%100 < 70 || n%100 > 79) && (n%100 < 90 || n%100 > 99) ? 2 :(n != 0 && n % 1000000 == 0) ? 3 : 4);" } \ No newline at end of file diff --git a/l10n/ca.js b/l10n/ca.js index ece4055..9d5a997 100644 --- a/l10n/ca.js +++ b/l10n/ca.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Plataforma de cerca", "Select the app to index content and answer search queries." : "Selecciona l'aplicació per indexar el contingut i respondre consultes de cerca.", "Navigation Icon" : "Icona de navegació", - "Enable global search within all your content." : "Acitveu la cerca global dins de tots els vostres continguts.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "la cerca a {title} de '{search}' ha retornat {total} resultats en {time} ms" + "Enable global search within all your content." : "Acitveu la cerca global dins de tots els vostres continguts." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/ca.json b/l10n/ca.json index 21ec86d..19899b8 100644 --- a/l10n/ca.json +++ b/l10n/ca.json @@ -14,7 +14,6 @@ "Search Platform" : "Plataforma de cerca", "Select the app to index content and answer search queries." : "Selecciona l'aplicació per indexar el contingut i respondre consultes de cerca.", "Navigation Icon" : "Icona de navegació", - "Enable global search within all your content." : "Acitveu la cerca global dins de tots els vostres continguts.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "la cerca a {title} de '{search}' ha retornat {total} resultats en {time} ms" + "Enable global search within all your content." : "Acitveu la cerca global dins de tots els vostres continguts." },"pluralForm" :"nplurals=2; plural=(n != 1);" } \ No newline at end of file diff --git a/l10n/cs.js b/l10n/cs.js index 3686b0e..195250b 100644 --- a/l10n/cs.js +++ b/l10n/cs.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Vyhledávací platforma", "Select the app to index content and answer search queries." : "Vyberte aplikaci kterou indexovat obsah a zodpovídat dotazy vyhledávání.", "Navigation Icon" : "Navigační ikona", - "Enable global search within all your content." : "Zapnout globální vyhledávání ve veškerém vašem obsahu.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "vyhledávání v {title} pro výraz „{search}“ nalezlo {total} výsledků v čase {time} ms" + "Enable global search within all your content." : "Zapnout globální vyhledávání ve veškerém vašem obsahu." }, "nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;"); diff --git a/l10n/cs.json b/l10n/cs.json index d7e1c61..f571de3 100644 --- a/l10n/cs.json +++ b/l10n/cs.json @@ -14,7 +14,6 @@ "Search Platform" : "Vyhledávací platforma", "Select the app to index content and answer search queries." : "Vyberte aplikaci kterou indexovat obsah a zodpovídat dotazy vyhledávání.", "Navigation Icon" : "Navigační ikona", - "Enable global search within all your content." : "Zapnout globální vyhledávání ve veškerém vašem obsahu.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "vyhledávání v {title} pro výraz „{search}“ nalezlo {total} výsledků v čase {time} ms" + "Enable global search within all your content." : "Zapnout globální vyhledávání ve veškerém vašem obsahu." },"pluralForm" :"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;" } \ No newline at end of file diff --git a/l10n/da.js b/l10n/da.js index 8019461..28ad6dd 100644 --- a/l10n/da.js +++ b/l10n/da.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Søge Platform", "Select the app to index content and answer search queries." : "Vælg app for at indeksere indholdt og besvare søge forespørgsler.", "Navigation Icon" : "Navigations Ikon", - "Enable global search within all your content." : "Aktivere globale søgninger i alt dit indhold.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "søgning i {title} for '{search}' returnerede {total} resultater på bare {time} ms" + "Enable global search within all your content." : "Aktivere globale søgninger i alt dit indhold." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/da.json b/l10n/da.json index d9c92ee..29d628f 100644 --- a/l10n/da.json +++ b/l10n/da.json @@ -14,7 +14,6 @@ "Search Platform" : "Søge Platform", "Select the app to index content and answer search queries." : "Vælg app for at indeksere indholdt og besvare søge forespørgsler.", "Navigation Icon" : "Navigations Ikon", - "Enable global search within all your content." : "Aktivere globale søgninger i alt dit indhold.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "søgning i {title} for '{search}' returnerede {total} resultater på bare {time} ms" + "Enable global search within all your content." : "Aktivere globale søgninger i alt dit indhold." },"pluralForm" :"nplurals=2; plural=(n != 1);" } \ No newline at end of file diff --git a/l10n/de.js b/l10n/de.js index 2e22789..626ba95 100644 --- a/l10n/de.js +++ b/l10n/de.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Suchplattform", "Select the app to index content and answer search queries." : "Wähle die App, die Inhalte indizieren und Such-Anfragen beantworten soll.", "Navigation Icon" : "Navigations-Symbol", - "Enable global search within all your content." : "Globale Suche über alle Inhalte aktivieren.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "Die Suche in {title} nach '{search}' ergab {total} Treffer in {time} ms" + "Enable global search within all your content." : "Globale Suche über alle Inhalte aktivieren." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/de.json b/l10n/de.json index 0967c67..5576745 100644 --- a/l10n/de.json +++ b/l10n/de.json @@ -14,7 +14,6 @@ "Search Platform" : "Suchplattform", "Select the app to index content and answer search queries." : "Wähle die App, die Inhalte indizieren und Such-Anfragen beantworten soll.", "Navigation Icon" : "Navigations-Symbol", - "Enable global search within all your content." : "Globale Suche über alle Inhalte aktivieren.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "Die Suche in {title} nach '{search}' ergab {total} Treffer in {time} ms" + "Enable global search within all your content." : "Globale Suche über alle Inhalte aktivieren." },"pluralForm" :"nplurals=2; plural=(n != 1);" } \ No newline at end of file diff --git a/l10n/de_DE.js b/l10n/de_DE.js index 9331bf3..6edc5ad 100644 --- a/l10n/de_DE.js +++ b/l10n/de_DE.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Suchplattform", "Select the app to index content and answer search queries." : "Wählen Sie die App, die Inhalte indizieren und Suchanfragen beantworten soll.", "Navigation Icon" : "Navigations-Symbol", - "Enable global search within all your content." : "Globale Suche für alle Inhalte aktivieren.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "Die Suche in {title} nach '{search}' ergab {total} Treffer in {time} ms" + "Enable global search within all your content." : "Globale Suche für alle Inhalte aktivieren." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/de_DE.json b/l10n/de_DE.json index da50eaf..77e5d01 100644 --- a/l10n/de_DE.json +++ b/l10n/de_DE.json @@ -14,7 +14,6 @@ "Search Platform" : "Suchplattform", "Select the app to index content and answer search queries." : "Wählen Sie die App, die Inhalte indizieren und Suchanfragen beantworten soll.", "Navigation Icon" : "Navigations-Symbol", - "Enable global search within all your content." : "Globale Suche für alle Inhalte aktivieren.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "Die Suche in {title} nach '{search}' ergab {total} Treffer in {time} ms" + "Enable global search within all your content." : "Globale Suche für alle Inhalte aktivieren." },"pluralForm" :"nplurals=2; plural=(n != 1);" } \ No newline at end of file diff --git a/l10n/el.js b/l10n/el.js index a8b6a6b..ff0a031 100644 --- a/l10n/el.js +++ b/l10n/el.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Πλατφόρμα αναζήτησης", "Select the app to index content and answer search queries." : "Επιλέξτε την εφαρμογή για την ευρετηρίοποίηση περιεχομένου και απαντήσεων σε ερωτήματα αναζήτησης.", "Navigation Icon" : "Εικονίδιο Πλοήγησης", - "Enable global search within all your content." : "Ενεργοποιήστε την γενική αναζήτηση σε όλο το περιεχόμενό σας.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "η αναζήτηση στο {title} για '{search}' επέστρεψε {total} αποτελέσματα σε {time} ms" + "Enable global search within all your content." : "Ενεργοποιήστε την γενική αναζήτηση σε όλο το περιεχόμενό σας." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/el.json b/l10n/el.json index 5817bd4..e59e3aa 100644 --- a/l10n/el.json +++ b/l10n/el.json @@ -14,7 +14,6 @@ "Search Platform" : "Πλατφόρμα αναζήτησης", "Select the app to index content and answer search queries." : "Επιλέξτε την εφαρμογή για την ευρετηρίοποίηση περιεχομένου και απαντήσεων σε ερωτήματα αναζήτησης.", "Navigation Icon" : "Εικονίδιο Πλοήγησης", - "Enable global search within all your content." : "Ενεργοποιήστε την γενική αναζήτηση σε όλο το περιεχόμενό σας.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "η αναζήτηση στο {title} για '{search}' επέστρεψε {total} αποτελέσματα σε {time} ms" + "Enable global search within all your content." : "Ενεργοποιήστε την γενική αναζήτηση σε όλο το περιεχόμενό σας." },"pluralForm" :"nplurals=2; plural=(n != 1);" } \ No newline at end of file diff --git a/l10n/eo.js b/l10n/eo.js index 3c67887..7013f34 100644 --- a/l10n/eo.js +++ b/l10n/eo.js @@ -14,7 +14,6 @@ OC.L10N.register( "Search Platform" : "Serĉa platformo", "Select the app to index content and answer search queries." : "Elekti la aplikaĵon, kiu indeksos la enhavon kaj respondos serĉopetojn.", "Navigation Icon" : "Navigada piktogramo", - "Enable global search within all your content." : "Ebligi serĉon ene de ĉiuj viaj enhavoj.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "la serĉo en {title} pri „{search}“ liveris {total} rezultojn en {time} ms" + "Enable global search within all your content." : "Ebligi serĉon ene de ĉiuj viaj enhavoj." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/eo.json b/l10n/eo.json index fec6523..d75b118 100644 --- a/l10n/eo.json +++ b/l10n/eo.json @@ -12,7 +12,6 @@ "Search Platform" : "Serĉa platformo", "Select the app to index content and answer search queries." : "Elekti la aplikaĵon, kiu indeksos la enhavon kaj respondos serĉopetojn.", "Navigation Icon" : "Navigada piktogramo", - "Enable global search within all your content." : "Ebligi serĉon ene de ĉiuj viaj enhavoj.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "la serĉo en {title} pri „{search}“ liveris {total} rezultojn en {time} ms" + "Enable global search within all your content." : "Ebligi serĉon ene de ĉiuj viaj enhavoj." },"pluralForm" :"nplurals=2; plural=(n != 1);" } \ No newline at end of file diff --git a/l10n/es.js b/l10n/es.js index a74ef50..0b9a8f9 100644 --- a/l10n/es.js +++ b/l10n/es.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Plataforma de búsqueda", "Select the app to index content and answer search queries." : "Selecciona la app para indexar contenido y responder a peticiones de búsqueda", "Navigation Icon" : "Icono de navegación", - "Enable global search within all your content." : "Activa la búsqueda global en todo tu contenido.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "la búsqueda en {title} con '{search}' encontró {total} resultados en {time} ms" + "Enable global search within all your content." : "Activa la búsqueda global en todo tu contenido." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/es.json b/l10n/es.json index 6501a96..2453fb3 100644 --- a/l10n/es.json +++ b/l10n/es.json @@ -14,7 +14,6 @@ "Search Platform" : "Plataforma de búsqueda", "Select the app to index content and answer search queries." : "Selecciona la app para indexar contenido y responder a peticiones de búsqueda", "Navigation Icon" : "Icono de navegación", - "Enable global search within all your content." : "Activa la búsqueda global en todo tu contenido.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "la búsqueda en {title} con '{search}' encontró {total} resultados en {time} ms" + "Enable global search within all your content." : "Activa la búsqueda global en todo tu contenido." },"pluralForm" :"nplurals=2; plural=(n != 1);" } \ No newline at end of file diff --git a/l10n/eu.js b/l10n/eu.js index 15054cd..ae3e03c 100644 --- a/l10n/eu.js +++ b/l10n/eu.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Bilaketa plataforma", "Select the app to index content and answer search queries." : "Aukeratu zein aplikaziok indexatu eta erantzungo dituen duen zure edukiaren inguruko bilaketak.", "Navigation Icon" : "Nabigazioaren ikonoa", - "Enable global search within all your content." : "Aktibatu zure eduki guztian bilatzea.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "{title} eremuan '{search}' bilatzeak {total} emaitza itzuli ditu {time} ms-tan" + "Enable global search within all your content." : "Aktibatu zure eduki guztian bilatzea." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/eu.json b/l10n/eu.json index f484dc8..a07893b 100644 --- a/l10n/eu.json +++ b/l10n/eu.json @@ -14,7 +14,6 @@ "Search Platform" : "Bilaketa plataforma", "Select the app to index content and answer search queries." : "Aukeratu zein aplikaziok indexatu eta erantzungo dituen duen zure edukiaren inguruko bilaketak.", "Navigation Icon" : "Nabigazioaren ikonoa", - "Enable global search within all your content." : "Aktibatu zure eduki guztian bilatzea.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "{title} eremuan '{search}' bilatzeak {total} emaitza itzuli ditu {time} ms-tan" + "Enable global search within all your content." : "Aktibatu zure eduki guztian bilatzea." },"pluralForm" :"nplurals=2; plural=(n != 1);" } \ No newline at end of file diff --git a/l10n/fa.js b/l10n/fa.js index a1eda1e..584a8cb 100644 --- a/l10n/fa.js +++ b/l10n/fa.js @@ -14,7 +14,6 @@ OC.L10N.register( "Search Platform" : "پلتفرم جستجو", "Select the app to index content and answer search queries." : "برنامه را برای فهرست بندی محتوا و پاسخ به سؤالات جستجو انتخاب کنید", "Navigation Icon" : "نماد ناوبری", - "Enable global search within all your content." : "جستجوی جهانی را در تمام محتوای خود فعال کن", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "جستجو در {عنوان} برای '{جستجو}' بازگشت {مجموع} نتیجه در {زمان} " + "Enable global search within all your content." : "جستجوی جهانی را در تمام محتوای خود فعال کن" }, "nplurals=2; plural=(n > 1);"); diff --git a/l10n/fa.json b/l10n/fa.json index 533c368..c5db573 100644 --- a/l10n/fa.json +++ b/l10n/fa.json @@ -12,7 +12,6 @@ "Search Platform" : "پلتفرم جستجو", "Select the app to index content and answer search queries." : "برنامه را برای فهرست بندی محتوا و پاسخ به سؤالات جستجو انتخاب کنید", "Navigation Icon" : "نماد ناوبری", - "Enable global search within all your content." : "جستجوی جهانی را در تمام محتوای خود فعال کن", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "جستجو در {عنوان} برای '{جستجو}' بازگشت {مجموع} نتیجه در {زمان} " + "Enable global search within all your content." : "جستجوی جهانی را در تمام محتوای خود فعال کن" },"pluralForm" :"nplurals=2; plural=(n > 1);" } \ No newline at end of file diff --git a/l10n/fr.js b/l10n/fr.js index 0106822..cd8b3ee 100644 --- a/l10n/fr.js +++ b/l10n/fr.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Plateforme de recherche", "Select the app to index content and answer search queries." : "Sélectionner l'application pour indexer le contenu et répondre à des recherches.", "Navigation Icon" : "Icône de navigation", - "Enable global search within all your content." : "Activer la recherche dans tous votre contenu.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "la recherche dans {title} pour '{search}' a retourné {total} résultats en {time} ms" + "Enable global search within all your content." : "Activer la recherche dans tous votre contenu." }, "nplurals=2; plural=(n > 1);"); diff --git a/l10n/fr.json b/l10n/fr.json index 8d24688..fe26d78 100644 --- a/l10n/fr.json +++ b/l10n/fr.json @@ -14,7 +14,6 @@ "Search Platform" : "Plateforme de recherche", "Select the app to index content and answer search queries." : "Sélectionner l'application pour indexer le contenu et répondre à des recherches.", "Navigation Icon" : "Icône de navigation", - "Enable global search within all your content." : "Activer la recherche dans tous votre contenu.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "la recherche dans {title} pour '{search}' a retourné {total} résultats en {time} ms" + "Enable global search within all your content." : "Activer la recherche dans tous votre contenu." },"pluralForm" :"nplurals=2; plural=(n > 1);" } \ No newline at end of file diff --git a/l10n/gd.js b/l10n/gd.js new file mode 100644 index 0000000..9515dbd --- /dev/null +++ b/l10n/gd.js @@ -0,0 +1,7 @@ +OC.L10N.register( + "fulltextsearch", + { + "Search" : "Lorg", + "General" : "Coitcheann" +}, +"nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3;"); diff --git a/l10n/gd.json b/l10n/gd.json new file mode 100644 index 0000000..ab27abf --- /dev/null +++ b/l10n/gd.json @@ -0,0 +1,5 @@ +{ "translations": { + "Search" : "Lorg", + "General" : "Coitcheann" +},"pluralForm" :"nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3;" +} \ No newline at end of file diff --git a/l10n/gl.js b/l10n/gl.js index adb11e4..6012910 100644 --- a/l10n/gl.js +++ b/l10n/gl.js @@ -15,7 +15,6 @@ OC.L10N.register( "Search Platform" : "Plataforma de buscas", "Select the app to index content and answer search queries." : "Seleccionar a aplicación para indexar o contido e responder a peticións de buscas", "Navigation Icon" : "Icona de navegación", - "Enable global search within all your content." : "Activa a busca global en todo o seu contido.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "a busca en {title} de «{search}» devolveu {total} resultados en {time} ms." + "Enable global search within all your content." : "Activa a busca global en todo o seu contido." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/gl.json b/l10n/gl.json index 340e361..1f6494b 100644 --- a/l10n/gl.json +++ b/l10n/gl.json @@ -13,7 +13,6 @@ "Search Platform" : "Plataforma de buscas", "Select the app to index content and answer search queries." : "Seleccionar a aplicación para indexar o contido e responder a peticións de buscas", "Navigation Icon" : "Icona de navegación", - "Enable global search within all your content." : "Activa a busca global en todo o seu contido.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "a busca en {title} de «{search}» devolveu {total} resultados en {time} ms." + "Enable global search within all your content." : "Activa a busca global en todo o seu contido." },"pluralForm" :"nplurals=2; plural=(n != 1);" } \ No newline at end of file diff --git a/l10n/he.js b/l10n/he.js index 7e90240..f4cfb8c 100644 --- a/l10n/he.js +++ b/l10n/he.js @@ -14,7 +14,6 @@ OC.L10N.register( "Search Platform" : "פלטפורמת חיפוש", "Select the app to index content and answer search queries." : "נא לבחור את היישומון לסידור תוכן באינדקס ולענות על שאילתות חיפוש.", "Navigation Icon" : "סמל ניווט", - "Enable global search within all your content." : "הפעלת חיפוש גלובלי על כל התוכן שלך.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "החיפוש תחת {title} אחר ‚{search}’ החזיר {total} תוצאות תוך {time} מילישניות" + "Enable global search within all your content." : "הפעלת חיפוש גלובלי על כל התוכן שלך." }, "nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;"); diff --git a/l10n/he.json b/l10n/he.json index 6d27e1d..5ce8706 100644 --- a/l10n/he.json +++ b/l10n/he.json @@ -12,7 +12,6 @@ "Search Platform" : "פלטפורמת חיפוש", "Select the app to index content and answer search queries." : "נא לבחור את היישומון לסידור תוכן באינדקס ולענות על שאילתות חיפוש.", "Navigation Icon" : "סמל ניווט", - "Enable global search within all your content." : "הפעלת חיפוש גלובלי על כל התוכן שלך.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "החיפוש תחת {title} אחר ‚{search}’ החזיר {total} תוצאות תוך {time} מילישניות" + "Enable global search within all your content." : "הפעלת חיפוש גלובלי על כל התוכן שלך." },"pluralForm" :"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;" } \ No newline at end of file diff --git a/l10n/hr.js b/l10n/hr.js index e6a8fc4..c30a0a9 100644 --- a/l10n/hr.js +++ b/l10n/hr.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Platforma za pretraživanje", "Select the app to index content and answer search queries." : "Odaberite aplikaciju za indeksiranje sadržaja i odgovorite na upite pretraživanja.", "Navigation Icon" : "Navigacijska ikona", - "Enable global search within all your content." : "Omogućite globalno pretraživanje cijelog sadržaja.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "pretraživanjem u {title} za '{search}' pronađeno je {total} rezultata za {time} ms" + "Enable global search within all your content." : "Omogućite globalno pretraživanje cijelog sadržaja." }, "nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;"); diff --git a/l10n/hr.json b/l10n/hr.json index c8e11f0..dcdbcb2 100644 --- a/l10n/hr.json +++ b/l10n/hr.json @@ -14,7 +14,6 @@ "Search Platform" : "Platforma za pretraživanje", "Select the app to index content and answer search queries." : "Odaberite aplikaciju za indeksiranje sadržaja i odgovorite na upite pretraživanja.", "Navigation Icon" : "Navigacijska ikona", - "Enable global search within all your content." : "Omogućite globalno pretraživanje cijelog sadržaja.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "pretraživanjem u {title} za '{search}' pronađeno je {total} rezultata za {time} ms" + "Enable global search within all your content." : "Omogućite globalno pretraživanje cijelog sadržaja." },"pluralForm" :"nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;" } \ No newline at end of file diff --git a/l10n/hu.js b/l10n/hu.js index 7adf34e..699c79f 100644 --- a/l10n/hu.js +++ b/l10n/hu.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Keresési platform", "Select the app to index content and answer search queries." : "Válassza ki az alkalmazást, amely indexeli a tartalmat és válaszol a keresési kérésekre.", "Navigation Icon" : "Navigációs ikon", - "Enable global search within all your content." : "Globális keresés engedélyezése az összes tartalomban.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "a(z) {title} elemben, a(z) „{search}” keresés {total} találatot adott vissza {time} ms alatt" + "Enable global search within all your content." : "Globális keresés engedélyezése az összes tartalomban." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/hu.json b/l10n/hu.json index 3f0e757..50b722f 100644 --- a/l10n/hu.json +++ b/l10n/hu.json @@ -14,7 +14,6 @@ "Search Platform" : "Keresési platform", "Select the app to index content and answer search queries." : "Válassza ki az alkalmazást, amely indexeli a tartalmat és válaszol a keresési kérésekre.", "Navigation Icon" : "Navigációs ikon", - "Enable global search within all your content." : "Globális keresés engedélyezése az összes tartalomban.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "a(z) {title} elemben, a(z) „{search}” keresés {total} találatot adott vissza {time} ms alatt" + "Enable global search within all your content." : "Globális keresés engedélyezése az összes tartalomban." },"pluralForm" :"nplurals=2; plural=(n != 1);" } \ No newline at end of file diff --git a/l10n/is.js b/l10n/is.js index 62ddb1f..31ede8b 100644 --- a/l10n/is.js +++ b/l10n/is.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Leita á kerfi", "Select the app to index content and answer search queries." : "Veldu forrit til að atriðaskrá efni og svara leitarbeiðnum.", "Navigation Icon" : "Táknmynd flakks", - "Enable global search within all your content." : "Virkja víðværa leit inna alls efnisins þíns.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "leitin í {title} eftir '{search}' skilaði {total} niðurstöðum á {time} ms" + "Enable global search within all your content." : "Virkja víðværa leit inna alls efnisins þíns." }, "nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);"); diff --git a/l10n/is.json b/l10n/is.json index 35b7815..ff86787 100644 --- a/l10n/is.json +++ b/l10n/is.json @@ -14,7 +14,6 @@ "Search Platform" : "Leita á kerfi", "Select the app to index content and answer search queries." : "Veldu forrit til að atriðaskrá efni og svara leitarbeiðnum.", "Navigation Icon" : "Táknmynd flakks", - "Enable global search within all your content." : "Virkja víðværa leit inna alls efnisins þíns.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "leitin í {title} eftir '{search}' skilaði {total} niðurstöðum á {time} ms" + "Enable global search within all your content." : "Virkja víðværa leit inna alls efnisins þíns." },"pluralForm" :"nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);" } \ No newline at end of file diff --git a/l10n/it.js b/l10n/it.js index a83d49a..7133762 100644 --- a/l10n/it.js +++ b/l10n/it.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Piattaforma di ricerca", "Select the app to index content and answer search queries." : "Seleziona l'applicazione per indicizzare i contenuti e rispondere alle ricerche.", "Navigation Icon" : "Icona di navigazione", - "Enable global search within all your content." : "Abilita la ricerca globale in tutti i tuoi contenuti.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "la ricerca in {title} per '{search}' ha restituito {total} risultati in {time} ms" + "Enable global search within all your content." : "Abilita la ricerca globale in tutti i tuoi contenuti." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/it.json b/l10n/it.json index bb580be..1c4538e 100644 --- a/l10n/it.json +++ b/l10n/it.json @@ -14,7 +14,6 @@ "Search Platform" : "Piattaforma di ricerca", "Select the app to index content and answer search queries." : "Seleziona l'applicazione per indicizzare i contenuti e rispondere alle ricerche.", "Navigation Icon" : "Icona di navigazione", - "Enable global search within all your content." : "Abilita la ricerca globale in tutti i tuoi contenuti.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "la ricerca in {title} per '{search}' ha restituito {total} risultati in {time} ms" + "Enable global search within all your content." : "Abilita la ricerca globale in tutti i tuoi contenuti." },"pluralForm" :"nplurals=2; plural=(n != 1);" } \ No newline at end of file diff --git a/l10n/ja.js b/l10n/ja.js index f2a851d..406bd9b 100644 --- a/l10n/ja.js +++ b/l10n/ja.js @@ -15,7 +15,6 @@ OC.L10N.register( "Search Platform" : "検索プラットフォーム", "Select the app to index content and answer search queries." : "コンテンツをインデックス化して検索クエリに答えるアプリを選択します。", "Navigation Icon" : "ナビゲーションアイコン", - "Enable global search within all your content." : "すべてのコンテンツ内でグローバル検索を有効にする。", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "{title}で「{search}」を検索すると{time}msで{total}件の結果が返ってきます" + "Enable global search within all your content." : "すべてのコンテンツ内でグローバル検索を有効にする。" }, "nplurals=1; plural=0;"); diff --git a/l10n/ja.json b/l10n/ja.json index 51940a2..9da5a50 100644 --- a/l10n/ja.json +++ b/l10n/ja.json @@ -13,7 +13,6 @@ "Search Platform" : "検索プラットフォーム", "Select the app to index content and answer search queries." : "コンテンツをインデックス化して検索クエリに答えるアプリを選択します。", "Navigation Icon" : "ナビゲーションアイコン", - "Enable global search within all your content." : "すべてのコンテンツ内でグローバル検索を有効にする。", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "{title}で「{search}」を検索すると{time}msで{total}件の結果が返ってきます" + "Enable global search within all your content." : "すべてのコンテンツ内でグローバル検索を有効にする。" },"pluralForm" :"nplurals=1; plural=0;" } \ No newline at end of file diff --git a/l10n/kab.js b/l10n/kab.js new file mode 100644 index 0000000..086b516 --- /dev/null +++ b/l10n/kab.js @@ -0,0 +1,7 @@ +OC.L10N.register( + "fulltextsearch", + { + "Search" : "Nadi", + "General" : "Amatu" +}, +"nplurals=2; plural=(n != 1);"); diff --git a/l10n/kab.json b/l10n/kab.json new file mode 100644 index 0000000..c09fe50 --- /dev/null +++ b/l10n/kab.json @@ -0,0 +1,5 @@ +{ "translations": { + "Search" : "Nadi", + "General" : "Amatu" +},"pluralForm" :"nplurals=2; plural=(n != 1);" +} \ No newline at end of file diff --git a/l10n/lo.js b/l10n/lo.js index c632cdb..5c67645 100644 --- a/l10n/lo.js +++ b/l10n/lo.js @@ -1,6 +1,7 @@ OC.L10N.register( "fulltextsearch", { - "Search" : "ຄົ້ນຫາ" + "Search" : "ຄົ້ນຫາ", + "General" : "ທົ່ວໄປ" }, "nplurals=1; plural=0;"); diff --git a/l10n/lo.json b/l10n/lo.json index 94f3873..ab90e66 100644 --- a/l10n/lo.json +++ b/l10n/lo.json @@ -1,4 +1,5 @@ { "translations": { - "Search" : "ຄົ້ນຫາ" + "Search" : "ຄົ້ນຫາ", + "General" : "ທົ່ວໄປ" },"pluralForm" :"nplurals=1; plural=0;" } \ No newline at end of file diff --git a/l10n/lt_LT.js b/l10n/lt_LT.js index 4068bf8..7c3c999 100644 --- a/l10n/lt_LT.js +++ b/l10n/lt_LT.js @@ -14,7 +14,6 @@ OC.L10N.register( "Search Platform" : "Paieškos platforma", "Select the app to index content and answer search queries." : "Pasirinkite programą, norėdami indeksuoti turinį ir atsakyti į paieškos užklausas.", "Navigation Icon" : "Naršymo piktograma", - "Enable global search within all your content." : "Įjunkite visuotinę paiešką visame savo turinyje.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "ieškant „{title}“ žodžių „{search}“ pateikti {total} rezultatai per {laikas} ms" + "Enable global search within all your content." : "Įjunkite visuotinę paiešką visame savo turinyje." }, "nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);"); diff --git a/l10n/lt_LT.json b/l10n/lt_LT.json index cea4e65..ac012b9 100644 --- a/l10n/lt_LT.json +++ b/l10n/lt_LT.json @@ -12,7 +12,6 @@ "Search Platform" : "Paieškos platforma", "Select the app to index content and answer search queries." : "Pasirinkite programą, norėdami indeksuoti turinį ir atsakyti į paieškos užklausas.", "Navigation Icon" : "Naršymo piktograma", - "Enable global search within all your content." : "Įjunkite visuotinę paiešką visame savo turinyje.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "ieškant „{title}“ žodžių „{search}“ pateikti {total} rezultatai per {laikas} ms" + "Enable global search within all your content." : "Įjunkite visuotinę paiešką visame savo turinyje." },"pluralForm" :"nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);" } \ No newline at end of file diff --git a/l10n/nl.js b/l10n/nl.js index 44ccecf..975231a 100644 --- a/l10n/nl.js +++ b/l10n/nl.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Zoekplatform", "Select the app to index content and answer search queries." : "Selecteer de app om inhoud te indexeren en zoekvragen te beantwoorden.", "Navigation Icon" : "Navigatiepictogram", - "Enable global search within all your content." : "Inschakelen globaal al je inhoud indexeren.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "de zoekopdracht in {title} naar '{search}' leverde {total} resultaten op in {time} ms" + "Enable global search within all your content." : "Inschakelen globaal al je inhoud indexeren." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/nl.json b/l10n/nl.json index c3fcf40..4999c75 100644 --- a/l10n/nl.json +++ b/l10n/nl.json @@ -14,7 +14,6 @@ "Search Platform" : "Zoekplatform", "Select the app to index content and answer search queries." : "Selecteer de app om inhoud te indexeren en zoekvragen te beantwoorden.", "Navigation Icon" : "Navigatiepictogram", - "Enable global search within all your content." : "Inschakelen globaal al je inhoud indexeren.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "de zoekopdracht in {title} naar '{search}' leverde {total} resultaten op in {time} ms" + "Enable global search within all your content." : "Inschakelen globaal al je inhoud indexeren." },"pluralForm" :"nplurals=2; plural=(n != 1);" } \ No newline at end of file diff --git a/l10n/pl.js b/l10n/pl.js index 8cded01..e811fe2 100644 --- a/l10n/pl.js +++ b/l10n/pl.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Platforma wyszukiwania", "Select the app to index content and answer search queries." : "Wybierz aplikację, aby indeksować treść i odpowiadać na wyszukiwane hasła.", "Navigation Icon" : "Ikona nawigacji", - "Enable global search within all your content." : "Włącz globalne wyszukiwanie we wszystkich swoich treściach.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "wyszukiwanie dla '{search}' w {title} zwróciło {total} wyników w {time} ms" + "Enable global search within all your content." : "Włącz globalne wyszukiwanie we wszystkich swoich treściach." }, "nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);"); diff --git a/l10n/pl.json b/l10n/pl.json index 84fa107..d05dcfc 100644 --- a/l10n/pl.json +++ b/l10n/pl.json @@ -14,7 +14,6 @@ "Search Platform" : "Platforma wyszukiwania", "Select the app to index content and answer search queries." : "Wybierz aplikację, aby indeksować treść i odpowiadać na wyszukiwane hasła.", "Navigation Icon" : "Ikona nawigacji", - "Enable global search within all your content." : "Włącz globalne wyszukiwanie we wszystkich swoich treściach.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "wyszukiwanie dla '{search}' w {title} zwróciło {total} wyników w {time} ms" + "Enable global search within all your content." : "Włącz globalne wyszukiwanie we wszystkich swoich treściach." },"pluralForm" :"nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);" } \ No newline at end of file diff --git a/l10n/pt_BR.js b/l10n/pt_BR.js index 7841d78..cb45cab 100644 --- a/l10n/pt_BR.js +++ b/l10n/pt_BR.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Plataforma de Pesquisa", "Select the app to index content and answer search queries." : "Selecione o aplicativo para indexar conteúdo e responder a consultas de pesquisa.", "Navigation Icon" : "Ícone de Navegação", - "Enable global search within all your content." : "Ativar a pesquisa global em todo o seu conteúdo.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "a procura em {title} por '{search}' encontrou {total} resultados em {time} ms" + "Enable global search within all your content." : "Ativar a pesquisa global em todo o seu conteúdo." }, "nplurals=2; plural=(n > 1);"); diff --git a/l10n/pt_BR.json b/l10n/pt_BR.json index 3fcf347..2666845 100644 --- a/l10n/pt_BR.json +++ b/l10n/pt_BR.json @@ -14,7 +14,6 @@ "Search Platform" : "Plataforma de Pesquisa", "Select the app to index content and answer search queries." : "Selecione o aplicativo para indexar conteúdo e responder a consultas de pesquisa.", "Navigation Icon" : "Ícone de Navegação", - "Enable global search within all your content." : "Ativar a pesquisa global em todo o seu conteúdo.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "a procura em {title} por '{search}' encontrou {total} resultados em {time} ms" + "Enable global search within all your content." : "Ativar a pesquisa global em todo o seu conteúdo." },"pluralForm" :"nplurals=2; plural=(n > 1);" } \ No newline at end of file diff --git a/l10n/ru.js b/l10n/ru.js index 4d80862..0ea7b1a 100644 --- a/l10n/ru.js +++ b/l10n/ru.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Платформа поиска", "Select the app to index content and answer search queries." : "Выберите приложение для индексирования содержимого файлов и обработки поисковых запросов.", "Navigation Icon" : "Значок навигации", - "Enable global search within all your content." : "Включить глобальный поиск по всему содержимому.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "поиск в {title} по запросу «{search}» вернул {total} результатов за {time} мс" + "Enable global search within all your content." : "Включить глобальный поиск по всему содержимому." }, "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/l10n/ru.json b/l10n/ru.json index 8b05b11..817878a 100644 --- a/l10n/ru.json +++ b/l10n/ru.json @@ -14,7 +14,6 @@ "Search Platform" : "Платформа поиска", "Select the app to index content and answer search queries." : "Выберите приложение для индексирования содержимого файлов и обработки поисковых запросов.", "Navigation Icon" : "Значок навигации", - "Enable global search within all your content." : "Включить глобальный поиск по всему содержимому.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "поиск в {title} по запросу «{search}» вернул {total} результатов за {time} мс" + "Enable global search within all your content." : "Включить глобальный поиск по всему содержимому." },"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);" } \ No newline at end of file diff --git a/l10n/sc.js b/l10n/sc.js index fc2c5c3..a1e376b 100644 --- a/l10n/sc.js +++ b/l10n/sc.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Prataforma de chirca", "Select the app to index content and answer search queries." : "Sèbera s'aplicatzione pro inditzizare is cuntenutos e rispòndere a is chircas.", "Navigation Icon" : "Icona de navigatzione", - "Enable global search within all your content." : "Ativa sa chirca globale in totu su cuntenutu tuo.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "sa chirca in {title} pro '{search}' at torradu {total} resurtados in {time} ms" + "Enable global search within all your content." : "Ativa sa chirca globale in totu su cuntenutu tuo." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/sc.json b/l10n/sc.json index 038e5b0..1e81265 100644 --- a/l10n/sc.json +++ b/l10n/sc.json @@ -14,7 +14,6 @@ "Search Platform" : "Prataforma de chirca", "Select the app to index content and answer search queries." : "Sèbera s'aplicatzione pro inditzizare is cuntenutos e rispòndere a is chircas.", "Navigation Icon" : "Icona de navigatzione", - "Enable global search within all your content." : "Ativa sa chirca globale in totu su cuntenutu tuo.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "sa chirca in {title} pro '{search}' at torradu {total} resurtados in {time} ms" + "Enable global search within all your content." : "Ativa sa chirca globale in totu su cuntenutu tuo." },"pluralForm" :"nplurals=2; plural=(n != 1);" } \ No newline at end of file diff --git a/l10n/sk.js b/l10n/sk.js index 466e129..9135b8a 100644 --- a/l10n/sk.js +++ b/l10n/sk.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Vyhľadávacia platforma", "Select the app to index content and answer search queries." : "Vyber aplikáciu pre indexovanie obsahu a odpoveď na dotazy.", "Navigation Icon" : "Ikona navigácie", - "Enable global search within all your content." : "Povoliť globálne vyhľadávanie v celom obsahu.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "Vyhľadávanie '{search}' v {title} našlo {total} výsledkov za {time} ms" + "Enable global search within all your content." : "Povoliť globálne vyhľadávanie v celom obsahu." }, "nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);"); diff --git a/l10n/sk.json b/l10n/sk.json index 638880d..7d34ee2 100644 --- a/l10n/sk.json +++ b/l10n/sk.json @@ -14,7 +14,6 @@ "Search Platform" : "Vyhľadávacia platforma", "Select the app to index content and answer search queries." : "Vyber aplikáciu pre indexovanie obsahu a odpoveď na dotazy.", "Navigation Icon" : "Ikona navigácie", - "Enable global search within all your content." : "Povoliť globálne vyhľadávanie v celom obsahu.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "Vyhľadávanie '{search}' v {title} našlo {total} výsledkov za {time} ms" + "Enable global search within all your content." : "Povoliť globálne vyhľadávanie v celom obsahu." },"pluralForm" :"nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);" } \ No newline at end of file diff --git a/l10n/sl.js b/l10n/sl.js index e769526..bb90053 100644 --- a/l10n/sl.js +++ b/l10n/sl.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Iskalno orodje", "Select the app to index content and answer search queries." : "Izbor programa za indeksiranje vsebine in izvajanje iskalnih poizvedb.", "Navigation Icon" : "Navigacijska ikona", - "Enable global search within all your content." : "Omogoči splošno iskanje med vso vsebino oblaka.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "iskanje po predmetu {title} za »{search}« je vrnilo skupno {total} zadetkov v {time} ms" + "Enable global search within all your content." : "Omogoči splošno iskanje med vso vsebino oblaka." }, "nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);"); diff --git a/l10n/sl.json b/l10n/sl.json index 6bee341..87e5c31 100644 --- a/l10n/sl.json +++ b/l10n/sl.json @@ -14,7 +14,6 @@ "Search Platform" : "Iskalno orodje", "Select the app to index content and answer search queries." : "Izbor programa za indeksiranje vsebine in izvajanje iskalnih poizvedb.", "Navigation Icon" : "Navigacijska ikona", - "Enable global search within all your content." : "Omogoči splošno iskanje med vso vsebino oblaka.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "iskanje po predmetu {title} za »{search}« je vrnilo skupno {total} zadetkov v {time} ms" + "Enable global search within all your content." : "Omogoči splošno iskanje med vso vsebino oblaka." },"pluralForm" :"nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);" } \ No newline at end of file diff --git a/l10n/sr.js b/l10n/sr.js index 3ccc7bd..c01456c 100644 --- a/l10n/sr.js +++ b/l10n/sr.js @@ -15,7 +15,6 @@ OC.L10N.register( "Search Platform" : "Платформа претраге", "Select the app to index content and answer search queries." : "Одаберите апликацију за индексирање садржаја и за одговарање на упите.", "Navigation Icon" : "Икона навигације", - "Enable global search within all your content." : "Укључите глобалну претраге кроз цео Ваш садржај.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "претрага у {title} за '{search}' је вратила {total} резултата за {time} ms" + "Enable global search within all your content." : "Укључите глобалну претраге кроз цео Ваш садржај." }, "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"); diff --git a/l10n/sr.json b/l10n/sr.json index ae87c39..2f654db 100644 --- a/l10n/sr.json +++ b/l10n/sr.json @@ -13,7 +13,6 @@ "Search Platform" : "Платформа претраге", "Select the app to index content and answer search queries." : "Одаберите апликацију за индексирање садржаја и за одговарање на упите.", "Navigation Icon" : "Икона навигације", - "Enable global search within all your content." : "Укључите глобалну претраге кроз цео Ваш садржај.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "претрага у {title} за '{search}' је вратила {total} резултата за {time} ms" + "Enable global search within all your content." : "Укључите глобалну претраге кроз цео Ваш садржај." },"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" } \ No newline at end of file diff --git a/l10n/sv.js b/l10n/sv.js index 7c96d7c..ca336ef 100644 --- a/l10n/sv.js +++ b/l10n/sv.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Sökplattform", "Select the app to index content and answer search queries." : "Välj app för att indexera innehåll och svara på sökfrågor.", "Navigation Icon" : "Navigations-ikon", - "Enable global search within all your content." : "Aktivera global sökning i allt ditt innehåll.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "sökningen på {title} för '{search}' gav {total} resultat på {time} ms" + "Enable global search within all your content." : "Aktivera global sökning i allt ditt innehåll." }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/sv.json b/l10n/sv.json index 7dd373b..97303b7 100644 --- a/l10n/sv.json +++ b/l10n/sv.json @@ -14,7 +14,6 @@ "Search Platform" : "Sökplattform", "Select the app to index content and answer search queries." : "Välj app för att indexera innehåll och svara på sökfrågor.", "Navigation Icon" : "Navigations-ikon", - "Enable global search within all your content." : "Aktivera global sökning i allt ditt innehåll.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "sökningen på {title} för '{search}' gav {total} resultat på {time} ms" + "Enable global search within all your content." : "Aktivera global sökning i allt ditt innehåll." },"pluralForm" :"nplurals=2; plural=(n != 1);" } \ No newline at end of file diff --git a/l10n/tk.js b/l10n/tk.js new file mode 100644 index 0000000..70e6e9f --- /dev/null +++ b/l10n/tk.js @@ -0,0 +1,7 @@ +OC.L10N.register( + "fulltextsearch", + { + "Search" : "Gözlemek", + "General" : "Esasy" +}, +"nplurals=2; plural=(n != 1);"); diff --git a/l10n/tk.json b/l10n/tk.json new file mode 100644 index 0000000..3cb7a26 --- /dev/null +++ b/l10n/tk.json @@ -0,0 +1,5 @@ +{ "translations": { + "Search" : "Gözlemek", + "General" : "Esasy" +},"pluralForm" :"nplurals=2; plural=(n != 1);" +} \ No newline at end of file diff --git a/l10n/tr.js b/l10n/tr.js index fb6da9b..aa18033 100644 --- a/l10n/tr.js +++ b/l10n/tr.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "Arama platformu", "Select the app to index content and answer search queries." : "İçeriği dizine ekleyecek ve arama sorgularını yanıtlayacak uygulamayı seçin", "Navigation Icon" : "Gezinme simgesi", - "Enable global search within all your content." : "Tüm içeriğiniz üzerinde genel aramayı etkinleştirin.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "{title} başlığındaki '{search}' aramasında {time} ms sürede {total} sonuç bulundu" + "Enable global search within all your content." : "Tüm içeriğiniz üzerinde genel aramayı etkinleştirin." }, "nplurals=2; plural=(n > 1);"); diff --git a/l10n/tr.json b/l10n/tr.json index 914ca48..fbe6eb9 100644 --- a/l10n/tr.json +++ b/l10n/tr.json @@ -14,7 +14,6 @@ "Search Platform" : "Arama platformu", "Select the app to index content and answer search queries." : "İçeriği dizine ekleyecek ve arama sorgularını yanıtlayacak uygulamayı seçin", "Navigation Icon" : "Gezinme simgesi", - "Enable global search within all your content." : "Tüm içeriğiniz üzerinde genel aramayı etkinleştirin.", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "{title} başlığındaki '{search}' aramasında {time} ms sürede {total} sonuç bulundu" + "Enable global search within all your content." : "Tüm içeriğiniz üzerinde genel aramayı etkinleştirin." },"pluralForm" :"nplurals=2; plural=(n > 1);" } \ No newline at end of file diff --git a/l10n/zh_CN.js b/l10n/zh_CN.js index 44c34b1..b695101 100644 --- a/l10n/zh_CN.js +++ b/l10n/zh_CN.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "搜索平台", "Select the app to index content and answer search queries." : "选择用于索引内容和回答搜索查询的应用程序。", "Navigation Icon" : "导航图标", - "Enable global search within all your content." : "为您所有的内容启用全局搜索。", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "{title} 中对 '{search}' 的搜索在 {time} ms内返回 {total} 个结果" + "Enable global search within all your content." : "为您所有的内容启用全局搜索。" }, "nplurals=1; plural=0;"); diff --git a/l10n/zh_CN.json b/l10n/zh_CN.json index c9cfc65..b73b6c5 100644 --- a/l10n/zh_CN.json +++ b/l10n/zh_CN.json @@ -14,7 +14,6 @@ "Search Platform" : "搜索平台", "Select the app to index content and answer search queries." : "选择用于索引内容和回答搜索查询的应用程序。", "Navigation Icon" : "导航图标", - "Enable global search within all your content." : "为您所有的内容启用全局搜索。", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "{title} 中对 '{search}' 的搜索在 {time} ms内返回 {total} 个结果" + "Enable global search within all your content." : "为您所有的内容启用全局搜索。" },"pluralForm" :"nplurals=1; plural=0;" } \ No newline at end of file diff --git a/l10n/zh_HK.js b/l10n/zh_HK.js index 50cf787..d137686 100644 --- a/l10n/zh_HK.js +++ b/l10n/zh_HK.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "搜尋平台", "Select the app to index content and answer search queries." : "選擇應用以索引內容並回應搜尋查詢。", "Navigation Icon" : "導覽圖示", - "Enable global search within all your content." : "在您所有的內容中啟用全域搜尋。", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "對 '{search}' 中的 {title} 搜尋於 {time} ms 內回傳了 {total} 個結果" + "Enable global search within all your content." : "在您所有的內容中啟用全域搜尋。" }, "nplurals=1; plural=0;"); diff --git a/l10n/zh_HK.json b/l10n/zh_HK.json index 1d3e099..33d6afb 100644 --- a/l10n/zh_HK.json +++ b/l10n/zh_HK.json @@ -14,7 +14,6 @@ "Search Platform" : "搜尋平台", "Select the app to index content and answer search queries." : "選擇應用以索引內容並回應搜尋查詢。", "Navigation Icon" : "導覽圖示", - "Enable global search within all your content." : "在您所有的內容中啟用全域搜尋。", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "對 '{search}' 中的 {title} 搜尋於 {time} ms 內回傳了 {total} 個結果" + "Enable global search within all your content." : "在您所有的內容中啟用全域搜尋。" },"pluralForm" :"nplurals=1; plural=0;" } \ No newline at end of file diff --git a/l10n/zh_TW.js b/l10n/zh_TW.js index 9100036..0000ed9 100644 --- a/l10n/zh_TW.js +++ b/l10n/zh_TW.js @@ -16,7 +16,6 @@ OC.L10N.register( "Search Platform" : "搜尋平台", "Select the app to index content and answer search queries." : "選取應用以索引內容並回應搜尋查詢。", "Navigation Icon" : "導覽圖示", - "Enable global search within all your content." : "在您所有的內容中啟用全域搜尋。", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "對 '{search}' 中的 {title} 搜尋於 {time} 毫秒內回傳了 {total} 個結果" + "Enable global search within all your content." : "在您所有的內容中啟用全域搜尋。" }, "nplurals=1; plural=0;"); diff --git a/l10n/zh_TW.json b/l10n/zh_TW.json index 5b0bb3b..1a7585c 100644 --- a/l10n/zh_TW.json +++ b/l10n/zh_TW.json @@ -14,7 +14,6 @@ "Search Platform" : "搜尋平台", "Select the app to index content and answer search queries." : "選取應用以索引內容並回應搜尋查詢。", "Navigation Icon" : "導覽圖示", - "Enable global search within all your content." : "在您所有的內容中啟用全域搜尋。", - "the search in {title} for '{search}' returned {total} results in {time} ms" : "對 '{search}' 中的 {title} 搜尋於 {time} 毫秒內回傳了 {total} 個結果" + "Enable global search within all your content." : "在您所有的內容中啟用全域搜尋。" },"pluralForm" :"nplurals=1; plural=0;" } \ No newline at end of file diff --git a/lib/Command/CollectionDelete.php b/lib/Command/CollectionDelete.php new file mode 100644 index 0000000..98b0a23 --- /dev/null +++ b/lib/Command/CollectionDelete.php @@ -0,0 +1,89 @@ + + * @copyright 2018 + * @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 . + * + */ + + +namespace OCA\FullTextSearch\Command; + + +use OC\Core\Command\Base; +use OCA\FullTextSearch\Exceptions\CollectionArgumentException; +use OCA\FullTextSearch\Service\CollectionService; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + + +class CollectionDelete extends Base { + + + /** @var CollectionService */ + private $collectionService; + + + /** + * @param CollectionService $collectionService + */ + public function __construct(CollectionService $collectionService) { + parent::__construct(); + + $this->collectionService = $collectionService; + } + + + /** + * + */ + protected function configure() { + parent::configure(); + $this->setName('fulltextsearch:collection:delete') + ->setDescription('Delete collection') + ->addArgument('name', InputArgument::REQUIRED, 'name of the collection to delete'); + } + + + /** + * @param InputInterface $input + * @param OutputInterface $output + * + * @return int + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $collection = $input->getArgument('name'); + if (!$this->collectionService->hasCollection($collection)) { + throw new CollectionArgumentException('unknown collection'); + } + + $this->collectionService->deleteCollection($collection); + + return 0; + } +} + + + diff --git a/lib/Command/CollectionInit.php b/lib/Command/CollectionInit.php new file mode 100644 index 0000000..3f5c263 --- /dev/null +++ b/lib/Command/CollectionInit.php @@ -0,0 +1,198 @@ + + * @copyright 2018 + * @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 . + * + */ + + +namespace OCA\FullTextSearch\Command; + + +use Exception; +use OC\Core\Command\Base; +use OCA\FullTextSearch\Model\IndexOptions; +use OCA\FullTextSearch\Model\Runner; +use OCA\FullTextSearch\Service\CliService; +use OCA\FullTextSearch\Service\CollectionService; +use OCA\FullTextSearch\Service\ProviderService; +use OCA\FullTextSearch\Service\RunningService; +use OCP\FullTextSearch\IFullTextSearchProvider; +use OCP\IUserManager; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + + +class CollectionInit extends Base { + + + /** @var ProviderService */ + private $providerService; + + /** @var CollectionService */ + private $collectionService; + + /** @var IUserManager */ + private $userManager; + + /** @var RunningService */ + private $runningService; + + /** @var CliService */ + private $cliService; + + + /** + * @param IUserManager $userManager + * @param CollectionService $collectionService + * @param ProviderService $providerService + * @param RunningService $runningService + * @param CliService $cliService + */ + public function __construct( + IUserManager $userManager, + CollectionService $collectionService, + ProviderService $providerService, + RunningService $runningService, + CliService $cliService + ) { + parent::__construct(); + + $this->userManager = $userManager; + $this->collectionService = $collectionService; + $this->providerService = $providerService; + $this->runningService = $runningService; + $this->cliService = $cliService; + } + + + /** + * + */ + protected function configure() { + parent::configure(); + $this->setName('fulltextsearch:collection:init') + ->setDescription('Initiate a collection') + ->addArgument('name', InputArgument::REQUIRED, 'name of the collection'); + } + + + /** + * @param InputInterface $input + * @param OutputInterface $output + * + * @return int + * @throws Exception + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $collection = $input->getArgument('name'); + $this->collectionService->confirmCollectionString($collection); + + $runner = new Runner($this->runningService, 'commandIndex', ['nextStep' => 'n']); +// $runner->sourceIsCommandLine($this, $output); + $this->collectionService->setRunner($runner); + $this->cliService->setRunner($runner); + + $this->cliService->createPanel( + 'collection', [ + '┌─ Collection ' . $collection . ' ────', + '│ ProviderId, UserId: %providerId% / %userId%', + '│ Chunk: %chunkCurr:3s%/%chunkTotal%', + '│ Document: %documentCurr:6s%/%documentChunk%', + '│', + '│ Total Document: %documentTotal%', + '│ Index initiated: %indexCount%', + '└──' + ] + ); + + $runner->setInfoArray([ + 'providerId' => '', + 'userId' => '', + 'chunkCurr' => '', + 'chunkTotal' => '', + 'documentCurr' => '', + 'documentChunk' => '', + 'documentTotal' => '', + 'indexCount' => 0 + ]); + + $this->cliService->initDisplay(); + $this->cliService->displayPanel('run', 'collection'); + $this->cliService->runDisplay($output); + + $providers = $this->providerService->getProviders(); + foreach ($providers as $providerWrapper) { + $this->indexProvider($runner, $collection, $providerWrapper->getProvider()); + } + + + return 0; + } + + + /** + * @param string $collection + * @param IFullTextSearchProvider $provider + * + * @throws Exception + */ + private function indexProvider( + Runner $runner, + string $collection, + IFullTextSearchProvider $provider, + string $userId = '' + ) { + $runner->setInfo('providerId', $provider->getId()); + $options = new IndexOptions(); + $provider->setIndexOptions($options); + + if ($userId === '') { + $users = $this->userManager->search(''); + } else { + $users = [$userId]; + } + + foreach ($users as $user) { + if ($user === null) { + continue; + } + + $runner->setInfo('userId', $user->getUID()); + + try { + $this->collectionService->initCollectionIndexes( + $provider, + $collection, + $user->getUID(), + $options + ); + } catch (Exception $e) { + continue; + } + } + } +} diff --git a/lib/Command/CollectionList.php b/lib/Command/CollectionList.php new file mode 100644 index 0000000..c267107 --- /dev/null +++ b/lib/Command/CollectionList.php @@ -0,0 +1,83 @@ + + * @copyright 2018 + * @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 . + * + */ + + +namespace OCA\FullTextSearch\Command; + + +use OC\Core\Command\Base; +use OCA\FullTextSearch\Service\CollectionService; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + + +class CollectionList extends Base { + + + /** @var CollectionService */ + private $collectionService; + + + /** + * @param CollectionService $collectionService + */ + public function __construct(CollectionService $collectionService) { + parent::__construct(); + + $this->collectionService = $collectionService; + } + + + /** + * + */ + protected function configure() { + parent::configure(); + $this->setName('fulltextsearch:collection:list') + ->setDescription('List collections'); + } + + + /** + * @param InputInterface $input + * @param OutputInterface $output + * + * @return int + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $collections = $this->collectionService->getCollections(); + $output->writeln('found ' . sizeof($collections) . ' collection(s)'); + + foreach ($this->collectionService->getCollections() as $collection) { + $output->writeln('- ' . $collection); + } + + return 0; + } +} diff --git a/lib/Command/Index.php b/lib/Command/Index.php index 73211f2..1508769 100644 --- a/lib/Command/Index.php +++ b/lib/Command/Index.php @@ -40,6 +40,7 @@ use OCA\FullTextSearch\Model\Index as ModelIndex; use OCA\FullTextSearch\Model\IndexOptions; use OCA\FullTextSearch\Model\Runner; use OCA\FullTextSearch\Service\CliService; +use OCA\FullTextSearch\Service\ConfigService; use OCA\FullTextSearch\Service\IndexService; use OCA\FullTextSearch\Service\MiscService; use OCA\FullTextSearch\Service\PlatformService; @@ -48,7 +49,6 @@ use OCA\FullTextSearch\Service\RunningService; use OCP\FullTextSearch\IFullTextSearchProvider; use OCP\IUserManager; use OutOfBoundsException; -use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -138,6 +138,8 @@ class Index extends ACommandBase { /** @var MiscService */ private $miscService; + /** @var ConfigService */ + private $configService; /** @var Runner */ private $runner; @@ -168,11 +170,13 @@ class Index extends ACommandBase { * @param PlatformService $platformService * @param ProviderService $providerService * @param MiscService $miscService + * @param ConfigService $configService */ public function __construct( IUserManager $userManager, RunningService $runningService, CliService $cliService, IndexService $indexService, PlatformService $platformService, - ProviderService $providerService, MiscService $miscService + ProviderService $providerService, MiscService $miscService, + ConfigService $configService ) { parent::__construct(); $this->userManager = $userManager; @@ -184,6 +188,7 @@ class Index extends ACommandBase { $this->platformService = $platformService; $this->providerService = $providerService; $this->miscService = $miscService; + $this->configService = $configService; } @@ -210,6 +215,7 @@ class Index extends ACommandBase { * @throws Exception */ protected function execute(InputInterface $input, OutputInterface $output) { + $this->configService->requireMigration24(); $options = $this->generateIndexOptions($input); @@ -217,7 +223,7 @@ class Index extends ACommandBase { /** do not get stuck while waiting interactive input */ try { readline_callback_handler_install( - '', function() { + '', function () { } ); } catch (Throwable $t) { @@ -414,7 +420,7 @@ class Index extends ACommandBase { } } - $this->providerService->setProviderAsIndexed($provider, true); + $this->providerService->setProviderAsIndexed($provider->getId(), true); } @@ -572,37 +578,37 @@ class Index extends ACommandBase { // full list of info that can be edited $this->runner->setInfoArray( [ - 'userId' => '', + 'userId' => '', 'providerName' => '', - '_memory' => '', - 'documentId' => '', - 'action' => '', - 'info' => '', - 'title' => '', - '_paused' => '', + '_memory' => '', + 'documentId' => '', + 'action' => '', + 'info' => '', + 'title' => '', + '_paused' => '', - 'resultIndex' => '', - 'resultCurrent' => '', - 'resultTotal' => '', - 'resultMessageA' => '', - 'resultMessageB' => '', - 'resultMessageC' => '', - 'resultStatus' => '', + 'resultIndex' => '', + 'resultCurrent' => '', + 'resultTotal' => '', + 'resultMessageA' => '', + 'resultMessageB' => '', + 'resultMessageC' => '', + 'resultStatus' => '', 'resultStatusColored' => '', - 'content' => '', - 'statusColored' => '', - 'chunkCurrent' => '', - 'chunkTotal' => '', - 'documentCurrent' => '', - 'documentTotal' => '', - 'progressStatus' => '', - 'errorCurrent' => '0', - 'errorTotal' => '0', - 'errorMessageA' => '', - 'errorMessageB' => '', - 'errorMessageC' => '', - 'errorException' => '', - 'errorIndex' => '' + 'content' => '', + 'statusColored' => '', + 'chunkCurrent' => '', + 'chunkTotal' => '', + 'documentCurrent' => '', + 'documentTotal' => '', + 'progressStatus' => '', + 'errorCurrent' => '0', + 'errorTotal' => '0', + 'errorMessageA' => '', + 'errorMessageB' => '', + 'errorMessageC' => '', + 'errorException' => '', + 'errorIndex' => '' ] ); } @@ -618,7 +624,7 @@ class Index extends ACommandBase { $this->runner->setInfoArray( [ 'errorCurrent' => 0, - 'errorTotal' => 0, + 'errorTotal' => 0, ] ); @@ -648,13 +654,13 @@ class Index extends ACommandBase { $this->runner->setInfoArray( [ - 'errorCurrent' => $current, - 'errorTotal' => $total, - 'errorMessageA' => trim($err1), - 'errorMessageB' => trim($err2), - 'errorMessageC' => trim($err3), + 'errorCurrent' => $current, + 'errorTotal' => $total, + 'errorMessageA' => trim($err1), + 'errorMessageB' => trim($err2), + 'errorMessageC' => trim($err3), 'errorException' => $this->get('exception', $error, ''), - 'errorIndex' => $errorIndex + 'errorIndex' => $errorIndex ] ); } @@ -670,7 +676,7 @@ class Index extends ACommandBase { $this->runner->setInfoArray( [ 'resultCurrent' => 0, - 'resultTotal' => 0, + 'resultTotal' => 0, ] ); @@ -704,13 +710,13 @@ class Index extends ACommandBase { $this->runner->setInfoArray( [ - 'resultCurrent' => $current, - 'resultTotal' => $total, + 'resultCurrent' => $current, + 'resultTotal' => $total, 'resultMessageA' => trim($msg1), 'resultMessageB' => trim($msg2), 'resultMessageC' => trim($msg3), - 'resultStatus' => $status, - 'resultIndex' => $resultIndex + 'resultStatus' => $status, + 'resultIndex' => $resultIndex ] ); $this->runner->setInfoColored('resultStatus', $type); @@ -808,10 +814,10 @@ class Index extends ACommandBase { foreach ($indexes as $index) { foreach ($index->getErrors() as $error) { $this->errors[] = [ - 'index' => $index, - 'message' => $error['message'], + 'index' => $index, + 'message' => $error['message'], 'exception' => $error['exception'], - 'severity' => $error['severity'] + 'severity' => $error['severity'] ]; } @@ -830,11 +836,11 @@ class Index extends ACommandBase { $this->runner->setInfoArray( [ - 'errorMessageA' => '', - 'errorMessageB' => '', - 'errorMessageC' => '', + 'errorMessageA' => '', + 'errorMessageB' => '', + 'errorMessageC' => '', 'errorException' => '', - 'errorIndex' => '' + 'errorIndex' => '' ] ); diff --git a/lib/Command/Migration24.php b/lib/Command/Migration24.php new file mode 100644 index 0000000..0d9a899 --- /dev/null +++ b/lib/Command/Migration24.php @@ -0,0 +1,135 @@ + + * @copyright 2018 + * @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 . + * + */ + + +namespace OCA\FullTextSearch\Command; + + +use OC\Core\Command\Base; +use OCA\FullTextSearch\Exceptions\DatabaseException; +use OCA\FullTextSearch\Service\ConfigService; +use OCA\FullTextSearch\Service\MigrationService; +use OCP\DB\Exception; +use OCP\IDBConnection; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + + +class Migration24 extends Base { + + + /** @var IDBConnection */ + private $dbConnection; + + /** @var MigrationService */ + private $migrationService; + + /** @var ConfigService */ + private $configService; + + + /** + * @param IDBConnection $dbConnection + * @param MigrationService $migrationService + * @param ConfigService $configService + */ + public function __construct( + IDBConnection $dbConnection, + MigrationService $migrationService, + ConfigService $configService + ) { + parent::__construct(); + + $this->dbConnection = $dbConnection; + $this->migrationService = $migrationService; + $this->configService = $configService; + } + + + /** + * + */ + protected function configure() { + parent::configure(); + $this->setName('fulltextsearch:migration:24') + ->setDescription('Migrate index for NC24'); + } + + + /** + * @param InputInterface $input + * @param OutputInterface $output + * + * @return int + * @throws \Exception + */ + protected function execute(InputInterface $input, OutputInterface $output): int { + try { + $this->configService->requireMigration24(); + throw new \Exception('Should not be required'); + } catch (DatabaseException $e) { + } + + $this->runMigration($output); + + return 0; + } + + + private function runMigration(OutputInterface $output): void { + $progressBar = new ProgressBar($output, $this->getTotalRows()); + $this->migrationService->setProgressBar($progressBar); + while (true) { + if (!$this->migrationService->migrate24Chunk(10000, $progressBar)) { + break; + } + } + + $progressBar->finish(); + $output->writeln(''); + } + + + /** + * @return int + * @throws Exception + */ + private function getTotalRows(): int { + $query = $this->dbConnection->getQueryBuilder(); + $query->select($query->func()->count('*', 'index_count')) + ->from('fulltextsearch_indexes'); + $result = $query->executeQuery(); + $row = $result->fetch(); + $result->closeCursor(); + + return (int)$row['index_count']; + } + +} diff --git a/lib/Command/Reset.php b/lib/Command/Reset.php index c5aa80e..a1e80ab 100644 --- a/lib/Command/Reset.php +++ b/lib/Command/Reset.php @@ -87,7 +87,8 @@ class Reset extends ACommandBase { parent::configure(); $this->setName('fulltextsearch:reset') ->setDescription('Reset index') - ->addArgument('provider', InputArgument::OPTIONAL, 'provider'); + ->addArgument('provider', InputArgument::OPTIONAL, 'provider id', '') + ->addArgument('collection', InputArgument::OPTIONAL, 'name of the collection', ''); } @@ -111,7 +112,10 @@ class Reset extends ACommandBase { $this->indexService->setRunner($this->runner); try { - $this->indexService->resetIndex($this->getProviderIdFromArgument($input)); + $this->indexService->resetIndex( + $input->getArgument('provider'), + $input->getArgument('collection') + ); } catch (Exception $e) { throw $e; @@ -123,21 +127,6 @@ class Reset extends ACommandBase { } - /** - * @param InputInterface $input - * - * @return string - */ - private function getProviderIdFromArgument(InputInterface $input): string { - $providerId = $input->getArgument('provider'); - if ($providerId === null) { - $providerId = ''; - } - - return $providerId; - } - - /** * @throws TickDoesNotExistException */ diff --git a/lib/Controller/CollectionController.php b/lib/Controller/CollectionController.php new file mode 100644 index 0000000..26f7f1a --- /dev/null +++ b/lib/Controller/CollectionController.php @@ -0,0 +1,164 @@ + + * @copyright 2022 + * @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 . + * + */ + + +namespace OCA\FullTextSearch\Controller; + + +use OCA\FullTextSearch\AppInfo\Application; +use OCA\FullTextSearch\Service\CollectionService; +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\OCS\OCSException; +use OCP\AppFramework\OCSController; +use OCP\FullTextSearch\Model\IIndexDocument; +use OCP\IRequest; + + +class CollectionController extends OCSController { + + + /** @var CollectionService */ + private $collectionService; + + + /** + * @param IRequest $request + * @param CollectionService $collectionService + */ + public function __construct( + IRequest $request, + CollectionService $collectionService + ) { + parent::__construct(Application::APP_ID, $request); + + $this->collectionService = $collectionService; + } + + + /** + * @param string $collection + * @param int $length + * + * @return DataResponse + * @throws OCSException + */ + public function getQueue(string $collection, int $length = 0): DataResponse { + try { + $this->collectionService->confirmCollection($collection); + + return new DataResponse($this->collectionService->getQueue($collection, $length)); + } catch (\Exception $e) { +// $this->e($e, ['circleId' => $circleId]); + throw new OCSException($e->getMessage(), $e->getCode()); + } + } + + + /** + * @param string $collection + * @param string $providerId + * @param string $documentId + * + * @return DataResponse + * @throws OCSException + */ + public function indexDocument(string $collection, string $providerId, string $documentId): DataResponse { + try { + $this->collectionService->confirmCollection($collection); + $document = $this->collectionService->getDocument( + $collection, + $providerId, + $documentId + ); + + return new DataResponse($this->displayDocument($document)); + } catch (\Exception $e) { + throw new OCSException($e->getMessage(), $e->getCode()); + } + } + + + /** + * @param string $collection + * @param string $providerId + * @param string $documentId + * + * @return DataResponse + * @throws OCSException + */ + public function updateStatusDone( + string $collection, + string $providerId, + string $documentId + ): DataResponse { + try { + $this->collectionService->confirmCollection($collection); + $this->collectionService->setAsDone($collection, $providerId, $documentId); + + return new DataResponse([]); + } catch (\Exception $e) { + throw new OCSException($e->getMessage(), $e->getCode()); + } + } + + + /** + * @param IIndexDocument $document + * + * @return array + */ + private function displayDocument(IIndexDocument $document): array { + $display = [ + 'id' => $document->getId(), + 'providerId' => $document->getProviderId(), + 'access' => $document->getAccess(), + 'index' => $document->getIndex(), + 'title' => $document->getTitle(), + 'link' => $document->getLink(), + 'parts' => $document->getParts(), + 'tags' => $document->getTags(), + 'metatags' => $document->getMetaTags(), + 'source' => $document->getSource(), + 'info' => $document->getInfoAll(), + 'hash' => $document->getHash(), + 'modifiedTime' => $document->getModifiedTime() + ]; + + foreach ($document->getMore() as $k => $v) { + $display[$k] = $v; + } + + $display['content'] = $document->getContent(); + $display['isContentEncoded'] = $document->isContentEncoded(); + + return json_decode(json_encode($display), true); + } + +} + diff --git a/lib/Cron/Index.php b/lib/Cron/Index.php index 8374583..49374fe 100644 --- a/lib/Cron/Index.php +++ b/lib/Cron/Index.php @@ -119,7 +119,7 @@ class Index extends TimedJob { $platform = $wrapper->getPlatform(); $all = $this->shouldWeGetAllIndex(); - $indexes = $this->indexService->getQueuedIndexes($all); + $indexes = $this->indexService->getQueuedIndexes('', $all); foreach ($indexes as $index) { $this->runner->updateAction('indexing'); diff --git a/lib/Cron/Maintenance.php b/lib/Cron/Maintenance.php new file mode 100644 index 0000000..5a2d505 --- /dev/null +++ b/lib/Cron/Maintenance.php @@ -0,0 +1,74 @@ + + * @copyright 2022 + * @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 . + * + */ + + +namespace OCA\FullTextSearch\Cron; + + +use OC\BackgroundJob\TimedJob; +use OCA\FullTextSearch\Service\ConfigService; +use OCA\FullTextSearch\Service\MigrationService; + + +class Maintenance extends TimedJob { + + + /** @var MigrationService */ + private $migrationService; + + /** @var ConfigService */ + private $configService; + + + /** + * + */ + public function __construct(MigrationService $migrationService, ConfigService $configService) { + $this->setInterval(3600); + + $this->migrationService = $migrationService; + $this->configService = $configService; + } + + + /** + * @param mixed $argument + * + * @throws \OCP\DB\Exception + */ + protected function run($argument) { + $size = $this->configService->getAppValue('size_migration_24'); + if ($size === '') { + $size = 10000; + } + + $this->migrationService->migrate24Chunk((int)$size); + } + +} diff --git a/lib/Db/CoreRequestBuilder.php b/lib/Db/CoreRequestBuilder.php index d90054c..47018cc 100644 --- a/lib/Db/CoreRequestBuilder.php +++ b/lib/Db/CoreRequestBuilder.php @@ -47,7 +47,7 @@ use OCP\IL10N; */ class CoreRequestBuilder { - const TABLE_INDEXES = 'fulltextsearch_indexes'; + const TABLE_INDEXES = 'fulltextsearch_index'; const TABLE_TICKS = 'fulltextsearch_ticks'; /** @var IDBConnection */ @@ -129,6 +129,17 @@ class CoreRequestBuilder { } + /** + * Limit to the documentId + * + * @param IQueryBuilder $qb + * @param string $collection + */ + protected function limitToCollection(IQueryBuilder &$qb, string $collection) { + $this->limitToDBField($qb, 'collection', $collection); + } + + /** * Limit to the entry with at least one Error * diff --git a/lib/Db/IndexesRequest.php b/lib/Db/IndexesRequest.php index f0c7ab9..99745d9 100644 --- a/lib/Db/IndexesRequest.php +++ b/lib/Db/IndexesRequest.php @@ -31,9 +31,11 @@ declare(strict_types=1); namespace OCA\FullTextSearch\Db; +use OCA\FullTextSearch\Exceptions\DatabaseException; use OCA\FullTextSearch\Exceptions\IndexDoesNotExistException; use OCA\FullTextSearch\Model\Index; use OCP\FullTextSearch\Model\IIndex; +use OCP\IDBConnection; /** @@ -53,6 +55,7 @@ class IndexesRequest extends IndexesRequestBuilder { $qb = $this->getIndexesInsertSql(); $qb->setValue('owner_id', $qb->createNamedParameter($index->getOwnerId())) ->setValue('provider_id', $qb->createNamedParameter($index->getProviderId())) + ->setValue('collection', $qb->createNamedParameter($index->getCollection())) ->setValue('document_id', $qb->createNamedParameter($index->getDocumentId())) ->setValue('source', $qb->createNamedParameter($index->getSource())) ->setValue('err', $qb->createNamedParameter($index->getErrorCount())) @@ -73,9 +76,8 @@ class IndexesRequest extends IndexesRequestBuilder { * @return bool */ public function resetError(Index $index): bool { - try { - $this->getIndex($index->getProviderId(), $index->getDocumentId()); + $this->getIndex($index->getProviderId(), $index->getDocumentId(), $index->getCollection()); } catch (IndexDoesNotExistException $e) { return false; } @@ -86,7 +88,7 @@ class IndexesRequest extends IndexesRequestBuilder { $this->limitToProviderId($qb, $index->getProviderId()); $this->limitToDocumentId($qb, $index->getDocumentId()); - + $this->limitToCollection($qb, $index->getCollection()); $qb->execute(); return true; @@ -109,7 +111,6 @@ class IndexesRequest extends IndexesRequestBuilder { * @return Index[] */ public function getErrorIndexes(): array { - $qb = $this->getIndexesSelectSql(); $this->limitToErr($qb); @@ -129,36 +130,32 @@ class IndexesRequest extends IndexesRequestBuilder { * * @return bool */ - public function update(Index $index): bool { - - try { - $this->getIndex($index->getProviderId(), $index->getDocumentId()); - } catch (IndexDoesNotExistException $e) { - return false; - } - + public function update(Index $index, bool $statusOnly = false): bool { $qb = $this->getIndexesUpdateSql(); $qb->set('status', $qb->createNamedParameter($index->getStatus())); - $qb->set('source', $qb->createNamedParameter($index->getSource())); - $qb->set('options', $qb->createNamedParameter(json_encode($index->getOptions()))); - if ($index->getOwnerId() !== '') { - $qb->set('owner_id', $qb->createNamedParameter($index->getOwnerId())); + if (!$statusOnly) { + $qb->set('source', $qb->createNamedParameter($index->getSource())); + + $qb->set('options', $qb->createNamedParameter(json_encode($index->getOptions()))); + + if ($index->getOwnerId() !== '') { + $qb->set('owner_id', $qb->createNamedParameter($index->getOwnerId())); + } + + if ($index->getLastIndex() > 0) { + $qb->set('indexed', $qb->createNamedParameter($index->getLastIndex())); + } + + $qb->set('message', $qb->createNamedParameter(json_encode($index->getErrors()))); + $qb->set('err', $qb->createNamedParameter($index->getErrorCount())); } - if ($index->getLastIndex() > 0) { - $qb->set('indexed', $qb->createNamedParameter($index->getLastIndex())); - } - - $qb->set('message', $qb->createNamedParameter(json_encode($index->getErrors()))); - $qb->set('err', $qb->createNamedParameter($index->getErrorCount())); - $this->limitToProviderId($qb, $index->getProviderId()); $this->limitToDocumentId($qb, $index->getDocumentId()); + $this->limitToCollection($qb, $index->getCollection()); - $qb->execute(); - - return true; + return ($qb->executeStatement() === 1); } @@ -167,27 +164,30 @@ class IndexesRequest extends IndexesRequestBuilder { * @param string $documentId * @param int $status */ - public function updateStatus(string $providerId, string $documentId, int $status) { + public function updateStatus(string $collection, string $providerId, string $documentId, int $status) { $qb = $this->getIndexesUpdateSql(); $qb->set('status', $qb->createNamedParameter($status)); $this->limitToProviderId($qb, $providerId); $this->limitToDocumentId($qb, $documentId); + $this->limitToCollection($qb, $collection); $qb->execute(); } /** + * @param string $collection * @param string $providerId * @param array $indexes * @param int $status */ - public function updateStatuses(string $providerId, array $indexes, int $status) { + public function updateStatuses(string $collection, string $providerId, array $indexes, int $status) { $qb = $this->getIndexesUpdateSql(); $qb->set('status', $qb->createNamedParameter($status)); $this->limitToProviderId($qb, $providerId); $this->limitToDocumentIds($qb, $indexes); + $this->limitToCollection($qb, $collection); $qb->execute(); } @@ -200,8 +200,20 @@ class IndexesRequest extends IndexesRequestBuilder { $qb = $this->getIndexesDeleteSql(); $this->limitToProviderId($qb, $index->getProviderId()); $this->limitToDocumentId($qb, $index->getDocumentId()); + $this->limitToCollection($qb, $index->getCollection()); - $qb->execute(); + $qb->executeStatement(); + } + + + /** + * @param string $collection \ + */ + public function deleteCollection(string $collection): void { + $qb = $this->getIndexesDeleteSql(); + $this->limitToCollection($qb, $collection); + + $qb->executeStatement(); } @@ -219,10 +231,11 @@ class IndexesRequest extends IndexesRequestBuilder { /** * */ - public function reset() { + public function reset(string $collection = ''): void { $qb = $this->getIndexesDeleteSql(); + $this->limitToCollection($qb, $collection); - $qb->execute(); + $qb->executeStatement(); } @@ -235,10 +248,11 @@ class IndexesRequest extends IndexesRequestBuilder { * @return Index * @throws IndexDoesNotExistException */ - public function getIndex(string $providerId, string $documentId): Index { + public function getIndex(string $providerId, string $documentId, string $collection = ''): Index { $qb = $this->getIndexesSelectSql(); $this->limitToProviderId($qb, $providerId); $this->limitToDocumentId($qb, $documentId); + $this->limitToCollection($qb, $collection); $cursor = $qb->execute(); $data = $cursor->fetch(); @@ -256,15 +270,14 @@ class IndexesRequest extends IndexesRequestBuilder { * return index. * * @param string $providerId - * @param array $documentIds + * @param string $documentId * * @return Index[] - * @throws IndexDoesNotExistException */ - public function getIndexes(string $providerId, array $documentIds): array { + public function getIndexes(string $providerId, string $documentId): array { $qb = $this->getIndexesSelectSql(); $this->limitToProviderId($qb, $providerId); - $this->limitToDocumentIds($qb, $documentIds); + $this->limitToDocumentId($qb, $documentId); $indexes = []; $cursor = $qb->execute(); @@ -273,10 +286,6 @@ class IndexesRequest extends IndexesRequestBuilder { } $cursor->closeCursor(); - if (sizeof($indexes) === 0) { - throw new IndexDoesNotExistException($this->l10n->t('Index not found')); - } - return $indexes; } @@ -286,13 +295,18 @@ class IndexesRequest extends IndexesRequestBuilder { * * @return Index[] */ - public function getQueuedIndexes(bool $all = false): array { + public function getQueuedIndexes(string $collection = '', bool $all = false, int $length = 0): array { $qb = $this->getIndexesSelectSql(); $this->limitToQueuedIndexes($qb); if ($all === false) { $this->limitToNoErr($qb); } + $this->limitToCollection($qb, $collection); + if ($length > 0) { + $qb->setMaxResults($length); + } + $indexes = []; $cursor = $qb->execute(); while ($data = $cursor->fetch()) { @@ -327,4 +341,69 @@ class IndexesRequest extends IndexesRequestBuilder { } + /** + * @return string[] + */ + public function getCollections(): array { + $qb = $this->dbConnection->getQueryBuilder(); + + $qb->select('li.collection') + ->from(self::TABLE_INDEXES, 'li'); + $qb->andWhere($qb->expr()->nonEmptyString('li.collection')); + $qb->groupBy('li.collection'); + + $collections = []; + $cursor = $qb->executeQuery(); + while ($data = $cursor->fetch()) { + $collections[] = $this->get('collection', $data); + } + $cursor->closeCursor(); + + return array_merge([''], $collections); + } + + + /** + * TODO: remove this in NC30+ + * + * @param string $providerId + * @param string $documentId + */ + public function migrateIndex24(string $providerId, string $documentId): array { + try { + $this->configService->requireMigration24(); + + return []; + } catch (DatabaseException $e) { + } + + // check data from old table. + /** @var IDBConnection $dbConnection */ + $dbConnection = \OC::$server->get(IDBConnection::class); + + $query = $dbConnection->getQueryBuilder(); + $query->select('*') + ->from('fulltextsearch_indexes') + ->where($query->expr()->eq('provider_id', $query->createNamedParameter($providerId))) + ->andWhere($query->expr()->eq('document_id', $query->createNamedParameter($documentId))); + + $result = $query->executeQuery(); + if ($result->rowCount() === 0) { + return []; + } + + $data = $result->fetch(); + $index = $this->parseIndexesSelectSql($data); + $result->closeCursor(); + + $query = $dbConnection->getQueryBuilder(); + $query->delete('fulltextsearch_indexes') + ->where($query->expr()->eq('provider_id', $query->createNamedParameter($providerId))) + ->andWhere($query->expr()->eq('document_id', $query->createNamedParameter($documentId))); + $query->executeStatement(); + + $this->create($index); + + return $this->getIndexes($providerId, $documentId); + } } diff --git a/lib/Db/IndexesRequestBuilder.php b/lib/Db/IndexesRequestBuilder.php index d81df09..7634ec6 100644 --- a/lib/Db/IndexesRequestBuilder.php +++ b/lib/Db/IndexesRequestBuilder.php @@ -83,8 +83,8 @@ class IndexesRequestBuilder extends CoreRequestBuilder { /** @noinspection PhpMethodParametersCountMismatchInspection */ $qb->select( - 'li.owner_id', 'li.provider_id', 'li.document_id', 'li.source', 'li.status', - 'li.options', 'li.err', 'li.message', 'li.indexed' + 'li.owner_id', 'li.provider_id', 'li.document_id', 'li.collection', 'li.source', + 'li.status', 'li.options', 'li.err', 'li.message', 'li.indexed' ) ->from(self::TABLE_INDEXES, 'li'); @@ -119,7 +119,8 @@ class IndexesRequestBuilder extends CoreRequestBuilder { $index->setStatus($this->getInt('status', $data)) ->setSource($this->get('source', $data, '')) ->setOwnerId($this->get('owner_id', $data, '')) - ->setLastIndex($this->getInt('indexed', $data, 0)); + ->setLastIndex($this->getInt('indexed', $data, 0)) + ->setCollection($this->get('collection', $data)); $index->setOptions($this->getArray('options', $data, [])); $index->setErrorCount($this->getInt('err', $data, 0)); $index->setErrors($this->getArray('message', $data, [])); diff --git a/lib/Exceptions/CollectionArgumentException.php b/lib/Exceptions/CollectionArgumentException.php new file mode 100644 index 0000000..eb05978 --- /dev/null +++ b/lib/Exceptions/CollectionArgumentException.php @@ -0,0 +1,40 @@ + + * @copyright 2018 + * @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 . + * + */ + + +namespace OCA\FullTextSearch\Exceptions; + + +use Exception; + + +class CollectionArgumentException extends Exception { + +} + diff --git a/lib/Migration/Version2000Date20201208130255.php b/lib/Migration/Version2000Date20201208130255.php index a36f9b5..dc6d710 100644 --- a/lib/Migration/Version2000Date20201208130255.php +++ b/lib/Migration/Version2000Date20201208130255.php @@ -32,47 +32,6 @@ class Version2000Date20201208130255 extends SimpleMigrationStep { /** @var ISchemaWrapper $schema */ $schema = $schemaClosure(); - if (!$schema->hasTable('fulltextsearch_indexes')) { - $table = $schema->createTable('fulltextsearch_indexes'); - $table->addColumn('provider_id', 'string', [ - 'notnull' => true, - 'length' => 255, - ]); - $table->addColumn('document_id', 'string', [ - 'notnull' => true, - 'length' => 254, - ]); - $table->addColumn('source', 'string', [ - 'notnull' => false, - 'length' => 64, - ]); - $table->addColumn('owner_id', 'string', [ - 'notnull' => true, - 'length' => 64, - ]); - $table->addColumn('status', 'smallint', [ - 'notnull' => true, - 'length' => 1, - ]); - $table->addColumn('options', 'string', [ - 'notnull' => false, - 'length' => 511, - ]); - $table->addColumn('err', 'smallint', [ - 'notnull' => true, - 'length' => 1, - ]); - $table->addColumn('message', 'string', [ - 'notnull' => false, - 'length' => 8000, - ]); - $table->addColumn('indexed', 'bigint', [ - 'notnull' => false, - 'length' => 6, - ]); - $table->setPrimaryKey(['provider_id', 'document_id']); - } - if (!$schema->hasTable('fulltextsearch_ticks')) { $table = $schema->createTable('fulltextsearch_ticks'); $table->addColumn('id', 'bigint', [ diff --git a/lib/Migration/Version23001Date20220408140253.php b/lib/Migration/Version23001Date20220408140253.php new file mode 100644 index 0000000..ec7d6fe --- /dev/null +++ b/lib/Migration/Version23001Date20220408140253.php @@ -0,0 +1,57 @@ + + * + * @author Joas Schilling + * + * @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 . + * + */ + +namespace OCA\FullTextSearch\Migration; + +use Closure; +use Doctrine\DBAL\Types\Type; +use OCP\DB\ISchemaWrapper; +use OCP\DB\Types; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +class Version23001Date20220408140253 extends SimpleMigrationStep { + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + $table = $schema->getTable('fulltextsearch_indexes'); + $column = $table->getColumn('message'); + + if ($column->getType()->getName() === Types::TEXT) { + return null; + } + + $column->setType(Type::getType(Types::TEXT)); + return $schema; + } +} diff --git a/lib/Migration/Version2400Date202201301329.php b/lib/Migration/Version2400Date202201301329.php new file mode 100644 index 0000000..0290be5 --- /dev/null +++ b/lib/Migration/Version2400Date202201301329.php @@ -0,0 +1,162 @@ +dbConnection = $dbConnection; + $this->configService = $configService; + } + + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + */ + public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { + } + + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + if (!$schema->hasTable('fulltextsearch_index')) { + $table = $schema->createTable('fulltextsearch_index'); + $table->addColumn( + 'provider_id', 'string', + [ + 'notnull' => true, + 'length' => 254, + ] + ); + $table->addColumn( + 'document_id', 'string', + [ + 'notnull' => true, + 'length' => 254, + ] + ); + $table->addColumn( + 'collection', 'string', + [ + 'default' => '', + 'notnull' => false, + 'length' => 31 + ] + ); + $table->addColumn( + 'source', 'string', + [ + 'notnull' => false, + 'length' => 64, + ] + ); + $table->addColumn( + 'owner_id', 'string', + [ + 'notnull' => true, + 'length' => 64, + ] + ); + $table->addColumn( + 'status', 'smallint', + [ + 'notnull' => true, + 'length' => 1, + ] + ); + $table->addColumn( + 'options', 'string', + [ + 'notnull' => false, + 'length' => 511, + ] + ); + $table->addColumn( + 'err', 'smallint', + [ + 'notnull' => true, + 'length' => 1, + ] + ); + $table->addColumn( + 'message', 'text', + [ + 'notnull' => false, + ] + ); + $table->addColumn( + 'indexed', 'bigint', + [ + 'notnull' => false, + 'length' => 6, + 'unsigned' => true + ] + ); + $table->setPrimaryKey(['provider_id', 'document_id', 'collection']); + $table->addIndex(['collection']); + $table->addIndex(['collection', 'provider_id', 'document_id', 'status'], 'cpds'); + } + + return $schema; + } + + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * + * @throws Exception + */ + public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + if (!$schema->hasTable('fulltextsearch_indexes')) { + return; + } + + $query = $this->dbConnection->getQueryBuilder(); + $query->select($query->func()->count('*', 'index_count')) + ->from('fulltextsearch_indexes'); + $result = $query->executeQuery(); + $row = $result->fetch(); + $result->closeCursor(); + + if ((int)$row['index_count'] > 0) { + $this->configService->setAppValue(ConfigService::MIGRATION_24, '0'); + } + } +} diff --git a/lib/Model/Index.php b/lib/Model/Index.php index a324fff..31d74ec 100644 --- a/lib/Model/Index.php +++ b/lib/Model/Index.php @@ -34,6 +34,7 @@ namespace OCA\FullTextSearch\Model; use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools; use JsonSerializable; use OCP\FullTextSearch\Model\IIndex; +use OCP\IURLGenerator; /** @@ -53,6 +54,9 @@ class Index implements IIndex, JsonSerializable { /** @var string */ private $documentId; + /** @var string */ + private $collection; + /** @var string */ private $source = ''; @@ -80,10 +84,12 @@ class Index implements IIndex, JsonSerializable { * * @param string $providerId * @param string $documentId + * @param string $collection */ - public function __construct(string $providerId, string $documentId) { + public function __construct(string $providerId, string $documentId, string $collection = '') { $this->providerId = $providerId; $this->documentId = $documentId; + $this->collection = $collection; } @@ -140,6 +146,25 @@ class Index implements IIndex, JsonSerializable { } + /** + * @param string $collection + * + * @return Index + */ + public function setCollection(string $collection): self { + $this->collection = $collection; + + return $this; + } + + /** + * @return string + */ + public function getCollection(): string { + return $this->collection; + } + + /** * @param int $status * @param bool $reset @@ -318,9 +343,9 @@ class Index implements IIndex, JsonSerializable { public function addError(string $message, string $exception = '', int $sev = IIndex::ERROR_SEV_3 ): IIndex { $this->errors[] = [ - 'message' => substr($message, 0, 1800), + 'message' => substr($message, 0, 1800), 'exception' => $exception, - 'severity' => $sev + 'severity' => $sev ]; $this->err++; @@ -352,20 +377,39 @@ class Index implements IIndex, JsonSerializable { } + /** + * @param IURLGenerator $urlGenerator + * + * @return array{url: string, status: int} + */ + public function asSitemap(IURLGenerator $urlGenerator): array { + return [ + 'url' => $urlGenerator->linkToOCSRouteAbsolute('fulltextsearch.Collection.indexDocument', + [ + 'collection' => $this->getCollection(), + 'providerId' => $this->getProviderId(), + 'documentId' => $this->getDocumentId() + ] + ), + 'status' => $this->getStatus() + ]; + } + /** * @return array */ public function jsonSerialize(): array { return [ - 'ownerId' => $this->getOwnerId(), + 'ownerId' => $this->getOwnerId(), 'providerId' => $this->getProviderId(), - 'source' => $this->getSource(), + 'collection' => $this->getCollection(), + 'source' => $this->getSource(), 'documentId' => $this->getDocumentId(), - 'lastIndex' => $this->getLastIndex(), - 'errors' => $this->getErrors(), + 'lastIndex' => $this->getLastIndex(), + 'errors' => $this->getErrors(), 'errorCount' => $this->getErrorCount(), - 'status' => (int)$this->getStatus(), - 'options' => $this->getOptions() + 'status' => (int)$this->getStatus(), + 'options' => $this->getOptions() ]; } diff --git a/lib/Model/Runner.php b/lib/Model/Runner.php index f09075e..0861861 100644 --- a/lib/Model/Runner.php +++ b/lib/Model/Runner.php @@ -210,6 +210,15 @@ class Runner implements IRunner { $this->infoUpdated(); } + /** + * @param string $info + * @param int $value + */ + public function setInfoInt(string $info, int $value): void { + $this->info[$info] = $value; + $this->infoUpdated(); + } + /** * @param array $data */ @@ -271,6 +280,14 @@ class Runner implements IRunner { return $this->get($info, $this->info, ''); } + /** + * @param string $info + * + * @return int + */ + public function getInfoInt(string $info): int { + return $this->getInt($info, $this->info); + } /** * @param array $method @@ -323,10 +340,10 @@ class Runner implements IRunner { public function newIndexError(IIndex $index, string $message, string $class = '', int $sev = 3 ) { $error = [ - 'index' => $index, - 'message' => $message, + 'index' => $index, + 'message' => $message, 'exception' => $class, - 'severity' => $sev + 'severity' => $sev ]; foreach ($this->methodOnIndexError as $method) { @@ -351,10 +368,10 @@ class Runner implements IRunner { */ public function newIndexResult(IIndex $index, string $message, string $status, int $type) { $result = [ - 'index' => $index, + 'index' => $index, 'message' => $message, - 'status' => $status, - 'type' => $type + 'status' => $status, + 'type' => $type ]; foreach ($this->methodOnIndexResult as $method) { diff --git a/lib/Service/CliService.php b/lib/Service/CliService.php index 6e08977..3b16977 100644 --- a/lib/Service/CliService.php +++ b/lib/Service/CliService.php @@ -170,7 +170,7 @@ class CliService { $initVar = $this->runner->getInfoAll(); $keys = array_keys($initVar); foreach ($keys as $key) { - $this->display->setMessage($initVar[$key], $key); + $this->display->setMessage((string)$initVar[$key], (string)$key); } $this->display->clear(); @@ -225,7 +225,7 @@ class CliService { $keys = array_keys($info); foreach ($keys as $k) { - $this->display->setMessage((string)$info[$k], $k); + $this->display->setMessage((string)$info[$k], (string)$k); } $this->refreshInfo(); diff --git a/lib/Service/CollectionService.php b/lib/Service/CollectionService.php new file mode 100644 index 0000000..4f18afb --- /dev/null +++ b/lib/Service/CollectionService.php @@ -0,0 +1,285 @@ + + * @copyright 2022 + * @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 . + * + */ + + +namespace OCA\FullTextSearch\Service; + + +use OCA\FullTextSearch\Db\IndexesRequest; +use OCA\FullTextSearch\Exceptions\CollectionArgumentException; +use OCA\FullTextSearch\Exceptions\IndexDoesNotExistException; +use OCA\FullTextSearch\Exceptions\ProviderDoesNotExistException; +use OCA\FullTextSearch\Model\Index; +use OCA\FullTextSearch\Model\IndexOptions; +use OCA\FullTextSearch\Model\Runner; +use OCP\FullTextSearch\IFullTextSearchProvider; +use OCP\FullTextSearch\Model\IIndex; +use OCP\FullTextSearch\Model\IIndexDocument; +use OCP\IURLGenerator; + + +class CollectionService { + + + /** @var IURLGenerator */ + private $urlGenerator; + + /** @var IndexesRequest */ + private $indexesRequest; + + /** @var ProviderService */ + private $providerService; + + /** @var IndexService */ + private $indexService; + + /** @var ConfigService */ + private $configService; + + + /** @var Runner */ + private $runner; + + + /** + * @param IURLGenerator $urlGenerator + * @param IndexesRequest $indexesRequest + * @param ProviderService $providerService + * @param IndexService $indexService + * @param ConfigService $configService + */ + public function __construct( + IURLGenerator $urlGenerator, + IndexesRequest $indexesRequest, + ProviderService $providerService, + IndexService $indexService, + ConfigService $configService + ) { + $this->urlGenerator = $urlGenerator; + $this->indexesRequest = $indexesRequest; + $this->providerService = $providerService; + $this->indexService = $indexService; + $this->configService = $configService; + } + + + public function setRunner(Runner $runner): void { + $this->runner = $runner; + } + + + /** + * @param string $collection + */ + public function deleteCollection(string $collection): void { + $this->indexesRequest->deleteCollection($collection); + } + + + /** + * @param string $collection + * + * @return bool + */ + public function hasCollection(string $collection): bool { + foreach ($this->getCollections() as $item) { + if ($item === $collection) { + return true; + } + } + + return false; + } + + /** + * @return string[] + */ + public function getCollections(): array { + return array_filter($this->indexesRequest->getCollections()); + } + + + /** + * @param string $collection + * @param int $length + * + * @return array + */ + public function getQueue(string $collection, int $length = 0): array { + if ($length === 0) { + $length = $this->configService->getAppValueInt(ConfigService::COLLECTION_INDEXING_LIST); + } + + return array_map( + function (Index $index): array { + return $index->asSitemap($this->urlGenerator); + }, + $this->indexesRequest->getQueuedIndexes($collection, false, $length) + ); + } + + + /** + * @param string $collection + * @param string $providerId + * @param string $documentId + * + * @throws IndexDoesNotExistException + */ + public function setAsDone(string $collection, string $providerId, string $documentId): void { + $index = $this->indexesRequest->getIndex($providerId, $documentId, $collection); + $index->setStatus(IIndex::INDEX_DONE); + $this->indexService->updateIndex($index); + } + + + /** + * @param IFullTextSearchProvider $provider + * @param string $collection + * @param string $userId + * @param IndexOptions $options + */ + public function initCollectionIndexes( + IFullTextSearchProvider $provider, + string $collection, + string $userId, + IndexOptions $options + ) { + $chunks = $provider->generateChunks($userId); + if (empty($chunks)) { + $chunks = [$userId]; + } + + $this->updateRunnerInfo('chunkTotal', (string)sizeof($chunks)); + $chunkCount = 0; + foreach ($chunks as $chunk) { + $documents = $provider->generateIndexableDocuments($userId, (string)$chunk); + $this->updateRunnerInfoArray( + [ + 'chunkCurr' => ++$chunkCount, + 'documentChunk' => sizeof($documents) + ] + ); + + $documentCount = 0; + foreach ($documents as $document) { + $this->updateRunnerInfo('documentCurr', (string)++$documentCount); + $curr = $this->runner->getInfoInt('documentTotal'); + $this->runner->setInfoInt('documentTotal', ++$curr); + + try { + $this->indexesRequest->getIndex( + $document->getProviderId(), + $document->getId(), + $collection + ); + } catch (IndexDoesNotExistException $e) { + $index = new Index($document->getProviderId(), $document->getId(), $collection); + $index->setStatus(IIndex::INDEX_FULL); + $index->setOwnerId($document->getAccess()->getOwnerId()); + $index->setSource($document->getSource()); + $index->setLastIndex(); + $this->indexesRequest->create($index); + + $curr = $this->runner->getInfoInt('indexCount'); + $this->runner->setInfoInt('indexCount', ++$curr); + } + } + } + } + + + /** + * @param string $collection + * + * @throws CollectionArgumentException + */ + public function confirmCollectionString(string $collection): void { +// throw new CollectionArgumentException(); + } + + /** + * @param string $collection + * + * @throws CollectionArgumentException + */ + public function confirmCollection(string $collection): void { + $this->confirmCollectionString($collection); + + if (!$this->hasCollection($collection)) { + throw new CollectionArgumentException('collection does not exist'); + } + } + + + /** + * @param string $collection + * @param string $providerId + * @param string $documentId + * + * @return IIndexDocument + * @throws ProviderDoesNotExistException + * @throws IndexDoesNotExistException + */ + public function getDocument(string $collection, string $providerId, string $documentId): IIndexDocument { + $wrapped = $this->providerService->getProvider($providerId); + $provider = $wrapped->getProvider(); + + $index = $this->indexService->getIndex($providerId, $documentId, $collection); + $index->setStatus(IIndex::INDEX_FULL); + + return $provider->updateDocument($index); + } + + + /** + * @param string $info + * @param string $value + */ + private function updateRunnerInfo(string $info, string $value): void { + if (is_null($this->runner)) { + return; + } + + $this->runner->setInfo($info, $value); + } + + + /** + * @param array $data + */ + private function updateRunnerInfoArray(array $data): void { + if (is_null($this->runner)) { + return; + } + + $this->runner->setInfoArray($data); + } + +} diff --git a/lib/Service/ConfigService.php b/lib/Service/ConfigService.php index 457ec1a..1bd5a2e 100644 --- a/lib/Service/ConfigService.php +++ b/lib/Service/ConfigService.php @@ -32,6 +32,7 @@ namespace OCA\FullTextSearch\Service; use OCA\FullTextSearch\AppInfo\Application; +use OCA\FullTextSearch\Exceptions\DatabaseException; use OCA\FullTextSearch\Exceptions\ProviderOptionsDoesNotExistException; use OCP\IConfig; use OCP\PreConditionNotMetException; @@ -51,14 +52,21 @@ class ConfigService { const PROVIDER_INDEXED = 'provider_indexed'; const CRON_LAST_ERR_RESET = 'cron_err_reset'; const TICK_TTL = 'tick_ttl'; + const COLLECTION_INDEXING_LIST = 'collection_indexing_list'; + + + // Temp. can be removed after few major releases + const MIGRATION_24 = 'migration_24'; /** @var array */ public $defaults = [ - self::SEARCH_PLATFORM => '', - self::APP_NAVIGATION => '0', - self::PROVIDER_INDEXED => '', + self::SEARCH_PLATFORM => '', + self::APP_NAVIGATION => '0', + self::PROVIDER_INDEXED => '', self::CRON_LAST_ERR_RESET => '0', - self::TICK_TTL => '1800' + self::TICK_TTL => '1800', + self::COLLECTION_INDEXING_LIST => 50, + self::MIGRATION_24 => 1 ]; @@ -135,14 +143,24 @@ class ConfigService { * @return string */ public function getAppValue(string $key): string { - $defaultValue = null; + $defaultValue = ''; if (array_key_exists($key, $this->defaults)) { $defaultValue = $this->defaults[$key]; } - return $this->config->getAppValue(Application::APP_ID, $key, $defaultValue); + return (string)$this->config->getAppValue(Application::APP_ID, $key, $defaultValue); } + /** + * @param string $config + * + * @return int + */ + public function getAppValueInt(string $config): int { + return (int)$this->getAppValue($config); + } + + /** * Set a value by key * @@ -274,4 +292,16 @@ class ConfigService { return $ver[0]; } + + + /** + * @throws DatabaseException + */ + public function requireMigration24(): void { + if ($this->getAppValueInt(self::MIGRATION_24) === 1) { + return; + } + + throw new DatabaseException('please run ./occ fulltextsearch:migration:24'); + } } diff --git a/lib/Service/IndexService.php b/lib/Service/IndexService.php index 2192536..b30283b 100644 --- a/lib/Service/IndexService.php +++ b/lib/Service/IndexService.php @@ -33,9 +33,9 @@ namespace OCA\FullTextSearch\Service; use Exception; use OCA\FullTextSearch\Db\IndexesRequest; -use OCA\FullTextSearch\Exceptions\DatabaseException; use OCA\FullTextSearch\Exceptions\IndexDoesNotExistException; use OCA\FullTextSearch\Exceptions\NotIndexableDocumentException; +use OCA\FullTextSearch\Exceptions\ProviderDoesNotExistException; use OCA\FullTextSearch\Model\Index; use OCA\FullTextSearch\Model\IndexOptions; use OCA\FullTextSearch\Model\Runner; @@ -59,18 +59,12 @@ class IndexService implements IIndexService { /** @var IndexesRequest */ private $indexesRequest; - /** @var ConfigService */ - private $configService; - /** @var ProviderService */ private $providerService; /** @var PlatformService */ private $platformService; - /** @var MiscService */ - private $miscService; - /** @var Runner */ private $runner = null; @@ -86,20 +80,17 @@ class IndexService implements IIndexService { * IndexService constructor. * * @param IndexesRequest $indexesRequest - * @param ConfigService $configService * @param ProviderService $providerService * @param PlatformService $platformService - * @param MiscService $miscService */ public function __construct( - IndexesRequest $indexesRequest, ConfigService $configService, - ProviderService $providerService, PlatformService $platformService, MiscService $miscService + IndexesRequest $indexesRequest, + ProviderService $providerService, + PlatformService $platformService ) { $this->indexesRequest = $indexesRequest; - $this->configService = $configService; $this->providerService = $providerService; $this->platformService = $platformService; - $this->miscService = $miscService; } @@ -167,15 +158,15 @@ class IndexService implements IIndexService { $this->updateRunnerAction('generateIndex' . $provider->getName()); $this->updateRunnerInfoArray( [ - 'userId' => $userId, - 'providerId' => $provider->getId(), - 'providerName' => $provider->getName(), - 'chunkCurrent' => 0, - 'chunkTotal' => 0, + 'userId' => $userId, + 'providerId' => $provider->getId(), + 'providerName' => $provider->getName(), + 'chunkCurrent' => 0, + 'chunkTotal' => 0, 'documentCurrent' => 0, - 'documentTotal' => 0, - 'info' => '', - 'title' => '' + 'documentTotal' => 0, + 'info' => '', + 'title' => '' ] ); @@ -193,14 +184,14 @@ class IndexService implements IIndexService { $this->currentTotalDocuments = sizeof($documents); $this->updateRunnerInfoArray( [ - 'documentTotal' => $this->currentTotalDocuments, + 'documentTotal' => $this->currentTotalDocuments, 'documentCurrent' => 0 ] ); //$maxSize = sizeof($documents); - $toIndex = $this->updateDocumentsWithCurrIndex($provider, $documents, $options); + $toIndex = $this->updateDocumentsWithCurrIndex($provider, '', $documents, $options); $this->indexDocuments($platform, $provider, $toIndex, $options); } } @@ -214,14 +205,16 @@ class IndexService implements IIndexService { * @return IIndexDocument[] * @throws Exception */ - private function updateDocumentsWithCurrIndex( - IFullTextSearchProvider $provider, array $documents, IIndexOptions $options + public function updateDocumentsWithCurrIndex( + IFullTextSearchProvider $provider, + string $collection, + array $documents, + IIndexOptions $options ): array { $result = []; $count = 0; foreach ($documents as $document) { - if ($count % 1000 === 0) { $this->updateRunnerAction('compareWithCurrentIndex', true); $this->updateRunnerInfo('documentCurrent', (string)$count); @@ -229,10 +222,13 @@ class IndexService implements IIndexService { $count++; try { - $index = - $this->indexesRequest->getIndex($document->getProviderId(), $document->getId()); + $index = $this->indexesRequest->getIndex( + $document->getProviderId(), + $document->getId(), + $collection + ); } catch (IndexDoesNotExistException $e) { - $index = new Index($document->getProviderId(), $document->getId()); + $index = new Index($document->getProviderId(), $document->getId(), $collection); $index->setStatus(Index::INDEX_FULL); $index->setLastIndex(); } @@ -267,7 +263,9 @@ class IndexService implements IIndexService { * * @return bool */ - private function isDocumentUpToDate(IFullTextSearchProvider $provider, IIndexDocument $document + private function isDocumentUpToDate( + IFullTextSearchProvider $provider, + IIndexDocument $document ): bool { $index = $document->getIndex(); if (!$index->isStatus(Index::INDEX_OK)) { @@ -305,11 +303,11 @@ class IndexService implements IIndexService { $this->updateRunnerAction('fillDocument', true); $this->updateRunnerInfoArray( [ - 'documentId' => $document->getId(), - 'info' => '', - 'title' => '', - 'content' => '', - 'status' => '', + 'documentId' => $document->getId(), + 'info' => '', + 'title' => '', + 'content' => '', + 'status' => '', 'statusColored' => '' ] ); @@ -317,7 +315,7 @@ class IndexService implements IIndexService { $provider->fillIndexDocument($document); $this->updateRunnerInfoArray( [ - 'title' => $document->getTitle(), + 'title' => $document->getTitle(), 'content' => $document->getContentSize() ] ); @@ -366,8 +364,8 @@ class IndexService implements IIndexService { $this->updateRunnerInfoArray( [ 'documentId' => $document->getId(), - 'title' => $document->getTitle(), - 'content' => $document->getContentSize() + 'title' => $document->getTitle(), + 'content' => $document->getContentSize() ] ); @@ -394,7 +392,7 @@ class IndexService implements IIndexService { $this->updateRunnerInfoArray( [ 'providerName' => $provider->getName(), - 'userId' => $index->getOwnerId(), + 'userId' => $index->getOwnerId(), ] ); @@ -424,8 +422,8 @@ class IndexService implements IIndexService { $this->updateRunnerInfoArray( [ 'documentId' => $document->getId(), - 'title' => $document->getTitle(), - 'content' => $document->getContentSize() + 'title' => $document->getTitle(), + 'content' => $document->getContentSize() ] ); @@ -438,28 +436,19 @@ class IndexService implements IIndexService { /** * @param Index[] $indexes - * - * @throws DatabaseException */ public function updateIndexes(array $indexes) { - try { - foreach ($indexes as $index) { - $this->updateIndex($index); - } - $this->resetErrorFromQueue(); - } catch (Exception $e) { - throw new DatabaseException($e->getMessage()); + foreach ($indexes as $index) { + $this->updateIndex($index); } + $this->resetErrorFromQueue(); } /** * @param IIndex $index - * - * @throws Exception */ public function updateIndex(IIndex $index) { - /** @var Index $index */ $this->updateIndexError($index); if ($index->isStatus(IIndex::INDEX_REMOVE)) { @@ -502,22 +491,20 @@ class IndexService implements IIndexService { * @throws Exception */ public function updateIndexStatus( - string $providerId, string $documentId, int $status, bool $reset = false + string $providerId, + string $documentId, + int $status, + bool $reset = false ) { - if ($reset === true) { - $this->indexesRequest->updateStatus($providerId, $documentId, $status); - - return; + $indexes = $this->indexesRequest->getIndexes($providerId, $documentId); + if (empty($indexes)) { + $indexes = $this->indexesRequest->migrateIndex24($providerId, $documentId); } - try { - $curr = $this->getIndex($providerId, $documentId); - } catch (IndexDoesNotExistException $e) { - return; + foreach ($indexes as $index) { + $index->setStatus($status); + $this->updateIndex($index); } - - $curr->setStatus($status); - $this->updateIndex($curr); } @@ -526,27 +513,18 @@ class IndexService implements IIndexService { * @param array $documentIds * @param int $status * @param bool $reset - * - * @throws DatabaseException */ public function updateIndexesStatus( - string $providerId, array $documentIds, int $status, bool $reset = false + string $providerId, + array $documentIds, + int $status, + bool $reset = false ) { - if ($reset === true) { - $this->indexesRequest->updateStatuses($providerId, $documentIds, $status); - - return; - } - - try { - $all = $this->getIndexes($providerId, $documentIds); - } catch (IndexDoesNotExistException $e) { - return; - } - - foreach ($all as $curr) { - $curr->setStatus($status); - $this->updateIndexes([$curr]); + foreach ($documentIds as $documentId) { + try { + $this->updateIndexStatus($providerId, $documentId, $status, $reset); + } catch (Exception $e) { + } } } @@ -588,39 +566,41 @@ class IndexService implements IIndexService { /** * @param string $providerId - * @param array $documentId + * @param string $documentId * * @return Index[] - * @throws IndexDoesNotExistException */ - public function getIndexes($providerId, $documentId) { + public function getIndexes(string $providerId, string $documentId): array { return $this->indexesRequest->getIndexes($providerId, $documentId); } /** + * @param string $collection * @param bool $all + * @param int $length * * @return Index[] */ - public function getQueuedIndexes(bool $all = false): array { - return $this->indexesRequest->getQueuedIndexes($all); + public function getQueuedIndexes(string $collection = '', bool $all = false, int $length = -1): array { + return $this->indexesRequest->getQueuedIndexes($collection, $all, $length); } /** * @param string $providerId + * @param string $collection * - * @throws Exception + * @throws ProviderDoesNotExistException */ - public function resetIndex(string $providerId = '') { + public function resetIndex(string $providerId = '', string $collection = '') { $wrapper = $this->platformService->getPlatform(); $platform = $wrapper->getPlatform(); if ($providerId === '') { $platform->resetIndex('all'); $this->providerService->setProvidersAsNotIndexed(); - $this->indexesRequest->reset(); + $this->indexesRequest->reset($collection); return; } else { @@ -628,13 +608,13 @@ class IndexService implements IIndexService { $providers = [$providerWrapper->getProvider()]; } - foreach ($providers AS $provider) { + foreach ($providers as $provider) { // TODO: need to specify the map to remove // TODO: need to remove entries with type=providerId // $provider->onResettingIndex($platform); $platform->resetIndex($provider->getId()); - $this->providerService->setProviderAsIndexed($provider, false); + $this->providerService->setProviderAsIndexed($provider->getId(), false); $this->indexesRequest->deleteFromProviderId($provider->getId()); } } @@ -647,8 +627,8 @@ class IndexService implements IIndexService { * @return IIndex * @throws IndexDoesNotExistException */ - public function getIndex(string $providerId, string $documentId): IIndex { - return $this->indexesRequest->getIndex($providerId, $documentId); + public function getIndex(string $providerId, string $documentId, string $collection = ''): IIndex { + return $this->indexesRequest->getIndex($providerId, $documentId, $collection); } @@ -659,23 +639,33 @@ class IndexService implements IIndexService { * @param int $status * * @return IIndex + * @throws IndexDoesNotExistException */ - public function createIndex(string $providerId, string $documentId, string $userId, int $status + public function createIndex( + string $providerId, + string $documentId, + string $userId, + int $status ): IIndex { - - try { - $known = $this->indexesRequest->getIndex($providerId, $documentId); - $known->setStatus($status); - $this->indexesRequest->updateStatus($providerId, $documentId, $status); - - return $known; - } catch (IndexDoesNotExistException $e) { + $index = null; + foreach ($this->indexesRequest->getCollections() as $collection) { + try { + $index = $this->indexesRequest->getIndex($providerId, $documentId, $collection); + $index->setStatus($status, true); + $this->indexesRequest->update($index, true); + } catch (IndexDoesNotExistException $e) { + $index = new Index($providerId, $documentId, $collection); + $index->setOwnerId($userId); + $index->setStatus($status); + $this->indexesRequest->create($index); + } } - $index = new Index($providerId, $documentId); - $index->setOwnerId($userId); - $index->setStatus($status); - $this->indexesRequest->create($index); + if (is_null($index)) { + throw new IndexDoesNotExistException(); + } + + $index->setCollection(''); return $index; } diff --git a/lib/Service/MigrationService.php b/lib/Service/MigrationService.php new file mode 100644 index 0000000..4b3c793 --- /dev/null +++ b/lib/Service/MigrationService.php @@ -0,0 +1,182 @@ + + * @copyright 2022 + * @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 . + * + */ + + +namespace OCA\FullTextSearch\Service; + + +use OCP\DB\Exception; +use OCP\DB\IResult; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; +use Symfony\Component\Console\Helper\ProgressBar; + + +class MigrationService { + + + /** @var IDBConnection */ + private $dbConnection; + + /** @var ConfigService */ + private $configService; + + + /** @var ProgressBar */ + private $progressBar; + + + /** + * @param IDBConnection $dbConnection + * @param ConfigService $configService + */ + public function __construct( + IDBConnection $dbConnection, + ConfigService $configService + ) { + $this->dbConnection = $dbConnection; + $this->configService = $configService; + } + + + public function setProgressBar(ProgressBar $progressBar): void { + $this->progressBar = $progressBar; + } + + + /** + * @param int $size + * + * @return bool + * @throws Exception + */ + public function migrate24Chunk(int $size = 10000): bool { + if ($this->configService->getAppValueInt(ConfigService::MIGRATION_24) === 1) { + return false; + } + + if ($size === 0) { + return false; + } + + $oldData = $this->getChunkOldData($size); + if ($oldData->rowCount() === 0) { + $this->configService->setAppValue(ConfigService::MIGRATION_24, '1'); + if ($this->dbConnection->tableExists('fulltextsearch_indexes')) { + $this->dbConnection->dropTable('fulltextsearch_indexes'); + } + + return false; + } + + $this->createNewEntries($oldData); + + return true; + } + + + /** + * @param int $size + * + * @return IResult + * @throws Exception + */ + private function getChunkOldData(int $size): IResult { + $query = $this->dbConnection->getQueryBuilder(); + $query->select('*') + ->from('fulltextsearch_indexes') + ->orderBy('provider_id', 'asc') + ->addOrderBy('document_id', 'asc') + ->setFirstResult(0) + ->setMaxResults($size); + + return $query->executeQuery(); + } + + + /** + * @param IResult $oldData + * + * @throws Exception + */ + private function createNewEntries(IResult $oldData): void { + $create = $this->dbConnection->getQueryBuilder(); + $create->insert('fulltextsearch_index') + ->values( + [ + 'owner_id' => $create->createParameter('owner_id'), + 'provider_id' => $create->createParameter('provider_id'), + 'collection' => $create->createParameter('collection'), + 'document_id' => $create->createParameter('document_id'), + 'source' => $create->createParameter('source'), + 'err' => $create->createParameter('err'), + 'message' => $create->createParameter('message'), + 'status' => $create->createParameter('status'), + 'options' => $create->createParameter('options'), + 'indexed' => $create->createParameter('indexed') + ] + ); + + while ($row = $oldData->fetch()) { + $create->setParameter('owner_id', $row['owner_id']) + ->setParameter('provider_id', $row['provider_id']) + ->setParameter('collection', '') + ->setParameter('document_id', $row['document_id']) + ->setParameter('source', $row['source']) + ->setParameter('err', $row['err'], IQueryBuilder::PARAM_INT) + ->setParameter('message', $row['message']) + ->setParameter('status', $row['status'], IQueryBuilder::PARAM_INT) + ->setParameter('options', $row['options']) + ->setParameter('indexed', $row['indexed'], IQueryBuilder::PARAM_INT); + + if (!is_null($this->progressBar)) { + $this->progressBar->advance(); + } + + try { + $create->executeStatement(); + } catch (Exception $e) { + if ($e->getReason() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) { + throw $e; + } + } + + $delete = $this->dbConnection->getQueryBuilder(); + $delete->delete('fulltextsearch_indexes'); + $delete->where( + $delete->expr()->eq('provider_id', $delete->createNamedParameter($row['provider_id'])) + ); + $delete->andWhere( + $delete->expr()->eq('document_id', $delete->createNamedParameter($row['document_id'])) + ); + + $delete->executeStatement(); + } + } +} diff --git a/lib/Service/ProviderService.php b/lib/Service/ProviderService.php index 17cda2d..093cfb4 100644 --- a/lib/Service/ProviderService.php +++ b/lib/Service/ProviderService.php @@ -244,12 +244,13 @@ class ProviderService implements IProviderService { /** - * @param IFullTextSearchProvider $provider + * @param string $providerId * @param bool $boolean */ - public function setProviderAsIndexed(IFullTextSearchProvider $provider, bool $boolean) { + public function setProviderAsIndexed(string $providerId, bool $boolean) { $this->configService->setProviderOptions( - $provider->getId(), ConfigService::PROVIDER_INDEXED, (($boolean) ? '1' : '0') + $providerId, + ConfigService::PROVIDER_INDEXED, (($boolean) ? '1' : '0') ); } @@ -291,7 +292,7 @@ class ProviderService implements IProviderService { if (array_key_exists('@attributes', $providers)) { $providers = [$providers]; } - foreach ($providers AS $provider) { + foreach ($providers as $provider) { if (is_array($provider)) { $attributes = $provider['@attributes']; if (array_key_exists('min-version', $attributes) @@ -326,7 +327,7 @@ class ProviderService implements IProviderService { * @throws Exception */ private function providerIdMustBeUnique(IFullTextSearchProvider $provider) { - foreach ($this->providers AS $providerWrapper) { + foreach ($this->providers as $providerWrapper) { $knownProvider = $providerWrapper->getProvider(); if ($knownProvider->getId() === $provider->getId()) { throw new ProviderIsNotUniqueException( @@ -346,7 +347,7 @@ class ProviderService implements IProviderService { $arr = []; foreach ($providers as $provider) { $arr[] = [ - 'id' => $provider->getId(), + 'id' => $provider->getId(), 'name' => $provider->getName() ]; }