From 9c6867f4945a1648b2fba19f135dcf0dc02dc29c Mon Sep 17 00:00:00 2001 From: Alban Jubert Date: Tue, 27 Feb 2018 11:22:09 +0100 Subject: [PATCH] Initial commit --- .craftplugin | 1 + .gitignore | 32 +++ CHANGELOG.md | 9 + LICENSE.md | 9 + README.md | 36 +++ composer.json | 47 ++++ resources/img/plugin-logo.png | Bin 0 -> 11047 bytes src/Elasticsearch.php | 236 ++++++++++++++++++ .../elasticsearch/ElasticsearchAsset.php | 65 +++++ .../elasticsearch/dist/css/Elasticsearch.css | 11 + .../dist/img/Elasticsearch-icon.svg | 52 ++++ .../elasticsearch/dist/js/Elasticsearch.js | 11 + .../ElasticsearchCPSectionAsset.php | 65 +++++ .../dist/css/Elasticsearch.css | 11 + .../elasticsearchcpsection/dist/img/icon.svg | 56 +++++ .../dist/js/Elasticsearch.js | 11 + src/config.php | 27 ++ .../controllers/ElasticsearchController.php | 82 ++++++ src/controllers/ElasticsearchController.php | 80 ++++++ src/icon-mask.svg | 16 ++ src/icon.svg | 13 + src/jobs/IndexElement.php | 97 +++++++ src/models/Settings.php | 70 ++++++ src/records/ElasticsearchRecord.php | 230 +++++++++++++++++ src/services/Elasticsearch.php | 198 +++++++++++++++ src/templates/index.twig | 52 ++++ src/templates/settings.twig | 48 ++++ src/translations/en/elasticsearch.php | 25 ++ src/translations/fr/elasticsearch.php | 35 +++ src/variables/ElasticsearchVariable.php | 56 +++++ 30 files changed, 1681 insertions(+) create mode 100644 .craftplugin create mode 100755 .gitignore create mode 100755 CHANGELOG.md create mode 100755 LICENSE.md create mode 100755 README.md create mode 100755 composer.json create mode 100644 resources/img/plugin-logo.png create mode 100755 src/Elasticsearch.php create mode 100755 src/assetbundles/elasticsearch/ElasticsearchAsset.php create mode 100755 src/assetbundles/elasticsearch/dist/css/Elasticsearch.css create mode 100755 src/assetbundles/elasticsearch/dist/img/Elasticsearch-icon.svg create mode 100755 src/assetbundles/elasticsearch/dist/js/Elasticsearch.js create mode 100755 src/assetbundles/elasticsearchcpsection/ElasticsearchCPSectionAsset.php create mode 100755 src/assetbundles/elasticsearchcpsection/dist/css/Elasticsearch.css create mode 100755 src/assetbundles/elasticsearchcpsection/dist/img/icon.svg create mode 100755 src/assetbundles/elasticsearchcpsection/dist/js/Elasticsearch.js create mode 100755 src/config.php create mode 100755 src/console/controllers/ElasticsearchController.php create mode 100755 src/controllers/ElasticsearchController.php create mode 100644 src/icon-mask.svg create mode 100644 src/icon.svg create mode 100755 src/jobs/IndexElement.php create mode 100755 src/models/Settings.php create mode 100644 src/records/ElasticsearchRecord.php create mode 100755 src/services/Elasticsearch.php create mode 100755 src/templates/index.twig create mode 100755 src/templates/settings.twig create mode 100755 src/translations/en/elasticsearch.php create mode 100755 src/translations/fr/elasticsearch.php create mode 100755 src/variables/ElasticsearchVariable.php diff --git a/.craftplugin b/.craftplugin new file mode 100644 index 0000000..c131703 --- /dev/null +++ b/.craftplugin @@ -0,0 +1 @@ +{"pluginName":"Elasticsearch","pluginDescription":"Bring the power of Elasticsearch to you Craft 3 CMS project","pluginVersion":"1.0.0","pluginAuthorName":"La Haute Société","pluginVendorName":"La Haute Société","pluginAuthorUrl":"https://www.lahautesociete.com","pluginAuthorGithub":"juban","codeComments":"codeComments","pluginComponents":["jobs","tasks","consolecommands","controllers","cpsection","models","records","services","settings","variables"],"consolecommandName":"Elasticsearch","controllerName":"Elasticsearch","cpsectionName":"Elasticsearch","modelName":"Elasticsearch","recordName":"Elasticsearch","serviceName":"Elasticsearch","apiVersion":"api_version_3_0"} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..a17970c --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# CRAFT ENVIRONMENT +.env.php +.env.sh +.env + +# COMPOSER +/vendor + +# BUILD FILES +/bower_components/* +/node_modules/* +/build/* +/yarn-error.log + +# MISC FILES +.cache +.DS_Store +.idea +.project +.settings +*.esproj +*.sublime-workspace +*.sublime-project +*.tmproj +*.tmproject +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +config.codekit3 +prepros-6.config diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100755 index 0000000..06a3ba9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Elasticsearch Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). + +## 1.0.0 - 2018-02-06 +### Added +- Initial release diff --git a/LICENSE.md b/LICENSE.md new file mode 100755 index 0000000..d79c90b --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright (c) 2018 Alban Jubert + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100755 index 0000000..bdf1e85 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# Elasticsearch plugin for Craft CMS 3.x + +Bring the power of Elasticsearch to you Craft 3 CMS project + +![Screenshot](resources/img/plugin-logo.png) + +## Requirements + +This plugin requires Craft CMS 3.0.0-RC1 or later. +In order to index data, you will need an Elasticsearch 6.0 or later with the Ingest attachment processor plugin activated. + +2. Then tell Composer to load the plugin: + + composer require la-hautes-societe/craft-elasticsearch + +3. In the Control Panel, go to Settings → Plugins and click the “Install” button for Elasticsearch. + +## Elasticsearch Overview + +-Insert text here- + +## Configuring Elasticsearch + +-Insert text here- + +## Using Elasticsearch + +-Insert text here- + +## Elasticsearch Roadmap + +Some things to do, and ideas for potential features: + +* Release it + +Brought to you by [La Haute Société](https://www.lahautesociete.com) diff --git a/composer.json b/composer.json new file mode 100755 index 0000000..efc3782 --- /dev/null +++ b/composer.json @@ -0,0 +1,47 @@ +{ + "name": "la-haute-societe/craft-elasticsearch", + "description": "Bring the power of Elasticsearch to your Craft CMS projects.", + "type": "craft-plugin", + "version": "1.0.0", + "keywords": [ + "craft", + "cms", + "craftcms", + "craft-plugin", + "elasticsearch" + ], + "support": { + "docs": "https://github.com/la-haute-societe/craft-elasticsearch/blob/master/README.md", + "issues": "https://github.com/la-haute-societe/craft-elasticsearch/issues" + }, + "license": "MIT", + "authors": [ + { + "name": "La Haute Société", + "homepage": "https://www.lahautesociete.com" + } + ], + "minimum-stability": "dev", + "prefer-stable": true, + "require": { + "craftcms/cms": "^3.0.0-RC1", + "yiisoft/yii2-elasticsearch": "^2.1.0-dev" + }, + "autoload": { + "psr-4": { + "lhs\\elasticsearch\\": "src/" + } + }, + "extra": { + "name": "Elasticsearch", + "handle": "elasticsearch", + "schemaVersion": "1.0.0", + "hasCpSettings": true, + "hasCpSection": true, + "changelogUrl": "https://raw.githubusercontent.com/la-haute-societe/craft-elasticsearch/master/CHANGELOG.md", + "components": { + "elasticsearch": "lhs\\elasticsearch\\services\\Elasticsearch" + }, + "class": "lhs\\elasticsearch\\Elasticsearch" + } +} diff --git a/resources/img/plugin-logo.png b/resources/img/plugin-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..931c63937db24a6846b4b2b1755ccbc2d55672f8 GIT binary patch literal 11047 zcmZvC1ymf(wlxskfofiuBy}f$Ue1JclA0IuB<49h5jBL4h{}WMq2#K-@f&46G27(dk6I)SO4wcUA{%xrtq}0wRhq36a@SW!S{Fkj~ED`_!q?0Mi8K-piCj=;A}?0#rlbr4IqS0 zK|vwlY--N;MO^Yf>VIc~083X_M?N6X!^4BsgOk<4*#gM^`SWKW8wZesgXJ%R#RY8d zYV65k??Ux&BmZSb+|1>xvz4Q(m4iLSKX#2x9Nb(50f2uT{rB~sIPDz&+mXG?e^~wH z2lO;{1hTWT0sntWu2$y%6W;%!_0N-ktNn}BznuyEO%0!zvzf80gR`20gRPM0Ke?lj zu`{+X`(kEn3KjtV--!Q-RNx&*4yVx?jPPS8b%w4wGHsuSDL;8%64?bL!g9K|KsTR2(Ldx$j8g@^78_f(bf) z-EqdiQtJc6;**A--{qu3T?&E!qa>TYoZri>CLg)42FbWp{lq z_&d|>?54bE=xX`|9^CFJ(;uJb!$flAJ%p~|qVX6z|6)JuRH1Q8{w3y+RC|a0SJ-w# z7iEH=o&u#oJvM48E<9&tA3Yjjhr~q|`m`7G?B;{Q$d>O_Z|2$sUERzyB2mDgV&&41xtY523=4k<6?X?g3znHl^*n<0@XkxR3CiI zP%8y+I3$HulIfYbmvn>b&7e>6&w^;38%Mi$0u{r944$e%J?E0dS&UHEmG?8dHjH4T zqBNdcau)8>wx~Yzi#d!S!JZcDqhQ>g5j?7?BBT&;cj;s4O2;H?0hyEx8MEQ9>^3!8 zwi%~cDw*w-4YB)5hc6^KOAafsqqdK^dmbRQF`N893iQj(P^nApHcjz40DkHHji3mx-qL1ESfJ&HLVE|MELsH-EM zIa2l$*pW&dnoq(!dK)L7<9sL^a$1AlaR8`@c-MDom0X$$zsv!ddT)#iGf$J!wpL;) zucnxe1Ups8#5v4s7>?f$FOEk;)?}zq^J#cW# z(z6RU3~f&TNY!kaz^wS#?%SFZ$jpw>|DQ zc&Q{#>u1c!a*3AeK)qzH`NwWe*Q-b5z8v15asCCCs=G1xY*>s0qlW0t1`0jC1na3TeK1*R>aeWo z12~l#;5_O8zeX%))y_xB+F?ZmN$-W&t{RPhYYta@W1Nu*&E4h1^2=7DA9M=vh!@+W zwZFCBKKn3*;1jR^1GRW`n4Z;&O0T2)D(&lwy%W758i(j@VSQgw%#LCf+ED1(Oo+Vl z2k(6;*)U`(TCE&QQ27aS-C1%lYy)5jaoVeQ8 zz`wSRE9Kf_{5lm{z1qSqWfTn&lD^2M#nyW&>{of%?9q6B&L|akM~cZm|B`+Zz1BfV z(WsVDOhgx2fju)vRkAF-QuPL7n@Z%zq`p22py?a2OnBq(txe^c`zpbkE;Gtir!4S9 zYNM8ZY9Hf>^yG8d#&VaR$GgCMs&gs69L7&)tFq6FiV-z)<3&nU7NTE?2t()UDg!6K z`&NHIzPw;E6x{Z3?_%v`d`Lta3-gwc07m8kCrxrI856V@OgAdWkSP}Vukuj z1AN?RMd}O`Ei`n>%X!O8_n2~OrGD;z3bd=enwq-#=JlTYCpJ0f0#B%N&fEx6!6g3P z#KukTQqG~MFbQf`>y)yzoUaASF-KzU@M> zpxNoF`Gw5JLUp2nVr%Ab>F~%_mPB>r�s>*zxxQzE@_2x9zxObLGAks2-txORgDo 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 Ws}_SL+r&SoS7js=#VbKZ0sjjJDZ^g? literal 0 HcmV?d00001 diff --git a/src/Elasticsearch.php b/src/Elasticsearch.php new file mode 100755 index 0000000..39397ac --- /dev/null +++ b/src/Elasticsearch.php @@ -0,0 +1,236 @@ +name = "Elasticsearch"; + self::$plugin = $this; + + $this->setComponents([ + 'connection' => [ + 'class' => 'yii\elasticsearch\Connection', + 'nodes' => [ + ['http_address' => $this->settings->http_address], + // configure more hosts if you have a cluster + ], + 'auth' => [ + 'username' => $this->settings->auth_username, + 'password' => $this->settings->auth_password + ] + ] + ]); + + // Add in our console commands + if (Craft::$app instanceof ConsoleApplication) { + $this->controllerNamespace = 'lahautesociete\elasticsearch\console\controllers'; + } + + // Register our site routes + // Event::on( + // UrlManager::class, + // UrlManager::EVENT_REGISTER_SITE_URL_RULES, + // function (RegisterUrlRulesEvent $event) { + // $event->rules['siteActionTrigger1'] = 'elasticsearch/elasticsearch'; + // } + // ); + + // Register our CP routes + Event::on( + UrlManager::class, + UrlManager::EVENT_REGISTER_CP_URL_RULES, + function (RegisterUrlRulesEvent $event) { + $event->rules['elasticsearch/test-connection'] = 'elasticsearch/elasticsearch/test-connection'; + $event->rules['elasticsearch/reindex-all'] = 'elasticsearch/elasticsearch/reindex-all'; + } + ); + + // Register our variables + Event::on( + CraftVariable::class, + CraftVariable::EVENT_INIT, + function (Event $event) { + /** @var CraftVariable $variable */ + $variable = $event->sender; + $variable->set('elasticsearch', ElasticsearchVariable::class); + } + ); + + /* + * Add or update an element to the index + */ + Event::on( + Element::class, + Element::EVENT_AFTER_SAVE, + function (Event $event) { + $element = $event->sender; + if ($element instanceof Entry) { + if ($element->enabled) { + // Elasticsearch::$plugin->elasticsearch->indexEntry($element); + Craft::$app->queue->push(new IndexElement([ + 'siteId' => $element->siteId, + 'elementId' => $element->id + ])); + } else { + Elasticsearch::$plugin->elasticsearch->deleteEntry($element); + } + + } + } + ); + + /* + * Delete an element from the index + */ + Event::on( + Element::class, + Element::EVENT_AFTER_DELETE, + function (Event $event) { + $element = $event->sender; + Elasticsearch::$plugin->elasticsearch->deleteEntry($element); + } + ); + + // Do something after we're installed + Event::on( + Plugins::class, + Plugins::EVENT_AFTER_INSTALL_PLUGIN, + function (PluginEvent $event) { + if ($event->plugin === $this) { + // We were just installed + } + } + ); + + + /** + * Logging in Craft involves using one of the following methods: + * + * Craft::trace(): record a message to trace how a piece of code runs. This is mainly for development use. + * Craft::info(): record a message that conveys some useful information. + * Craft::warning(): record a warning message that indicates something unexpected has happened. + * Craft::error(): record a fatal error that should be investigated as soon as possible. + * + * Unless `devMode` is on, only Craft::warning() & Craft::error() will log to `craft/storage/logs/web.log` + * + * It's recommended that you pass in the magic constant `__METHOD__` as the second parameter, which sets + * the category to the method (prefixed with the fully qualified class name) where the constant appears. + * + * To enable the Yii debug toolbar, go to your user account in the AdminCP and check the + * [] Show the debug toolbar on the front end & [] Show the debug toolbar on the Control Panel + * + * http://www.yiiframework.com/doc-2.0/guide-runtime-logging.html + */ + Craft::info( + Craft::t( + 'elasticsearch', + '{name} plugin loaded', + ['name' => $this->name] + ), + __METHOD__ + ); + } + + // Protected Methods + // ========================================================================= + + /** + * Creates and returns the model used to store the plugin’s settings. + * + * @return \craft\base\Model|null + */ + protected function createSettingsModel() + { + return new Settings(); + } + + /** + * Returns the rendered settings HTML, which will be inserted into the content + * block on the settings page. + * + * @return string The rendered settings HTML + */ + protected function settingsHtml(): string + { + return Craft::$app->view->renderTemplate( + 'elasticsearch/settings', + [ + 'settings' => $this->getSettings() + ] + ); + } +} diff --git a/src/assetbundles/elasticsearch/ElasticsearchAsset.php b/src/assetbundles/elasticsearch/ElasticsearchAsset.php new file mode 100755 index 0000000..7a826a4 --- /dev/null +++ b/src/assetbundles/elasticsearch/ElasticsearchAsset.php @@ -0,0 +1,65 @@ +sourcePath = "@lhs/elasticsearch/assetbundles/elasticsearch/dist"; + + // define the dependencies + $this->depends = [ + CpAsset::class, + ]; + + // define the relative path to CSS/JS files that should be registered with the page + // when this asset bundle is registered + $this->js = [ + 'js/Elasticsearch.js', + ]; + + $this->css = [ + 'css/Elasticsearch.css', + ]; + + parent::init(); + } +} diff --git a/src/assetbundles/elasticsearch/dist/css/Elasticsearch.css b/src/assetbundles/elasticsearch/dist/css/Elasticsearch.css new file mode 100755 index 0000000..b41add6 --- /dev/null +++ b/src/assetbundles/elasticsearch/dist/css/Elasticsearch.css @@ -0,0 +1,11 @@ +/** + * Elasticsearch plugin for Craft CMS + * + * Elasticsearch CSS + * + * @author Alban Jubert + * @copyright Copyright (c) 2018 Alban Jubert + * @link https://www.lahautesociete.com + * @package Elasticsearch + * @since 1.0.0 + */ diff --git a/src/assetbundles/elasticsearch/dist/img/Elasticsearch-icon.svg b/src/assetbundles/elasticsearch/dist/img/Elasticsearch-icon.svg new file mode 100755 index 0000000..4370339 --- /dev/null +++ b/src/assetbundles/elasticsearch/dist/img/Elasticsearch-icon.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assetbundles/elasticsearch/dist/js/Elasticsearch.js b/src/assetbundles/elasticsearch/dist/js/Elasticsearch.js new file mode 100755 index 0000000..9e7fead --- /dev/null +++ b/src/assetbundles/elasticsearch/dist/js/Elasticsearch.js @@ -0,0 +1,11 @@ +/** + * Elasticsearch plugin for Craft CMS + * + * Elasticsearch JS + * + * @author Alban Jubert + * @copyright Copyright (c) 2018 Alban Jubert + * @link https://www.lahautesociete.com + * @package Elasticsearch + * @since 1.0.0 + */ diff --git a/src/assetbundles/elasticsearchcpsection/ElasticsearchCPSectionAsset.php b/src/assetbundles/elasticsearchcpsection/ElasticsearchCPSectionAsset.php new file mode 100755 index 0000000..dca8238 --- /dev/null +++ b/src/assetbundles/elasticsearchcpsection/ElasticsearchCPSectionAsset.php @@ -0,0 +1,65 @@ +sourcePath = "@lhs/elasticsearch/assetbundles/elasticsearchcpsection/dist"; + + // define the dependencies + $this->depends = [ + CpAsset::class, + ]; + + // define the relative path to CSS/JS files that should be registered with the page + // when this asset bundle is registered + $this->js = [ + 'js/Elasticsearch.js', + ]; + + $this->css = [ + 'css/Elasticsearch.css', + ]; + + parent::init(); + } +} diff --git a/src/assetbundles/elasticsearchcpsection/dist/css/Elasticsearch.css b/src/assetbundles/elasticsearchcpsection/dist/css/Elasticsearch.css new file mode 100755 index 0000000..bc97205 --- /dev/null +++ b/src/assetbundles/elasticsearchcpsection/dist/css/Elasticsearch.css @@ -0,0 +1,11 @@ +/** + * Elasticsearch plugin for Craft CMS + * + * Elasticsearch Field CSS + * + * @author Alban Jubert + * @copyright Copyright (c) 2018 Alban Jubert + * @link https://www.lahautesociete.com + * @package Elasticsearch + * @since 1.0.0 + */ diff --git a/src/assetbundles/elasticsearchcpsection/dist/img/icon.svg b/src/assetbundles/elasticsearchcpsection/dist/img/icon.svg new file mode 100755 index 0000000..07d2b67 --- /dev/null +++ b/src/assetbundles/elasticsearchcpsection/dist/img/icon.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assetbundles/elasticsearchcpsection/dist/js/Elasticsearch.js b/src/assetbundles/elasticsearchcpsection/dist/js/Elasticsearch.js new file mode 100755 index 0000000..37d868f --- /dev/null +++ b/src/assetbundles/elasticsearchcpsection/dist/js/Elasticsearch.js @@ -0,0 +1,11 @@ +/** + * Elasticsearch plugin for Craft CMS + * + * Elasticsearch Field JS + * + * @author Alban Jubert + * @copyright Copyright (c) 2018 Alban Jubert + * @link https://www.lahautesociete.com + * @package Elasticsearch + * @since 1.0.0 + */ diff --git a/src/config.php b/src/config.php new file mode 100755 index 0000000..877eea7 --- /dev/null +++ b/src/config.php @@ -0,0 +1,27 @@ + '/
(.*?)<\/main>/s' +]; diff --git a/src/console/controllers/ElasticsearchController.php b/src/console/controllers/ElasticsearchController.php new file mode 100755 index 0000000..2f8e1ce --- /dev/null +++ b/src/console/controllers/ElasticsearchController.php @@ -0,0 +1,82 @@ +elasticsearch->testConnection() === true) { + Craft::$app->session->setFlash('notice', Craft::t('elasticsearch', 'Successfully connected to {http_address}', ['http_address' => $this->module->settings->http_address])); + } + else { + Craft::$app->session->setFlash('error', Craft::t('elasticsearch', 'Could not establish connection with {http_address}', ['http_address' => $this->module->settings->http_address])); + } + + return $this->redirect(UrlHelper::cpUrl('elasticsearch')); + } + + public function actionReindexAll() + { + Elasticsearch::$plugin->elasticsearch->reindexAll(); + Craft::$app->session->setFlash('notice', Craft::t('elasticsearch', 'Elasticsearch indexing in progress...')); + return $this->redirect(UrlHelper::cpUrl('elasticsearch')); + } +} diff --git a/src/icon-mask.svg b/src/icon-mask.svg new file mode 100644 index 0000000..e77572c --- /dev/null +++ b/src/icon-mask.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/icon.svg b/src/icon.svg new file mode 100644 index 0000000..eed443e --- /dev/null +++ b/src/icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/jobs/IndexElement.php b/src/jobs/IndexElement.php new file mode 100755 index 0000000..1c1779e --- /dev/null +++ b/src/jobs/IndexElement.php @@ -0,0 +1,97 @@ +getQueue(); + * $jobId = $queue->push(new IndexEntryJob([ + * 'description' => Craft::t('generic', 'This overrides the default description'), + * 'someAttribute' => 'someValue', + * ])); + * + * The key/value pairs that you pass in to the job will set the public properties + * for that object. Thus whatever you set 'someAttribute' to will cause the + * public property $someAttribute to be set in the job. + * + * Passing in 'description' is optional, and only if you want to override the default + * description. + * + * More info: https://github.com/yiisoft/yii2-queue + * + * @author Alban Jubert + * @package Generic + * @since 1.0.0 + */ +class IndexElement extends BaseJob +{ + // Public Properties + // ========================================================================= + + public $siteId; + /** + * Element ID to be indexed + * + * @var int + */ + public $elementId; + + // Public Methods + // ========================================================================= + + /** + * When the Queue is ready to run your job, it will call this method. + * You don't need any steps or any other special logic handling, just do the + * jobs that needs to be done here. + * + * More info: https://github.com/yiisoft/yii2-queue + */ + public function execute($queue) + { + $site = Craft::$app->getSites()->getSiteById($this->siteId); + Craft::$app->getSites()->setCurrentSite($site); + $element = Entry::findOne($this->elementId); + Craft::debug(VarDumper::dumpAsString($element),__METHOD__); + if($element) { + $site = Craft::$app->getSites()->getSiteById($element->siteId); + Craft::$app->getSites()->setCurrentSite($site); + Elasticsearch::$plugin->elasticsearch->indexEntry($element); + } + } + + // Protected Methods + // ========================================================================= + + /** + * Returns a default description for [[getDescription()]], if [[description]] isn’t set. + * + * @return string The default task description + */ + protected function defaultDescription(): string + { + return Craft::t('elasticsearch', 'Index a page in Elasticsearch'); + } +} diff --git a/src/models/Settings.php b/src/models/Settings.php new file mode 100755 index 0000000..1e1323a --- /dev/null +++ b/src/models/Settings.php @@ -0,0 +1,70 @@ + Craft::t("elasticsearch", "Host is required")], + ['http_address', 'string'], + ['http_address', 'default', 'value' => 'elasticsearch:9200'], + [['auth_username', 'auth_password'], 'string'], + [['auth_username', 'auth_password'], 'trim'] + ]; + } +} diff --git a/src/records/ElasticsearchRecord.php b/src/records/ElasticsearchRecord.php new file mode 100644 index 0000000..d39528e --- /dev/null +++ b/src/records/ElasticsearchRecord.php @@ -0,0 +1,230 @@ +getIsNewRecord()) { + $this->delete(); // pipeline in not supported by Document Update API :( + } + return $this->insert($runValidation, $attributeNames, ["pipeline" => "attachment"]); + } + + /** + * Check if the Elasticsearch index already exists or created it if not + * @throws \yii\base\InvalidConfigException + * @throws \yii\elasticsearch\Exception + */ + protected static function checkIndex() + { + $db = static::getDb(); + $command = $db->createCommand(); + if (!$command->indexExists(static::index())) { + self::createIndex(); + } + } + + /** + * @return mixed|\yii\elasticsearch\Connection + */ + public static function getDb() + { + return Elasticsearch::$plugin->connection; + } + + /** + * @return string + * @throws InvalidConfigException + */ + public static function index() + { + if (null === static::$siteId) { + throw new InvalidConfigException('siteId was not set'); + } + return parent::index() . '_' . static::$siteId; + } + + /** + * Create this model's index + * @param bool $force + * @throws \yii\base\InvalidConfigException + * @throws \yii\elasticsearch\Exception + */ + public static function createIndex($force = false) + { + $db = static::getDb(); + $command = $db->createCommand(); + + if ($force === true && $command->indexExists(static::index())) { + self::deleteIndex(); + } + + $db->delete("_ingest/pipeline/attachment"); + $db->put("_ingest/pipeline/attachment", [], Json::encode([ + "description" => "Extract attachment information", + "processors" => [ + [ + "attachment" => [ + "field" => "content", + "target_field" => "attachment", + "indexed_chars" => -1, + "ignore_missing" => true + ], + "remove" => [ + "field" => "content" + ] + ], + ] + ])); + $command->createIndex(static::index(), [ + 'mappings' => static::mapping(), + ]); + } + + /** + * Delete this model's index + */ + public static function deleteIndex() + { + $db = static::getDb(); + $command = $db->createCommand(); + if ($command->indexExists(static::index())) { + $command->deleteIndex(static::index(), static::type()); + } + } + + public static function mapping() + { + $analyzer = self::siteAnalyzer(); + return [ + static::type() => [ + 'properties' => [ + 'title' => [ + 'type' => 'text', + 'analyzer' => $analyzer, + 'store' => true + ], + 'section' => [ + 'type' => 'keyword', + 'store' => true + ], + 'url' => [ + 'type' => 'text', + 'store' => true + ], + 'content' => [ + 'type' => 'text', + 'analyzer' => $analyzer, + 'store' => true + ], + 'attachment' => [ + 'properties' => [ + 'content' => [ + 'type' => 'text', + 'analyzer' => $analyzer, + 'store' => true + ] + ] + ] + ] + ] + ]; + } + + /** + * Try to guess the best Elasticsearch analyze for the current site language + * @return string + */ + public static function siteAnalyzer() + { + $analyzer = 'standard'; // Default analyzer + $availableAnalyzers = [ + 'ar' => 'arabic', + 'hy' => 'armenian', + 'eu' => 'basque', + 'bn' => 'bengali', + 'pt-BR' => 'brazilian', + 'bg' => 'bulgarian', + 'ca' => 'catalan', + 'cs' => 'czech', + 'da' => 'danish', + 'nl' => 'dutch', + 'pl' => 'stempel', // analysis-stempel plugin needed + 'en' => 'english', + 'fi' => 'finnish', + 'fr' => 'french', + 'gl' => 'galician', + 'de' => 'german', + 'el' => 'greek', + 'hi' => 'hindi', + 'hu' => 'hungarian', + 'id' => 'indonesian', + 'ga' => 'irish', + 'it' => 'italian', + 'ja' => 'cjk', + 'ko' => 'cjk', + 'lv' => 'latvian', + 'lt' => 'lithuanian', + 'nb' => 'norwegian', + 'fa' => 'persian', + 'pt' => 'portuguese', + 'ro' => 'romanian', + 'ru' => 'russian', + //sorani, Kurdish language is not part of the Craft locals... + // 'sk' no analyzer available at this time + 'es' => 'spanish', + 'sv' => 'swedish', + 'tr' => 'turkish', + 'th' => 'thai', + 'zh' => 'cjk' //Chinese + ]; + $siteLanguage = Craft::$app->language; + if (array_key_exists($siteLanguage, $availableAnalyzers)) { + $analyzer = $availableAnalyzers[$siteLanguage]; + } else { + $localParts = explode('-', Craft::$app->language); + $siteLanguage = $localParts[0]; + if (array_key_exists($siteLanguage, $availableAnalyzers)) { + $analyzer = $availableAnalyzers[$siteLanguage]; + } + } + return $analyzer; + } + + /** + * @inheritdoc + */ + public function attributes() + { + return ['title', 'url', 'section', 'content']; + } + +} \ No newline at end of file diff --git a/src/services/Elasticsearch.php b/src/services/Elasticsearch.php new file mode 100755 index 0000000..51ca528 --- /dev/null +++ b/src/services/Elasticsearch.php @@ -0,0 +1,198 @@ +elasticsearch->testConnection() + * + * @return boolean + */ + public function testConnection() + { + $connection = Es::$plugin->connection; + try { + $connection->open(); + return true; + } catch (\Exception $e) { + return false; + } + } + + /** + * Recreate all Elasticsearch indexes and reindex every Craft entries + */ + public function reindexAll() + { + foreach (Craft::$app->getSites()->getAllSites() as $site) { + Craft::$app->getSites()->setCurrentSite($site); + $this->reindexBySiteId($site->id); + } + } + + /** + * Recreate Elasticsearch index for a given siteId + * @param int $siteId + * @throws Exception + * @throws ServerErrorHttpException + * @throws \Twig_Error_Loader + * @throws \yii\base\Exception + * @throws \yii\base\InvalidConfigException + */ + public function reindexBySiteId($siteId) + { + $esClass = new ElasticsearchRecord(); + $esClass::$siteId = $siteId; + $esClass::deleteIndex(); + /** @var Entry $element */ + foreach (Entry::find()->each() as $element) { + if ($element->enabled) { + Craft::$app->queue->push(new IndexElement([ + 'siteId' => $siteId, + 'elementId' => $element->id, + ])); + } + } + } + + public function search($query) + { + + } + + /** + * Index a given entry into Elasticsearch + * @param Entry $entry + * @throws Exception + * @throws ServerErrorHttpException + * @throws \Twig_Error_Loader + * @throws \yii\base\Exception + * @throws \yii\base\InvalidConfigException + */ + public function indexEntry(Entry $entry) + { + if ($entry->status == Entry::STATUS_LIVE && $entry->enabledForSite && $entry->hasContent()) { + Craft::info( + Craft::t( + 'elasticsearch', + 'Indexing entry {url}', + ['url' => $entry->url] + ), + __METHOD__ + ); + + $esClass = new ElasticsearchRecord(); + $esClass::$siteId = $entry->siteId; + $esRecord = $esClass::findOne($entry->id); + + if (empty($esRecord)) { + $esRecord = new $esClass(); + $esClass::$siteId = $entry->siteId; + $esRecord->setPrimaryKey($entry->id); + } + $esRecord->title = $entry->title; + $esRecord->url = $entry->url; + + Craft::$app->view->setTemplateMode(View::TEMPLATE_MODE_SITE); + + $sectionSiteSettings = $entry->getSection()->getSiteSettings(); + + $site = Craft::$app->getSites()->getSiteById($entry->siteId); + + if (!$site) { + throw new ServerErrorHttpException('Invalid site ID: ' . $entry->siteId); + } + Craft::$app->getSites()->setCurrentSite($site); + + Craft::$app->language = $site->language; + + if (!$entry->postDate) { + $entry->postDate = new DateTime(); + } + + // Have this entry override any freshly queried entries with the same ID/site ID + Craft::$app->getElements()->setPlaceholderElement($entry); + + Craft::$app->view->getTwig()->disableStrictVariables(); + + $html = trim(Craft::$app->view->renderTemplate($sectionSiteSettings[$entry->siteId]->template, [ + 'entry' => $entry + ])); + + $body = null; + if (preg_match(Es::$plugin->settings->content_pattern, $html, $body)) { + $html = '' . trim($body[1]); + } + + $esRecord->content = base64_encode(trim($html)); + + Craft::$app->view->setTemplateMode(View::TEMPLATE_MODE_CP); + + + if (!$esRecord->save()) { + throw new Exception("Could not save elasticsearch record", $esRecord->errors); + } + } + } + + public function deleteEntry(Entry $entry) + { + Craft::info( + Craft::t( + 'elasticsearch', + 'Deleting entry {url}', + ['url' => $entry->url] + ), + __METHOD__ + ); + + $esClass = new ElasticsearchRecord(); + $esClass::$siteId = $entry->siteId; + $esRecord = $esClass::findOne($entry->id); + if ($esRecord) { + $esRecord->delete(); + } + } +} diff --git a/src/templates/index.twig b/src/templates/index.twig new file mode 100755 index 0000000..e6f1811 --- /dev/null +++ b/src/templates/index.twig @@ -0,0 +1,52 @@ +{# @var craft \craft\web\twig\variables\CraftVariable #} +{# +/** + * Elasticsearch plugin for Craft CMS 3.x + * + * Elasticsearch index.twig + * + * @author Alban Jubert + * @copyright Copyright (c) 2018 Alban Jubert + * @link https://www.lahautesociete.com + * @package Elasticsearch + * @since 1.0.0 + */ +#} + +{% extends "_layouts/cp" %} +{% import "_includes/forms" as forms %} + +{% do view.registerAssetBundle("lhs\\elasticsearch\\assetbundles\\elasticsearch\\ElasticsearchAsset") %} +{% do view.registerAssetBundle("lhs\\elasticsearch\\assetbundles\\elasticsearchcpsection\\ElasticsearchCPSectionAsset") %} + +{# Link for the ? icon at the bottom of the page #} +{% set docsUrl = "https://github.com/juban/elasticsearch/blob/master/README.md" %} + +{# The title of this CP section #} +{% set title = "Elasticsearch" %} + +{# The URL to this plugin's base CP section #} +{% set pluginCpUrl = url('elasticsearch') %} + +{% set pluginCpUrlTestConnection = url('elasticsearch/test-connection') %} +{% set pluginCpUrlReindexAll = url('elasticsearch/reindex-all') %} + +{# Get a URL to an image in our AssetBundle #} +{% set iconUrl = view.getAssetManager().getPublishedUrl('@lhs/elasticsearch/assetbundles/elasticsearchcpsection/dist', true) ~ '/img/icon.svg' %} + +{% block actionButton %} + +{% endblock %} + +{# The content of the CP Section#} +{% set content %} + +{% endset %} diff --git a/src/templates/settings.twig b/src/templates/settings.twig new file mode 100755 index 0000000..dc097c3 --- /dev/null +++ b/src/templates/settings.twig @@ -0,0 +1,48 @@ +{# @var craft \craft\web\twig\variables\CraftVariable #} +{# +/** + * Elasticsearch plugin for Craft CMS 3.x + * + * Elasticsearch Settings.twig + * + * @author Alban Jubert + * @copyright Copyright (c) 2018 Alban Jubert + * @link https://www.lahautesociete.com + * @package Elasticsearch + * @since 1.0.0 + */ +#} + +{% import "_includes/forms" as forms %} + +{% do view.registerAssetBundle("lhs\\elasticsearch\\assetbundles\\elasticsearch\\ElasticsearchAsset") %} + +{{ forms.textField({ + first: true, + label: 'Host'|t('elasticsearch'), + instructions: 'Elasticsearch hostname or IP and port (ie. elasticsearch:9200)'|t('elasticsearch'), + placeholder: '127.0.0.1:9200', + id: 'http_address', + name: 'http_address', + value: settings['http_address'], + errors: settings.getErrors('http_address')} +) }} + +
+ +{{ forms.textField({ + label: 'Auth username'|t('elasticsearch'), + id: 'auth_username', + name: 'auth_username', + value: settings['auth_username'], + errors: settings.getErrors('auth_username')} +) }} + +{{ forms.textField({ + label: 'Auth password'|t('elasticsearch'), + id: 'auth_password', + name: 'auth_password', + value: settings['auth_password'], + errors: settings.getErrors('auth_password')} +) }} + diff --git a/src/translations/en/elasticsearch.php b/src/translations/en/elasticsearch.php new file mode 100755 index 0000000..2f797d0 --- /dev/null +++ b/src/translations/en/elasticsearch.php @@ -0,0 +1,25 @@ + 'Elasticsearch plugin loaded', +]; diff --git a/src/translations/fr/elasticsearch.php b/src/translations/fr/elasticsearch.php new file mode 100755 index 0000000..9da845d --- /dev/null +++ b/src/translations/fr/elasticsearch.php @@ -0,0 +1,35 @@ + 'Hôte', + 'Auth username' => 'Nom d\'utilisateur', + 'Auth password' => 'Mot de passe', + 'Host' => 'Hôte', + 'Host is required' => 'L\'hôte est obligatoire', + 'Test connection' => 'Tester la connexion', + 'Reindex all' => 'Tout réindexer', + 'Successfully connected to {http_address}' => 'Connecté à {http_address} avec succès', + 'Could not establish connection with {http_address}' => 'Impossible d\'établir la connexion avec {http_address}', + 'Index a page in Elasticsearch' => 'Indexation d\'une page dans Elasticsearch', + 'Elasticsearch indexing in progress...' => 'Indexation Elasticsearch en cours...' +]; diff --git a/src/variables/ElasticsearchVariable.php b/src/variables/ElasticsearchVariable.php new file mode 100755 index 0000000..4f8a13a --- /dev/null +++ b/src/variables/ElasticsearchVariable.php @@ -0,0 +1,56 @@ +