From 7b4f3a4e8db0fc894a67da23a328d39239c06cb6 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Fri, 16 Sep 2016 09:51:01 +0200 Subject: [PATCH 01/38] Fix showing comments older than one week in the "latest comments" view --- CHANGELOG.md | 6 ++++++ comments.php | 5 ----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02466e2..a5fa7d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# v1.2.x +## 09/16/2016 + +1. [](#bugfix) + * Fix [#37](https://github.com/getgrav/grav-plugin-comments/issues/37) showing comments older than one week in the "latest comments" view + # v1.2.4 ## 09/15/2016 diff --git a/comments.php b/comments.php index 0629771..1363a8b 100644 --- a/comments.php +++ b/comments.php @@ -312,11 +312,6 @@ class CommentsPlugin extends Plugin for ($i = 0; $i < count($data['comments']); $i++) { $commentTimestamp = \DateTime::createFromFormat('D, d M Y H:i:s', $data['comments'][$i]['date'])->getTimestamp(); - $sevenDaysAgo = time() - (7 * 24 * 60 * 60); - - if ($commentTimestamp < $sevenDaysAgo) { - continue; - } $data['comments'][$i]['pageTitle'] = $data['title']; $data['comments'][$i]['filePath'] = $file->filePath; From 201ffedd18f16c7b8131a83dd9601dd4524c2770 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Fri, 16 Sep 2016 09:51:44 +0200 Subject: [PATCH 02/38] Prepare release --- CHANGELOG.md | 2 +- blueprints.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5fa7d0..58f5603 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# v1.2.x +# v1.2.5 ## 09/16/2016 1. [](#bugfix) diff --git a/blueprints.yaml b/blueprints.yaml index 41ce93a..08a112b 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -1,5 +1,5 @@ name: Comments -version: 1.2.4 +version: 1.2.5 description: Adds a commenting functionality to your site icon: comment author: From fbf7ee0bc8b2563cae3e8570dd4269adc887e525 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Wed, 28 Sep 2016 09:39:53 -0600 Subject: [PATCH 03/38] Use existing `Utils::startsWith()` method --- comments.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/comments.php b/comments.php index 1363a8b..c6ee298 100644 --- a/comments.php +++ b/comments.php @@ -9,6 +9,7 @@ use Grav\Common\Page\Pages; use Grav\Common\Plugin; use Grav\Common\Filesystem\RecursiveFolderFilterIterator; use Grav\Common\User\User; +use Grav\Common\Utils; use RocketTheme\Toolbox\File\File; use RocketTheme\Toolbox\Event\Event; use Symfony\Component\Yaml\Yaml; @@ -76,13 +77,6 @@ class CommentsPlugin extends Plugin $this->grav['twig']->comments = $this->fetchComments(); } - /** - * Determine if $haystack starts with $needle. Credit: http://stackoverflow.com/a/10473026/205039 - */ - private function startsWith($haystack, $needle) { - return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== FALSE; - } - /** * Determine if the plugin should be enabled based on the enable_on_routes and disable_on_routes config options */ @@ -99,7 +93,7 @@ class CommentsPlugin extends Plugin $this->enable = true; } else { foreach($enable_on_routes as $route) { - if ($this->startsWith($path, $route)) { + if (Utils::startsWith($path, $route)) { $this->enable = true; break; } From 4e88431863bbe047abcbeebcce378fdb254843cc Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Mon, 9 Jan 2017 10:12:23 +0100 Subject: [PATCH 04/38] Fix #41 using Comments in a Gantry-powered theme did not escape the comment form token correctly --- CHANGELOG.md | 8 ++++++++ templates/partials/comments.html.twig | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58f5603..a917685 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# v1.2.x +## 01/xx/2017 + +1. [](#improved) + Use existing `Utils::startsWith()` method +1. [](#bugfix) + * Fix [#41](https://github.com/getgrav/grav-plugin-comments/issues/41) using Comments in a Gantry-powered theme did not escape the comment form token correctly + # v1.2.5 ## 09/16/2016 diff --git a/templates/partials/comments.html.twig b/templates/partials/comments.html.twig index 6872180..7528223 100644 --- a/templates/partials/comments.html.twig +++ b/templates/partials/comments.html.twig @@ -35,7 +35,7 @@ {% endfor %} - {{ nonce_field('form', 'form-nonce') }} + {{ nonce_field('form', 'form-nonce')|raw }}
{{ form.message }}
From cf3284e540187f8ae450f3cc7238c9d8302883c5 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Mon, 9 Jan 2017 10:13:13 +0100 Subject: [PATCH 05/38] Prepare release --- CHANGELOG.md | 4 ++-- blueprints.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a917685..f72cf4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ -# v1.2.x -## 01/xx/2017 +# v1.2.6 +## 01/09/2017 1. [](#improved) Use existing `Utils::startsWith()` method diff --git a/blueprints.yaml b/blueprints.yaml index 08a112b..4de7704 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -1,5 +1,5 @@ name: Comments -version: 1.2.5 +version: 1.2.6 description: Adds a commenting functionality to your site icon: comment author: From d8e1e2dfe2de1d3e37ca8fada4e5a84d8d83297e Mon Sep 17 00:00:00 2001 From: tidiview Date: Tue, 7 Feb 2017 13:59:35 +0100 Subject: [PATCH 06/38] =?UTF-8?q?created=20a=20japanese=20translation=20?= =?UTF-8?q?=E2=99=AA=20(#43)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- languages.yaml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/languages.yaml b/languages.yaml index 61cc568..812f22d 100644 --- a/languages.yaml +++ b/languages.yaml @@ -130,6 +130,28 @@ it: EMAIL_NEW_COMMENT_SUBJECT: "[Nuovo commento] da {{ form.value.name|e }}" THANK_YOU_MESSAGE: "Grazie per il tuo commento!" +ja: + PLUGIN_COMMENTS: + ADD_COMMENT: コメントを追加する + COMMENTS: コメント + EMAIL_NOT_CONFIGURED: メールアドレスは設定さていません + NEW_COMMENT_EMAIL_SUBJECT: '%1$sについて新しいコメント' + NEW_COMMENT_EMAIL_BODY: '

新しいコメントが%1$sについて%3$sから(%4$s)書かれた.

ページー : %2$s

文書 : %5$s

' + EMAIL_FOOTER: '' + NAME: 名前 : + EMAIL: メールアドレス : + WRITTEN_ON: に書かれた + BY: に + NAME_LABEL: "名前" + NAME_PLACEHOLDER: "お名前を" + EMAIL_LABEL: "メールアドレスを" + EMAIL_PLACEHOLDER: "ご自分のメールアドレスをここに..." + MESSAGE_LABEL: "コメント" + MESSAGE_PLACEHOLDER: "コメントをここに" + SUBMIT_COMMENT_BUTTON_TEXT: "送信する" + EMAIL_NEW_COMMENT_SUBJECT: "[新しいコメント]、 {{ form.value.name|e }}から" + THANK_YOU_MESSAGE: "コメントを書いてくださいましてありがとうございました!" + pl: PLUGIN_COMMENTS: ADD_COMMENT: Dodaj komentarz From b3da8123a9b8ce55d271e370a00e58752ec0d377 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Tue, 7 Feb 2017 14:01:07 +0100 Subject: [PATCH 07/38] Changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f72cf4e..97be244 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# v1.2.7 +## xx/xx/2017 + +1. [](#improved) + Added Japanese translation + # v1.2.6 ## 01/09/2017 From fac87e9bb5b9a32d9e9d099197bcde9860984ba1 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Sun, 26 Feb 2017 22:05:26 +0100 Subject: [PATCH 08/38] Move captcha over email, fix #45 --- CHANGELOG.md | 5 +++-- comments.yaml | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97be244..f4670b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,14 @@ ## xx/xx/2017 1. [](#improved) - Added Japanese translation + * Added Japanese translation + * Move captcha over email [#45](https://github.com/getgrav/grav-plugin-comments/issues/45) # v1.2.6 ## 01/09/2017 1. [](#improved) - Use existing `Utils::startsWith()` method + * Use existing `Utils::startsWith()` method 1. [](#bugfix) * Fix [#41](https://github.com/getgrav/grav-plugin-comments/issues/41) using Comments in a Gantry-powered theme did not escape the comment form token correctly diff --git a/comments.yaml b/comments.yaml index 05a16eb..c96565b 100644 --- a/comments.yaml +++ b/comments.yaml @@ -65,11 +65,11 @@ form: value: PLUGIN_COMMENTS.SUBMIT_COMMENT_BUTTON_TEXT process: +# - captcha: +# recatpcha_secret: ej32oiej23oiej32oijeoi32jeio32je - email: subject: PLUGIN_COMMENTS.EMAIL_NEW_COMMENT_SUBJECT body: "{% include 'forms/data.html.twig' %}" -# - captcha: -# recatpcha_secret: ej32oiej23oiej32oijeoi32jeio32je - addComment: - message: PLUGIN_COMMENTS.THANK_YOU_MESSAGE - reset: true From f1b1f74348671d3c37f037ce3c86a68219d116f0 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Fri, 14 Apr 2017 16:19:33 +0200 Subject: [PATCH 09/38] Fix type in example config --- comments.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/comments.yaml b/comments.yaml index c96565b..5c09966 100644 --- a/comments.yaml +++ b/comments.yaml @@ -53,7 +53,7 @@ form: # - name: g-recaptcha-response # label: Captcha # type: captcha -# recatpcha_site_key: e32iojeoi32jeoi32jeoij32oiej32oiej3 +# recaptcha_site_key: e32iojeoi32jeoi32jeoij32oiej32oiej3 # recaptcha_not_validated: 'Captcha not valid!' # validate: # required: true @@ -66,7 +66,7 @@ form: process: # - captcha: -# recatpcha_secret: ej32oiej23oiej32oijeoi32jeio32je +# recaptcha_secret: ej32oiej23oiej32oijeoi32jeio32je - email: subject: PLUGIN_COMMENTS.EMAIL_NEW_COMMENT_SUBJECT body: "{% include 'forms/data.html.twig' %}" From 228ac73ba8024bc85a72e3587c28b83f65049b0e Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Thu, 11 May 2017 19:42:43 +0200 Subject: [PATCH 10/38] Fix comment form processing --- CHANGELOG.md | 2 ++ comments.php | 2 +- templates/partials/comments.html.twig | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4670b1..3415666 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ 1. [](#improved) * Added Japanese translation * Move captcha over email [#45](https://github.com/getgrav/grav-plugin-comments/issues/45) +1. [](#bugfix) + * Fix comment form processing # v1.2.6 ## 01/09/2017 diff --git a/comments.php b/comments.php index c6ee298..7a59543 100644 --- a/comments.php +++ b/comments.php @@ -187,7 +187,7 @@ class CommentsPlugin extends Plugin switch ($action) { case 'addComment': - $post = !empty($_POST) ? $_POST : []; + $post = isset($_POST['data']) ? $_POST['data'] : []; $lang = filter_var(urldecode($post['lang']), FILTER_SANITIZE_STRING); $path = filter_var(urldecode($post['path']), FILTER_SANITIZE_STRING); diff --git a/templates/partials/comments.html.twig b/templates/partials/comments.html.twig index 7528223..e44edef 100644 --- a/templates/partials/comments.html.twig +++ b/templates/partials/comments.html.twig @@ -1,4 +1,5 @@ {% if grav.twig.enable_comments_plugin %} + {% set scope = scope ?: 'data.' %}

{{'PLUGIN_COMMENTS.ADD_COMMENT'|t}}

From 42ff5564207bcb6983c7f352e1b91940750f0864 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Fri, 12 May 2017 14:40:51 +0200 Subject: [PATCH 11/38] Fix issue with scope for autofilled values --- CHANGELOG.md | 1 + templates/partials/comments.html.twig | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3415666..f263b69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Move captcha over email [#45](https://github.com/getgrav/grav-plugin-comments/issues/45) 1. [](#bugfix) * Fix comment form processing + * Fix issue with scope for autofilled values # v1.2.6 ## 01/09/2017 diff --git a/templates/partials/comments.html.twig b/templates/partials/comments.html.twig index e44edef..29d79b9 100644 --- a/templates/partials/comments.html.twig +++ b/templates/partials/comments.html.twig @@ -14,9 +14,9 @@ {% endif %} {% if config.plugins.login.enabled and grav.user.authenticated %} {% if field.name == 'name' %} - + {% elseif field.name == 'email' %} - + {% else %}
{% include "forms/fields/#{field.type}/#{field.type}.html.twig" %} From 0a5a7a2e9ca23c2c7b687afbd797891c086cbeda Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Fri, 12 May 2017 14:55:49 +0200 Subject: [PATCH 12/38] Prepare release --- CHANGELOG.md | 2 +- blueprints.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f263b69..f2d5015 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ # v1.2.7 -## xx/xx/2017 +## 05/12/2017 1. [](#improved) * Added Japanese translation diff --git a/blueprints.yaml b/blueprints.yaml index 4de7704..1daf30b 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -1,5 +1,5 @@ name: Comments -version: 1.2.6 +version: 1.2.7 description: Adds a commenting functionality to your site icon: comment author: From e8417c6a0ec0a6b6a95e6155d9b8f2ffcf99ee60 Mon Sep 17 00:00:00 2001 From: Thorsten Witteler Date: Thu, 19 Oct 2017 17:42:20 +0200 Subject: [PATCH 13/38] add index file to user data add data files to page folder --- comments.php | 120 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 88 insertions(+), 32 deletions(-) diff --git a/comments.php b/comments.php index 7a59543..df1f915 100644 --- a/comments.php +++ b/comments.php @@ -1,17 +1,19 @@ grav['page']; - if (!$page) { - return; - } - - if ($this->enable) { - $header = $page->header(); - if (!isset($header->form)) { - $header->form = $this->grav['config']->get('plugins.comments.form'); - $page->header($header); - } - } - } - /** * Add the comment form information to the page header dynamically * @@ -117,7 +95,6 @@ class CommentsPlugin extends Plugin $this->enable([ 'onFormProcessed' => ['onFormProcessed', 0], 'onFormPageHeaderProcessed' => ['onFormPageHeaderProcessed', 0], - 'onPageInitialized' => ['onPageInitialized', 10], 'onTwigSiteVariables' => ['onTwigSiteVariables', 0] ]); } @@ -208,13 +185,90 @@ class CommentsPlugin extends Plugin $language = $this->grav['language']; $lang = $language->getLanguage(); + /******************************/ + /** store comments with page **/ + /******************************/ + $page = $this->grav['page']; + $localfilename = $page->path() . '/comments.yaml'; + $localfile = CompiledYamlFile::instance($localfilename); + if (file_exists($localfilename)) { + $data = $localfile->content(); + $data['autoincrement']++; + $data['comments'][] = [ + 'id' => $data['autoincrement'], + 'parent' => 0, + 'lang' => $lang, + 'title' => $title, + 'text' => $text, + 'date' => date('D, d M Y H:i:s', time()), + 'author' => $name, + 'email' => $email + ]; + } else { + $data = array( + 'autoincrement' => 1, + 'comments' => array([ + 'id' => 1, + 'parent' => 0, + 'lang' => $lang, + 'title' => $title, + 'text' => $text, + 'date' => date('D, d M Y H:i:s', time()), + 'author' => $name, + 'email' => $email + ]) + ); + } + $localfile->save($data); + $localid = $data['autoincrement']; + $data = null; + /**********************************/ + /** store comments in index file **/ + /**********************************/ + $indexfilename = DATA_DIR . 'comments/index.yaml'; + $indexfile = CompiledYamlFile::instance($indexfilename); + if (file_exists($indexfilename)) { + $data = $indexfile->content(); + $data['comments'][] = [ + 'page' => $page->route(), + 'id' => $localid, + 'parent' => 0, + 'lang' => $lang, + 'title' => $title, + 'text' => $text, + 'date' => date('D, d M Y H:i:s', time()), + 'author' => $name, + 'email' => $email + ]; + } else { + $data = array( + 'comments' => array([ + 'page' => $page->route(), + 'id' => $localid, + 'parent' => 0, + 'lang' => $lang, + 'title' => $title, + 'text' => $text, + 'date' => date('D, d M Y H:i:s', time()), + 'author' => $name, + 'email' => $email + ]) + ); + } + $indexfile->save($data); + $data = null; + /**************************************/ + /** store comments in old data files **/ + /** TODO: remove as soon as admin **/ + /** panel uses new index file **/ + /**************************************/ $filename = DATA_DIR . 'comments'; $filename .= ($lang ? '/' . $lang : ''); $filename .= $path . '.yaml'; - $file = File::instance($filename); + $file = CompiledYamlFile::instance($filename); if (file_exists($filename)) { - $data = Yaml::parse($file->content()); + $data = $file->content(); $data['comments'][] = [ 'text' => $text, @@ -235,7 +289,7 @@ class CommentsPlugin extends Plugin ); } - $file->save(Yaml::dump($data)); + $file->save($data); //clear cache $this->grav['cache']->delete($this->comments_cache_id); @@ -380,14 +434,16 @@ class CommentsPlugin extends Plugin private function getDataFromFilename($fileRoute) { //Single item details - $fileInstance = File::instance(DATA_DIR . 'comments/' . $fileRoute); + //$fileInstance = CompiledYamlFile::instance(DATA_DIR . 'comments/' . $fileRoute); + //Use comment file in page folder + $fileInstance = CompiledYamlFile::instance($this->grav['page']->path() . '/comments.yaml'); if (!$fileInstance->content()) { //Item not found return; } - return Yaml::parse($fileInstance->content()); + return $fileInstance->content(); } /** From 9387fba6ed6d39ca17f8567c916203595bb9a5ad Mon Sep 17 00:00:00 2001 From: Thorsten Witteler Date: Thu, 19 Oct 2017 18:53:40 +0200 Subject: [PATCH 14/38] nested template --- templates/partials/comments.form.html.twig | 38 +++++++++++ templates/partials/comments.html.twig | 75 ++++++++-------------- 2 files changed, 64 insertions(+), 49 deletions(-) create mode 100644 templates/partials/comments.form.html.twig diff --git a/templates/partials/comments.form.html.twig b/templates/partials/comments.form.html.twig new file mode 100644 index 0000000..dab77dc --- /dev/null +++ b/templates/partials/comments.form.html.twig @@ -0,0 +1,38 @@ +

{{'PLUGIN_COMMENTS.ADD_COMMENT'|t}}

+
+ + {% for field in grav.config.plugins.comments.form.fields %} + {% set value = form.value(field.name) %} + {% if field.evaluateDefault %} + {% set value = evaluate(field.evaluateDefault) %} + {% endif %} + {% if config.plugins.login.enabled and grav.user.authenticated %} + {% if field.name == 'name' %} + + {% elseif field.name == 'email' %} + + {% else %} +
+ {% include "forms/fields/#{field.type}/#{field.type}.html.twig" %} +
+ {% endif %} + {% else %} +
+ {% include "forms/fields/#{field.type}/#{field.type}.html.twig" %} +
+ {% endif %} + {% endfor %} + {% include "forms/fields/formname/formname.html.twig" %} + +
+ {% for button in grav.config.plugins.comments.form.buttons %} + + {% endfor %} +
+ + {{ nonce_field('form', 'form-nonce')|raw }} +
+ +
{{ form.message }}
diff --git a/templates/partials/comments.html.twig b/templates/partials/comments.html.twig index 29d79b9..3eeb423 100644 --- a/templates/partials/comments.html.twig +++ b/templates/partials/comments.html.twig @@ -1,60 +1,37 @@ {% if grav.twig.enable_comments_plugin %} {% set scope = scope ?: 'data.' %} -

{{'PLUGIN_COMMENTS.ADD_COMMENT'|t}}

- -
- - {% for field in grav.config.plugins.comments.form.fields %} - {% set value = form.value(field.name) %} - {% if field.evaluateDefault %} - {% set value = evaluate(field.evaluateDefault) %} - {% endif %} - {% if config.plugins.login.enabled and grav.user.authenticated %} - {% if field.name == 'name' %} - - {% elseif field.name == 'email' %} - - {% else %} -
- {% include "forms/fields/#{field.type}/#{field.type}.html.twig" %} -
- {% endif %} - {% else %} -
- {% include "forms/fields/#{field.type}/#{field.type}.html.twig" %} -
- {% endif %} - {% endfor %} - {% include "forms/fields/formname/formname.html.twig" %} - -
- {% for button in grav.config.plugins.comments.form.buttons %} - - {% endfor %} -
- - {{ nonce_field('form', 'form-nonce')|raw }} -
- -
{{ form.message }}
+ {% include 'partials/comments.form.html.twig' %} {% if grav.twig.comments|length %}

{{'PLUGIN_COMMENTS.COMMENTS'|t}}

- - +
{% for comment in grav.twig.comments|array_reverse %} -
- - +
+
+ + user icon + +
+
+

{{comment.title}}

+
+ Reply +
+
+ {{'PLUGIN_COMMENTS.WRITTEN_ON'|t}} {{comment.date|e}} {{'PLUGIN_COMMENTS.BY'|t}} {{comment.author}} +
+
+ {{comment.text}} +
+ {{nested}} +
+
{% endfor %} -
- {{comment.text}} -
- {{'PLUGIN_COMMENTS.WRITTEN_ON'|t}} {{comment.date|e}} {{'PLUGIN_COMMENTS.BY'|t}} {{comment.author}} -
+
+ {% endif %} {% endif %} + + From 1bc97a31e7edf2cad3643c13d8cbe983d4ddf187 Mon Sep 17 00:00:00 2001 From: Thorsten Witteler Date: Thu, 19 Oct 2017 19:40:02 +0200 Subject: [PATCH 15/38] nested template --- templates/partials/comments.html.twig | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/templates/partials/comments.html.twig b/templates/partials/comments.html.twig index 3eeb423..a481604 100644 --- a/templates/partials/comments.html.twig +++ b/templates/partials/comments.html.twig @@ -8,21 +8,19 @@

{{'PLUGIN_COMMENTS.COMMENTS'|t}}

{% for comment in grav.twig.comments|array_reverse %} -
-
+
+ -
-

{{comment.title}}

-
- Reply +
+
+

{{comment.title}}

+ +
{{'PLUGIN_COMMENTS.WRITTEN_ON'|t}} {{comment.date|e}} {{'PLUGIN_COMMENTS.BY'|t}} {{comment.author}}
-
- {{'PLUGIN_COMMENTS.WRITTEN_ON'|t}} {{comment.date|e}} {{'PLUGIN_COMMENTS.BY'|t}} {{comment.author}} -
-
+
{{comment.text}}
{{nested}} From 018a42a3dc3c8855a374b41723f2d7dea0d28ae1 Mon Sep 17 00:00:00 2001 From: Thorsten Witteler Date: Thu, 19 Oct 2017 22:00:00 +0200 Subject: [PATCH 16/38] add level calculation --- class/Comment.php | 41 +++++++++++++++++++++++++++++++++++++++++ comments.php | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 class/Comment.php diff --git a/class/Comment.php b/class/Comment.php new file mode 100644 index 0000000..63c4c3a --- /dev/null +++ b/class/Comment.php @@ -0,0 +1,41 @@ +id = $id; + $this->value = $content; + } + + public function addItem($obj, $key = null) { + } + + public function deleteItem($key) { + } + + public function getItem($key) { + } + + public function getContent($level = 0) { + $comments = $this->value; + $comments['level'] = $level; + + foreach($this->children as $child) { + array_merge($comments, $child->getContent($level + 1)); + } + return $comments; + } + + public function setParent($parent) { + $this->parent = $parent; + } + public function addSubComment($obj) { + $this->children[] = $obj; + } + +} \ No newline at end of file diff --git a/comments.php b/comments.php index df1f915..7b9e312 100644 --- a/comments.php +++ b/comments.php @@ -16,6 +16,8 @@ use RocketTheme\Toolbox\Event\Event; use RocketTheme\Toolbox\File\File; use Symfony\Component\Yaml\Yaml; +require_once 'class\Comment.php'; + class CommentsPlugin extends Plugin { protected $route = 'comments'; @@ -402,11 +404,48 @@ class CommentsPlugin extends Plugin $filename .= $this->grav['uri']->path() . '.yaml'; $comments = $this->getDataFromFilename($filename)['comments']; + $comments = setCommentLevels($comments); //save to cache if enabled $cache->save($this->comments_cache_id, $comments); return $comments; } + /** + * Return the latest commented pages + */ + private function setCommentLevels($comments) { + $levels = array(); + $levelsflat = array(); + foreach($comments as $key => $comment) { + //$comments[$key]['level'] = 0; + $levels[$comment['parent']][] = $comment['id']; + $levelsflat[$comment['id']]['parent'] = $comment['parent']; + $levelsflat[$comment['id']]['class'] = new Comment($comment['id'], $comments[$key]); + } + //get starting points (entries without valid parent = root element) + $leveltree = array(); + foreach($levelsflat as $id => $parent) { + $parent_id = $parent['parent']; + if(!isset($levelsflat[$parent_id]){ + $leveltree[$id] = $levelsflat[$id]['class']; + //$leveltree[$id] = array(); + //$leveltree[$id]['level'] = 0; + //$leveltree[$id]['children'] = array(); + } else { + $currentParent = $levelsflat[$parent_id]['class']; + $currentChild = $levelsflat[$id]['class']; + $levelsflat[$id]['class']->setParent($currentParent); + $levelsflat[$parent_id]['class']->addSubComment($currentChild); + } + } + //reset comment values to nested order + $comments = array(); + foreach($leveltree as $id => $comment) { + array_merge($comments, $comment->getContent); + } + return $comments; + } + /** * Return the latest commented pages */ From 6d458f8fa4cb3ebbd22506d98aa584e67eef04c8 Mon Sep 17 00:00:00 2001 From: Thorsten Witteler Date: Thu, 19 Oct 2017 23:06:47 +0200 Subject: [PATCH 17/38] add level calculation --- class/Comment.php | 8 +++++--- comments.php | 15 +++++---------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/class/Comment.php b/class/Comment.php index 63c4c3a..affe1e9 100644 --- a/class/Comment.php +++ b/class/Comment.php @@ -1,5 +1,7 @@ value; - $comments['level'] = $level; + $this->value['level'] = $level; + $comments[] = $this->value; foreach($this->children as $child) { - array_merge($comments, $child->getContent($level + 1)); + $comments[] = $child->getContent($level + 1); } return $comments; } diff --git a/comments.php b/comments.php index 7b9e312..4771e57 100644 --- a/comments.php +++ b/comments.php @@ -16,7 +16,8 @@ use RocketTheme\Toolbox\Event\Event; use RocketTheme\Toolbox\File\File; use Symfony\Component\Yaml\Yaml; -require_once 'class\Comment.php'; +require_once '/html/apps/grav/user/plugins/comments/class/Comment.php'; +use Grav\Plugin\Comment; class CommentsPlugin extends Plugin { @@ -404,7 +405,7 @@ class CommentsPlugin extends Plugin $filename .= $this->grav['uri']->path() . '.yaml'; $comments = $this->getDataFromFilename($filename)['comments']; - $comments = setCommentLevels($comments); + $comments = $this->setCommentLevels($comments); //save to cache if enabled $cache->save($this->comments_cache_id, $comments); return $comments; @@ -414,11 +415,8 @@ class CommentsPlugin extends Plugin * Return the latest commented pages */ private function setCommentLevels($comments) { - $levels = array(); $levelsflat = array(); foreach($comments as $key => $comment) { - //$comments[$key]['level'] = 0; - $levels[$comment['parent']][] = $comment['id']; $levelsflat[$comment['id']]['parent'] = $comment['parent']; $levelsflat[$comment['id']]['class'] = new Comment($comment['id'], $comments[$key]); } @@ -426,11 +424,8 @@ class CommentsPlugin extends Plugin $leveltree = array(); foreach($levelsflat as $id => $parent) { $parent_id = $parent['parent']; - if(!isset($levelsflat[$parent_id]){ + if(!isset($levelsflat[$parent_id])){ $leveltree[$id] = $levelsflat[$id]['class']; - //$leveltree[$id] = array(); - //$leveltree[$id]['level'] = 0; - //$leveltree[$id]['children'] = array(); } else { $currentParent = $levelsflat[$parent_id]['class']; $currentChild = $levelsflat[$id]['class']; @@ -441,7 +436,7 @@ class CommentsPlugin extends Plugin //reset comment values to nested order $comments = array(); foreach($leveltree as $id => $comment) { - array_merge($comments, $comment->getContent); + $comments = array_merge($comments, $comment->getContent()); } return $comments; } From 7d153816f9abb5a9b235511ce46f6acea3c5bcb0 Mon Sep 17 00:00:00 2001 From: Thorsten Witteler Date: Fri, 20 Oct 2017 14:06:02 +0200 Subject: [PATCH 18/38] prepare ajax --- assets/comments.js | 83 ++++++++++++++++++++++++++++++++++++++++++++++ comments.php | 26 +++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 assets/comments.js diff --git a/assets/comments.js b/assets/comments.js new file mode 100644 index 0000000..bd499bd --- /dev/null +++ b/assets/comments.js @@ -0,0 +1,83 @@ +$(document).ready(function () { + + function PhpComment(element) { + this.element = element; + this.init(); + } + + PhpComment.prototype.init = function () { + this.setupVariables(); + this.setupEvents(); + } + + PhpComment.prototype.setupVariables = function () { + this.commentForm = this.element.find(".comment-form"); + this.titleField = this.element.find("#comment_title"); + this.bodyField = this.element.find("#comment_body"); + } + + PhpComment.prototype.setupEvents = function () { + var phpComment = this, + newMedia; + + $.ajax({ + url: '/media_template.php', + method: 'GET', + dataType: 'html', + success: function (data) { + newMedia = data; + } + }); + + phpComment.commentForm.on("submit", function (e) { + e.preventDefault(); + var parentId = 0, + title = phpComment.titleField.val(), + body = phpComment.bodyField.val(); + + if(phpComment.commentForm.parents(".media").length > 0){ + parentId = phpComment.commentForm.closest(".media").attr("data-Id"); + } + + $.ajax({ + url: phpComment.commentForm.attr("action"), + method: 'POST', + dataType: 'json', + data: {title: title, body: body, parentId: parentId}, + success: function (data) { + if(!data.created){ + alert("Couldn't create comment"); + return; + } + + newMedia = newMedia.replace("{{id}}", data.id); + newMedia = newMedia.replace("{{title}}", title); + newMedia = newMedia.replace("{{body}}", body); + newMedia = newMedia.replace("{{nested}}", ''); + phpComment.commentForm.before(newMedia); + phpComment.titleField.val(""); + phpComment.bodyField.val(""); + } + }); + }); + + $(document).on("click", ".comment-add-new", function (e) { + e.preventDefault(); + var media = $(this).closest(".comments"); + media.find(">.comment-body>.comment-text").after(phpComment.commentForm); + }); + $(document).on("click", ".comment-add-reply", function (e) { + e.preventDefault(); + var media = $(this).closest(".comment"); + media.find(">.comment-body>.comment-text").after(phpComment.commentForm); + }); + } + + $.fn.phpComment = function (options) { + new PhpComment(this); + return this; + } + + $(".comments").phpComment(); + +}); diff --git a/comments.php b/comments.php index 4771e57..832e6a9 100644 --- a/comments.php +++ b/comments.php @@ -96,6 +96,7 @@ class CommentsPlugin extends Plugin if ($this->enable) { $this->enable([ + 'onPageInitialized' => ['onPageInitialized', 0], 'onFormProcessed' => ['onFormProcessed', 0], 'onFormPageHeaderProcessed' => ['onFormPageHeaderProcessed', 0], 'onTwigSiteVariables' => ['onTwigSiteVariables', 0] @@ -150,6 +151,31 @@ class CommentsPlugin extends Plugin } } + /** + * Handle ajax call. + */ + public function onPageInitialized() + { + // initialize with page settings (post-cache) +// if (!$this->isAdmin() && isset($this->grav['page']->header()->{'star-ratings'})) { +// // if not in admin merge potential page-level configs +// $this->config->set('plugins.star-ratings', $this->mergeConfig($page)); +// } + $this->callback = 'nested-comments'; +// $this->callback = $this->config->get('plugins.star-ratings.callback'); +// $this->total_stars = $this->config->get('plugins.star-ratings.total_stars'); +// $this->only_full_stars = $this->config->get('plugins.star-ratings.only_full_stars'); + + // Process vote if required + if ($this->callback === $this->grav['uri']->path()) { + // try to add the vote + $result = $this->addVote(); + echo json_encode(['status' => $result[0], 'message' => $result[1], 'data' => ['score' => $result[2][0], 'count' => $result[2][1]]]); + exit(); + } + } + + /** * Handle form processing instructions. * From de3a683cbe162aea20e542b4ff62d801b8f4f671 Mon Sep 17 00:00:00 2001 From: Thorsten Witteler Date: Fri, 20 Oct 2017 15:08:26 +0200 Subject: [PATCH 19/38] prepare ajax --- assets/comments.js | 9 ++-- comments.php | 50 ++++++++++++++++++++-- templates/partials/comments.form.html.twig | 2 +- templates/partials/comments.html.twig | 3 +- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/assets/comments.js b/assets/comments.js index bd499bd..ab811ff 100644 --- a/assets/comments.js +++ b/assets/comments.js @@ -11,7 +11,7 @@ $(document).ready(function () { } PhpComment.prototype.setupVariables = function () { - this.commentForm = this.element.find(".comment-form"); + this.commentForm = this.element.find(".comments-form"); this.titleField = this.element.find("#comment_title"); this.bodyField = this.element.find("#comment_body"); } @@ -35,8 +35,8 @@ $(document).ready(function () { title = phpComment.titleField.val(), body = phpComment.bodyField.val(); - if(phpComment.commentForm.parents(".media").length > 0){ - parentId = phpComment.commentForm.closest(".media").attr("data-Id"); + if(phpComment.commentForm.parents(".comment").length > 0){ + parentId = phpComment.commentForm.closest(".comment").attr("data-Id"); } $.ajax({ @@ -63,8 +63,7 @@ $(document).ready(function () { $(document).on("click", ".comment-add-new", function (e) { e.preventDefault(); - var media = $(this).closest(".comments"); - media.find(">.comment-body>.comment-text").after(phpComment.commentForm); + $(this).find(".comments").before(phpComment.commentForm); }); $(document).on("click", ".comment-add-reply", function (e) { e.preventDefault(); diff --git a/comments.php b/comments.php index 832e6a9..a6fc7ec 100644 --- a/comments.php +++ b/comments.php @@ -56,6 +56,13 @@ class CommentsPlugin extends Plugin public function onTwigSiteVariables() { $this->grav['twig']->enable_comments_plugin = $this->enable; $this->grav['twig']->comments = $this->fetchComments(); + if ($this->config->get('plugins.comments.built_in_css')) { + $this->grav['assets'] + ->addCss('plugin://comments/assets/comments.css'); + } + $this->grav['assets'] + ->add('jquery', 101) + ->addJs('plugin://comments/assets/comments.js'); } /** @@ -166,15 +173,52 @@ class CommentsPlugin extends Plugin // $this->total_stars = $this->config->get('plugins.star-ratings.total_stars'); // $this->only_full_stars = $this->config->get('plugins.star-ratings.only_full_stars'); - // Process vote if required + // Process comment if required if ($this->callback === $this->grav['uri']->path()) { - // try to add the vote - $result = $this->addVote(); + // try to add the comment + $result = $this->addComment(); echo json_encode(['status' => $result[0], 'message' => $result[1], 'data' => ['score' => $result[2][0], 'count' => $result[2][1]]]); exit(); } } + public function addComment() + { + $nonce = $this->grav['uri']->param('nonce'); + if (!Utils::verifyNonce($nonce, 'comments')) { + return [false, 'Invalid security nonce', [0, 0]]; + } + $language = $this->grav['language']; + // get and filter the data + $parent_id = filter_input(INPUT_POST, 'parent_id', FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION); + $email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_STRING); + $text = filter_input(INPUT_POST, 'text', FILTER_SANITIZE_STRING); + $title = filter_input(INPUT_POST, 'title', FILTER_SANITIZE_STRING); + $name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_STRING); + //$data = $this->getStars($id); + $data = array( + ['parent_id'] => $parent_id, + ['email'] => $email, + ['text'] => $text, + ['title'] => $title, + ['name'] => $name, + ); + // ensure both values are sent + if (is_null($title) || is_null($text)) { + return [false, 'missing either text or title', [0, 0]]; + //return [false, $language->translate('PLUGIN_COMMENTS.FAIL'), $data]; + } + // sanity checks for parents + if ($parent_id < 0) { + $parent_id = 0; + } elseif ($parent_id > 999 ) { //TODO: Change to 'exists in list of comment ids + $parent_id = 0; + } + //$this->saveVoteData($id, $rating); + //$data = $this->getStars($id); + return [true, $language->translate('PLUGIN_COMMENTS.SUCCESS'), $data]; + } + /** * Handle form processing instructions. diff --git a/templates/partials/comments.form.html.twig b/templates/partials/comments.form.html.twig index dab77dc..78a2a15 100644 --- a/templates/partials/comments.form.html.twig +++ b/templates/partials/comments.form.html.twig @@ -1,5 +1,5 @@

{{'PLUGIN_COMMENTS.ADD_COMMENT'|t}}

-
diff --git a/templates/partials/comments.html.twig b/templates/partials/comments.html.twig index a481604..5f11eec 100644 --- a/templates/partials/comments.html.twig +++ b/templates/partials/comments.html.twig @@ -6,6 +6,7 @@ {% if grav.twig.comments|length %}

{{'PLUGIN_COMMENTS.COMMENTS'|t}}

+ {{'PLUGIN_COMMENTS.ADD_NEW'|t}}
{% for comment in grav.twig.comments|array_reverse %}
@@ -17,7 +18,7 @@

{{comment.title}}

- +
{{'PLUGIN_COMMENTS.WRITTEN_ON'|t}} {{comment.date|e}} {{'PLUGIN_COMMENTS.BY'|t}} {{comment.author}}
From 868d7b1231bdb35240b36425d59d0981c4208d87 Mon Sep 17 00:00:00 2001 From: Thorsten Witteler Date: Mon, 23 Oct 2017 13:37:52 +0200 Subject: [PATCH 20/38] add built in css --- assets/comments.css | 68 +++++++++++++++++++++++++++++++++++++++++++++ comments.yaml | 5 ++-- 2 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 assets/comments.css diff --git a/assets/comments.css b/assets/comments.css new file mode 100644 index 0000000..4105deb --- /dev/null +++ b/assets/comments.css @@ -0,0 +1,68 @@ +/* +=============================================================================================================================== +Comments Plugin Styles +=============================================================================================================================== +*/ + +.comment { + margin-top: 15px; + border-top: gray solid 2px; +} +.comment:first-child { + margin-top: 0; +} +.comment, +.comment-body { + zoom: 1; + overflow: hidden; +} +.comment-body { + width: 10000px; +} +.comment-object { + display: block; +} +.comment-right, +.comment > .pull-right { + padding-left: 10px; +} +.comment-left, +.comment > .pull-left { + padding-right: 10px; +} +.comment-left, +.comment-right, +.comment-body { + display: table-cell; + vertical-align: top; +} +.comment-middle { + vertical-align: middle; +} +.comment-bottom { + vertical-align: bottom; +} +.comment-heading { + margin-top: 0; + margin-bottom: 5px; + border-bottom: gray dashed 1px; +} +.comment-title, .comment-title h4 { + padding: 0px; + margin: 0px; +} +.comment-meta { + display: inline; + font-size: small; +} +.comment-reply { + display: inline; + float: right; +} +.comment-text { + clear: both; +} +.comment-list { + padding-left: 0; + list-style: none; +} diff --git a/comments.yaml b/comments.yaml index 5c09966..3c7a312 100644 --- a/comments.yaml +++ b/comments.yaml @@ -1,13 +1,12 @@ enabled: true - +built_in_css: true +ajax_callback: /nested-comments enable_on_routes: - '/blog' - disable_on_routes: - /blog/blog-post-to-ignore - /ignore-this-route #- '/blog/daring-fireball-link' - form: name: comments fields: From cace16f6060263a2649d8c55c7dd51b3d5fb2a6d Mon Sep 17 00:00:00 2001 From: Thorsten Witteler Date: Mon, 23 Oct 2017 13:39:31 +0200 Subject: [PATCH 21/38] change nonce action from "form" to "comments" --- templates/partials/comments.form.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/partials/comments.form.html.twig b/templates/partials/comments.form.html.twig index 78a2a15..5be563e 100644 --- a/templates/partials/comments.form.html.twig +++ b/templates/partials/comments.form.html.twig @@ -32,7 +32,7 @@ {% endfor %}
- {{ nonce_field('form', 'form-nonce')|raw }} + {{ nonce_field('comments', 'form-nonce')|raw }}
{{ form.message }}
From d70f10977613f06616c4ee8506308290d2d932f0 Mon Sep 17 00:00:00 2001 From: Thorsten Witteler Date: Mon, 23 Oct 2017 13:39:49 +0200 Subject: [PATCH 22/38] validate ajax input --- assets/comments.js | 210 ++++++++++++++++++++++++++++----------------- comments.php | 133 ++++++++++++++++++++++------ 2 files changed, 234 insertions(+), 109 deletions(-) diff --git a/assets/comments.js b/assets/comments.js index ab811ff..0964879 100644 --- a/assets/comments.js +++ b/assets/comments.js @@ -1,82 +1,130 @@ -$(document).ready(function () { - - function PhpComment(element) { - this.element = element; - this.init(); - } - - PhpComment.prototype.init = function () { - this.setupVariables(); - this.setupEvents(); - } - - PhpComment.prototype.setupVariables = function () { - this.commentForm = this.element.find(".comments-form"); - this.titleField = this.element.find("#comment_title"); - this.bodyField = this.element.find("#comment_body"); - } - - PhpComment.prototype.setupEvents = function () { - var phpComment = this, - newMedia; - - $.ajax({ - url: '/media_template.php', - method: 'GET', - dataType: 'html', - success: function (data) { - newMedia = data; - } - }); - - phpComment.commentForm.on("submit", function (e) { - e.preventDefault(); - var parentId = 0, - title = phpComment.titleField.val(), - body = phpComment.bodyField.val(); - - if(phpComment.commentForm.parents(".comment").length > 0){ - parentId = phpComment.commentForm.closest(".comment").attr("data-Id"); - } - - $.ajax({ - url: phpComment.commentForm.attr("action"), - method: 'POST', - dataType: 'json', - data: {title: title, body: body, parentId: parentId}, - success: function (data) { - if(!data.created){ - alert("Couldn't create comment"); - return; - } - - newMedia = newMedia.replace("{{id}}", data.id); - newMedia = newMedia.replace("{{title}}", title); - newMedia = newMedia.replace("{{body}}", body); - newMedia = newMedia.replace("{{nested}}", ''); - phpComment.commentForm.before(newMedia); - phpComment.titleField.val(""); - phpComment.bodyField.val(""); - } - }); - }); - - $(document).on("click", ".comment-add-new", function (e) { - e.preventDefault(); - $(this).find(".comments").before(phpComment.commentForm); - }); - $(document).on("click", ".comment-add-reply", function (e) { - e.preventDefault(); - var media = $(this).closest(".comment"); - media.find(">.comment-body>.comment-text").after(phpComment.commentForm); - }); - } - - $.fn.phpComment = function (options) { - new PhpComment(this); - return this; - } - - $(".comments").phpComment(); - +jQuery(document).ready(function () { + var commentForm = $(document).find('.comments-form'); + var commentSection = $(document).find('.comments').first(); + var commentAlert = commentForm.closest('.alert'); + //var newMedia; + //hide form, show link + commentForm.hide(); + $(document).find('.comment-add-new').show(); + //get template for inserting new comments + /* +$.ajax({ + url: '/media_template.php', + method: 'GET', + dataType: 'html', + success: function (data) { + newMedia = data; + } +}); + */ + $('body').on('click', '.comment-add-new-sadf', function (e) { + e.preventDefault(); + alert('asdf'); + $('span').stop().css('opacity', 1).text('myName = ' + e.name).fadeIn(30).fadeOut(1000); + }); + //show comment form above comments section (new comment thread) + $('body').on('click', '.comment-add-new', function (e) { + e.preventDefault(); + //commentForm.hide(1000); + //commentSection.before(commentForm); + $(this).before(commentForm); + commentForm.show('slow'); + //$(this).slideUp(); + }); + //show comment form below selected comment (reply to existing comment) + $('body').on('click', '.comment-add-reply', function (e) { + e.preventDefault(); + var media = $(this).closest('.comment'); + commentForm.hide(); + media.find('>.comment-body>.comment-text').after(commentForm); + commentForm.show('slow'); + }); + // Attach a submit handler to the form + $(commentForm).on('submit', function (event) { + event.preventDefault(); + // Get some values from elements on the page: + //var term = $(this).find( "input[name='s']" ).val(); + //var data = $(this).serializeArray(); + var data = $(this).serialize(); + console.log("Form Data (submit)", JSON.parse(JSON.stringify(data))); + //var url = $(this).attr( "action" ); + var url = '/nested-comments'; + var parentId = 0; + if ($(this).parents('.comment').length > 0) { + parentId = $(this).closest('.comment').attr('data-Id'); + } + // Send the data using post + + //var posting = $.post(url, { parentId: parentId, data: data }, null, 'json'); + var posting = $.post(url, data + '&parentID=' + parentId, null, 'json'); + //$.post( "test.php", $( "#testform" ).serialize() ); + // Put the results in a div + posting.done(function (response) { + alert('success'); + console.log("Response Data (done)", JSON.parse(JSON.stringify(response))); + //response = JSON.parse(response); + var message = response.status ? response.message : 'Error: ' + response.message; + commentForm.after(commentAlert); + commentAlert.empty().append(message); + if (!response.status) { + return; + } + if (response.status) { + var newMedia = ` +
+
+ + user icon + +
+
+
+

{{comment.title}}

+ +
{{'PLUGIN_COMMENTS.WRITTEN_ON'|t}} {{comment.date|e}} {{'PLUGIN_COMMENTS.BY'|t}} {{comment.author}}
+
+
+ {{comment.text}} +
+ {{nested}} +
+
+`; + newMedia = newMedia.replace('{{comment.id}}', response.id); + newMedia = newMedia.replace('{{comment.level|e}}', response.level); + newMedia = newMedia.replace('{{comment.email|trim|lower|md5}}', response.hash); + newMedia = newMedia.replace('{{parent_id}}', response.data.parent_id); + newMedia = newMedia.replace('{{comment.title}}', response.data.title); + newMedia = newMedia.replace('{{comment.text}}', response.data.text); + newMedia = newMedia.replace('{{comment.author}}', response.data.name); + //newMedia = newMedia.replace('{{comment.date|e}}', response.data.name); + if ($( "div[data-Id='" + response.data.parent_id + "']" ).length > 0) { + $( "div[data-Id='" + response.data.parent_id + "']" ).first().after(newMedia); + } else { + $( "div.comments" ).last().prepend(newMedia); + } + //phpComment.commentForm.before(newMedia); + //phpComment.titleField.val(""); + //phpComment.bodyField.val(""); + } + setTimeout(function () { + commentForm.hide(3000); + }, 5000); + }); + posting.fail(function (status, error, title) { + alert('error'); + console.log("Response Data (fail)", JSON.parse(JSON.stringify(status))); + commentForm.after(commentAlert); + commentAlert.empty().append("

TEST

"); + commentAlert.append("

" + status + "

"); + commentAlert.append("

" + error + "

"); + commentAlert.append("

" + title + "

"); + }); + posting.always(function (test) { + //alert("finished, be it successful or not"); + //test = JSON.parse(test); + //test = test.serialize(); + //alert(test); + }); + }); }); diff --git a/comments.php b/comments.php index a6fc7ec..e0b6352 100644 --- a/comments.php +++ b/comments.php @@ -73,9 +73,15 @@ class CommentsPlugin extends Plugin $disable_on_routes = (array) $this->config->get('plugins.comments.disable_on_routes'); $enable_on_routes = (array) $this->config->get('plugins.comments.enable_on_routes'); + $callback = $this->config->get('plugins.comments.ajax_callback'); $path = $uri->path(); + if ($callback === $path) { + $this->enable = true; + return; + } + if (!in_array($path, $disable_on_routes)) { if (in_array($path, $enable_on_routes)) { $this->enable = true; @@ -163,58 +169,126 @@ class CommentsPlugin extends Plugin */ public function onPageInitialized() { + $is_ajax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'; // initialize with page settings (post-cache) // if (!$this->isAdmin() && isset($this->grav['page']->header()->{'star-ratings'})) { // // if not in admin merge potential page-level configs // $this->config->set('plugins.star-ratings', $this->mergeConfig($page)); // } - $this->callback = 'nested-comments'; // $this->callback = $this->config->get('plugins.star-ratings.callback'); // $this->total_stars = $this->config->get('plugins.star-ratings.total_stars'); // $this->only_full_stars = $this->config->get('plugins.star-ratings.only_full_stars'); - + $callback = $this->config->get('plugins.comments.ajax_callback'); // Process comment if required - if ($this->callback === $this->grav['uri']->path()) { + if ($is_ajax || $callback === $this->grav['uri']->path()) { // try to add the comment $result = $this->addComment(); - echo json_encode(['status' => $result[0], 'message' => $result[1], 'data' => ['score' => $result[2][0], 'count' => $result[2][1]]]); - exit(); + echo json_encode([ + 'status' => $result[0], + 'message' => $result[1], + 'data' => $result[2], +// 'data' => [ +// 'score' => $result[2][0], +// 'count' => $result[2][1] +// ] + ]); + exit(); //prevents the page frontend from beeing displayed. } } public function addComment() { - $nonce = $this->grav['uri']->param('nonce'); - if (!Utils::verifyNonce($nonce, 'comments')) { - return [false, 'Invalid security nonce', [0, 0]]; - } - $language = $this->grav['language']; + if (!$_SERVER["REQUEST_METHOD"] == "POST") { + // Not a POST request, set a 403 (forbidden) response code. + http_response_code(403); + return [false, 'There was a problem with your submission, please try again.', [0, 0]]; + } // get and filter the data - $parent_id = filter_input(INPUT_POST, 'parent_id', FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION); - $email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_STRING); - $text = filter_input(INPUT_POST, 'text', FILTER_SANITIZE_STRING); - $title = filter_input(INPUT_POST, 'title', FILTER_SANITIZE_STRING); - $name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_STRING); - //$data = $this->getStars($id); - $data = array( - ['parent_id'] => $parent_id, - ['email'] => $email, - ['text'] => $text, - ['title'] => $title, - ['name'] => $name, - ); + if (!isset($_POST['data']) || !is_array($_POST['data'])) { + // Set a 400 (bad request) response code and exit. + http_response_code(400); + return [false, 'missing data', [0, 0]]; + } + $input = array(); + $input['parent_id'] = filter_input(INPUT_POST, 'parentID', FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION); + $input['name'] = isset($_POST['data']['name']) ? filter_var($_POST['data']['name'], FILTER_SANITIZE_STRING) : null; + $input['email'] = isset($_POST['data']['email']) ? filter_var($_POST['data']['email'], FILTER_SANITIZE_EMAIL) : null; + $input['text'] = isset($_POST['data']['text']) ? filter_var($_POST['data']['text'], FILTER_SANITIZE_STRING) : null; + $input['date'] = isset($_POST['data']['date']) ? filter_var($_POST['data']['date'], FILTER_SANITIZE_STRING) : null; + $input['title'] = isset($_POST['data']['title']) ? filter_var($_POST['data']['title'], FILTER_SANITIZE_STRING) : null; + $input['lang'] = isset($_POST['data']['lang']) ? filter_var($_POST['data']['lang'], FILTER_SANITIZE_STRING) : null; + $input['path'] = isset($_POST['data']['path']) ? filter_var($_POST['data']['path'], FILTER_SANITIZE_STRING) : null; + $input['form-name'] = filter_input(INPUT_POST, 'form-name', FILTER_SANITIZE_STRING); + $input['form-nonce'] = filter_input(INPUT_POST, 'form-nonce', FILTER_SANITIZE_STRING); +/* + foreach ($_POST['data'] as $field) { + if (isset($field['name']) && isset($field['value'])) { + switch ($field['name']) { + case 'data[name]': + $input['name'] = filter_var($field['value'], FILTER_SANITIZE_STRING); + break; + case 'data[email]': + $input['email'] = filter_var($field['value'], FILTER_SANITIZE_EMAIL); + break; + case 'data[text]': + $input['text'] = filter_var($field['value'], FILTER_SANITIZE_STRING); + break; + case 'data[date]': + $input['date'] = filter_var($field['value'], FILTER_SANITIZE_STRING); + break; + case 'data[title]': + $input['title'] = filter_var($field['value'], FILTER_SANITIZE_STRING); + break; + case 'data[lang]': + $input['lang'] = filter_var($field['value'], FILTER_SANITIZE_STRING); + break; + case 'data[path]': + $input['path'] = filter_var($field['value'], FILTER_SANITIZE_STRING); + break; + case '__form-name__': + $input['form-name'] = filter_var($field['value'], FILTER_SANITIZE_STRING); + break; + case 'form-nonce': + $input['form-nonce'] = filter_var($field['value'], FILTER_SANITIZE_STRING); //$this->grav['uri']->param('nonce'); + break; + default: + //ignore unexpected fields. + } + } + } +*/ + if (!Utils::verifyNonce($input['form-nonce'], 'comments')) { + http_response_code(403); + return [false, 'Invalid security nonce', [$_POST, $input['form-nonce']]]; + } // ensure both values are sent - if (is_null($title) || is_null($text)) { + if (is_null($input['title']) || is_null($input['text'])) { + // Set a 400 (bad request) response code and exit. + http_response_code(400); return [false, 'missing either text or title', [0, 0]]; //return [false, $language->translate('PLUGIN_COMMENTS.FAIL'), $data]; } + $language = $this->grav['language']; + //$data = $this->getStars($id); + $data = array( + 'parent_id' => $input['parent_id'], + 'email' => $input['email'], + 'text' => $input['text'], + 'title' => $input['title'], + 'name' => $input['name'], + 'id' => 99, + 'level' => 0, + 'hash' => md5(strtolower(trim($input['email']))), + ); // sanity checks for parents - if ($parent_id < 0) { - $parent_id = 0; - } elseif ($parent_id > 999 ) { //TODO: Change to 'exists in list of comment ids - $parent_id = 0; + if ($data['parent_id'] < 0) { + $data['parent_id'] = 0; + } elseif ($data['parent_id'] > 999 ) { //TODO: Change to 'exists in list of comment ids + $data['parent_id'] = 0; } //$this->saveVoteData($id, $rating); + // Set a 500 (internal server error) response code. +// http_response_code(500); //$data = $this->getStars($id); return [true, $language->translate('PLUGIN_COMMENTS.SUCCESS'), $data]; } @@ -485,6 +559,9 @@ class CommentsPlugin extends Plugin * Return the latest commented pages */ private function setCommentLevels($comments) { + if(!is_array($comments)) { + return $comments; + } $levelsflat = array(); foreach($comments as $key => $comment) { $levelsflat[$comment['id']]['parent'] = $comment['parent']; From 0936de7c1d07e66fe5df6646a90c08d2e76400ca Mon Sep 17 00:00:00 2001 From: Thorsten Witteler Date: Mon, 23 Oct 2017 16:56:13 +0200 Subject: [PATCH 23/38] remove sorting from twig template (breaks nested comments) --- templates/partials/comments.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/partials/comments.html.twig b/templates/partials/comments.html.twig index 5f11eec..1f61461 100644 --- a/templates/partials/comments.html.twig +++ b/templates/partials/comments.html.twig @@ -8,7 +8,7 @@

{{'PLUGIN_COMMENTS.COMMENTS'|t}}

{{'PLUGIN_COMMENTS.ADD_NEW'|t}}
- {% for comment in grav.twig.comments|array_reverse %} + {% for comment in grav.twig.comments %}
From b4ab9a5111661dc0c26cca59dd1facfaa1679874 Mon Sep 17 00:00:00 2001 From: Thorsten Witteler Date: Tue, 24 Oct 2017 02:57:44 +0200 Subject: [PATCH 24/38] finish nested comment implementation --- assets/comments.css | 55 ++++- assets/comments.js | 147 ++++++------ class/Comment.php | 3 +- comments.php | 309 ++++++++++++-------------- languages.yaml | 8 + templates/partials/comments.html.twig | 11 +- 6 files changed, 279 insertions(+), 254 deletions(-) diff --git a/assets/comments.css b/assets/comments.css index 4105deb..ad04314 100644 --- a/assets/comments.css +++ b/assets/comments.css @@ -9,7 +9,7 @@ Comments Plugin Styles border-top: gray solid 2px; } .comment:first-child { - margin-top: 0; + margin-top: 0px; } .comment, .comment-body { @@ -43,7 +43,7 @@ Comments Plugin Styles vertical-align: bottom; } .comment-heading { - margin-top: 0; + margin-top: 0px; margin-bottom: 5px; border-bottom: gray dashed 1px; } @@ -63,6 +63,55 @@ Comments Plugin Styles clear: both; } .comment-list { - padding-left: 0; + padding-left: 0px; list-style: none; } +.comment-flag-new { + background-color: lightcyan; +} +.comment-level-1 { margin-left: 20px; padding-left: 0px; border-left: gray solid 0px; } +.comment-level-2 { margin-left: 40px; padding-left: 0px; border-left: gray solid 0px; } +.comment-level-3 { margin-left: 60px; padding-left: 0px; border-left: gray solid 0px; } +.comment-level-4 { margin-left: 80px; padding-left: 0px; border-left: gray solid 0px; } +.comment-level-5 { margin-left: 100px; padding-left: 0px; border-left: gray solid 0px; } +.row.comments { position: relative; } +.comment-level-1::before { + content: "\f105"; + font-family: FontAwesome; + position: absolute; + left: 0px; + font-size: 3rem; + color: lightgray; +} +.comment-level-2::before { + content: "\f105\f105"; + font-family: FontAwesome; + position: absolute; + left: 0px; + font-size: 3rem; + color: lightgray; +} +.comment-level-3::before { + content: "\f105\f105\f105"; + font-family: FontAwesome; + position: absolute; + left: 0px; + font-size: 3rem; + color: lightgray; +} +.comment-level-4::before { + content: "\f105\f105\f105\f105"; + font-family: FontAwesome; + position: absolute; + left: 0px; + font-size: 3rem; + color: lightgray; +} +.comment-level-5::before { + content: "\f105\f105\f105\f105\f105"; + font-family: FontAwesome; + position: absolute; + left: 0px; + font-size: 3rem; + color: lightgray; +} diff --git a/assets/comments.js b/assets/comments.js index 0964879..3d63de1 100644 --- a/assets/comments.js +++ b/assets/comments.js @@ -1,36 +1,24 @@ +function escapeRegExp(str) { + return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); +} jQuery(document).ready(function () { var commentForm = $(document).find('.comments-form'); var commentSection = $(document).find('.comments').first(); - var commentAlert = commentForm.closest('.alert'); - //var newMedia; + var commentAlert = $(document).find('.alert').first(); + //hide form, show link commentForm.hide(); $(document).find('.comment-add-new').show(); - //get template for inserting new comments - /* -$.ajax({ - url: '/media_template.php', - method: 'GET', - dataType: 'html', - success: function (data) { - newMedia = data; - } -}); - */ - $('body').on('click', '.comment-add-new-sadf', function (e) { - e.preventDefault(); - alert('asdf'); - $('span').stop().css('opacity', 1).text('myName = ' + e.name).fadeIn(30).fadeOut(1000); - }); + //show comment form above comments section (new comment thread) $('body').on('click', '.comment-add-new', function (e) { e.preventDefault(); //commentForm.hide(1000); - //commentSection.before(commentForm); $(this).before(commentForm); commentForm.show('slow'); - //$(this).slideUp(); + commentAlert.slideUp(); }); + //show comment form below selected comment (reply to existing comment) $('body').on('click', '.comment-add-reply', function (e) { e.preventDefault(); @@ -38,93 +26,96 @@ $.ajax({ commentForm.hide(); media.find('>.comment-body>.comment-text').after(commentForm); commentForm.show('slow'); + commentAlert.slideUp(); }); + // Attach a submit handler to the form $(commentForm).on('submit', function (event) { event.preventDefault(); - // Get some values from elements on the page: - //var term = $(this).find( "input[name='s']" ).val(); - //var data = $(this).serializeArray(); + // Get form data: var data = $(this).serialize(); - console.log("Form Data (submit)", JSON.parse(JSON.stringify(data))); - //var url = $(this).attr( "action" ); - var url = '/nested-comments'; +//console.log("Form Data (submit)", JSON.parse(JSON.stringify(data))); + var url = $(this).attr( "action" ); + //var url = '/nested-comments'; var parentId = 0; + var ownLevel = 0; if ($(this).parents('.comment').length > 0) { - parentId = $(this).closest('.comment').attr('data-Id'); + parentId = $(this).closest('.comment').attr('data-id'); + ownLevel = parseInt($(this).closest('.comment').attr('data-level'), 10) + 1; } + // Send the data using post - //var posting = $.post(url, { parentId: parentId, data: data }, null, 'json'); var posting = $.post(url, data + '&parentID=' + parentId, null, 'json'); - //$.post( "test.php", $( "#testform" ).serialize() ); - // Put the results in a div + + // Register events to ajax call posting.done(function (response) { - alert('success'); - console.log("Response Data (done)", JSON.parse(JSON.stringify(response))); - //response = JSON.parse(response); - var message = response.status ? response.message : 'Error: ' + response.message; +//alert('success'); +//console.log("Response Data (done)", JSON.parse(JSON.stringify(response))); + //response = JSON.parse(response); //not needed, post was done using json commentForm.after(commentAlert); - commentAlert.empty().append(message); if (!response.status) { - return; + //should not trigger at all, if all bad requests return the right http status code + //i.e. <> 200 success => thus triggering posting.fail() + //leave this check just in case + commentAlert.stop().css('opacity', 1).text('Error: ' + response.message).fadeIn(30).fadeOut(5000); + return; } if (response.status) { - var newMedia = ` -
- -
-
-

{{comment.title}}

- -
{{'PLUGIN_COMMENTS.WRITTEN_ON'|t}} {{comment.date|e}} {{'PLUGIN_COMMENTS.BY'|t}} {{comment.author}}
-
-
- {{comment.text}} -
- {{nested}} -
-
-`; - newMedia = newMedia.replace('{{comment.id}}', response.id); - newMedia = newMedia.replace('{{comment.level|e}}', response.level); - newMedia = newMedia.replace('{{comment.email|trim|lower|md5}}', response.hash); - newMedia = newMedia.replace('{{parent_id}}', response.data.parent_id); - newMedia = newMedia.replace('{{comment.title}}', response.data.title); - newMedia = newMedia.replace('{{comment.text}}', response.data.text); - newMedia = newMedia.replace('{{comment.author}}', response.data.name); - //newMedia = newMedia.replace('{{comment.date|e}}', response.data.name); - if ($( "div[data-Id='" + response.data.parent_id + "']" ).length > 0) { - $( "div[data-Id='" + response.data.parent_id + "']" ).first().after(newMedia); - } else { + commentAlert.css('color', 'green').empty().append(document.createTextNode( response.message )).fadeIn(30); + var newMedia = "
" + + "
" + + "" + + "user icon" + + "" + + "
" + + "
" + + "
" + + "

{{comment.title}}

" + + "" + + "
{{'PLUGIN_COMMENTS.WRITTEN_ON'|t}} {{comment.date|e}} {{'PLUGIN_COMMENTS.BY'|t}} {{comment.author}}
" + + "
" + + "
" + + "{{comment.text}}" + + "
" + + "{{nested}}" + + "
" + + "
"; + newMedia = newMedia.replace(new RegExp(escapeRegExp("{{comment.id}}"), 'g'), response.data.id); + newMedia = newMedia.replace(new RegExp(escapeRegExp("{{comment.level|e}}"), 'g'), ownLevel); + newMedia = newMedia.replace(new RegExp(escapeRegExp("{{comment.level}}"), 'g'), ownLevel); + newMedia = newMedia.replace(new RegExp(escapeRegExp("{{comment.email|trim|lower|md5}}"), 'g'), response.data.hash); + newMedia = newMedia.replace(new RegExp(escapeRegExp("{{parent_id}}"), 'g'), response.data.parent_id); + newMedia = newMedia.replace(new RegExp(escapeRegExp("{{comment.title}}"), 'g'), response.data.title); + newMedia = newMedia.replace(new RegExp(escapeRegExp("{{comment.text}}"), 'g'), response.data.text); + newMedia = newMedia.replace(new RegExp(escapeRegExp("{{comment.author}}"), 'g'), response.data.name); + newMedia = newMedia.replace(new RegExp(escapeRegExp("{{comment.date|e}}"), 'g'), response.data.date); + newMedia = newMedia.replace(new RegExp(escapeRegExp("{{nested}}"), 'g'), ''); + newMedia = newMedia.replace(new RegExp(escapeRegExp("{{'PLUGIN_COMMENTS.ADD_REPLY'|t}}"), 'g'), response.data.ADD_REPLY); + newMedia = newMedia.replace(new RegExp(escapeRegExp("{{'PLUGIN_COMMENTS.WRITTEN_ON'|t}}"), 'g'), response.data.WRITTEN_ON); + newMedia = newMedia.replace(new RegExp(escapeRegExp("{{'PLUGIN_COMMENTS.BY'|t}}"), 'g'), response.data.BY); + if ($( "div[data-id='" + response.data.parent_id + "']" ).length > 0) { + $( "div[data-id='" + response.data.parent_id + "']" ).first().after(newMedia); + } else { $( "div.comments" ).last().prepend(newMedia); - } - //phpComment.commentForm.before(newMedia); - //phpComment.titleField.val(""); - //phpComment.bodyField.val(""); + } } setTimeout(function () { - commentForm.hide(3000); + commentForm.hide(2000); + commentAlert.fadeOut(5000); }, 5000); }); posting.fail(function (status, error, title) { - alert('error'); - console.log("Response Data (fail)", JSON.parse(JSON.stringify(status))); +//alert('error'); +//console.log("Response Data (fail)", JSON.parse(JSON.stringify(status))); commentForm.after(commentAlert); commentAlert.empty().append("

TEST

"); commentAlert.append("

" + status + "

"); commentAlert.append("

" + error + "

"); commentAlert.append("

" + title + "

"); }); - posting.always(function (test) { + posting.always(function () { //alert("finished, be it successful or not"); - //test = JSON.parse(test); - //test = test.serialize(); - //alert(test); }); }); }); diff --git a/class/Comment.php b/class/Comment.php index affe1e9..5219f53 100644 --- a/class/Comment.php +++ b/class/Comment.php @@ -28,7 +28,8 @@ class Comment $comments[] = $this->value; foreach($this->children as $child) { - $comments[] = $child->getContent($level + 1); + //$comments[] = $child->getContent($level + 1); //produces nested result array. + $comments = array_merge($comments, $child->getContent($level + 1)); //produces flat result array. } return $comments; } diff --git a/comments.php b/comments.php index e0b6352..ecfbe11 100644 --- a/comments.php +++ b/comments.php @@ -182,7 +182,7 @@ class CommentsPlugin extends Plugin // Process comment if required if ($is_ajax || $callback === $this->grav['uri']->path()) { // try to add the comment - $result = $this->addComment(); + $result = $this->addComment(true); echo json_encode([ 'status' => $result[0], 'message' => $result[1], @@ -196,8 +196,10 @@ class CommentsPlugin extends Plugin } } - public function addComment() + public function addComment($is_ajax = false) { + if($is_ajax) { + $language = $this->grav['language']; if (!$_SERVER["REQUEST_METHOD"] == "POST") { // Not a POST request, set a 403 (forbidden) response code. http_response_code(403); @@ -220,43 +222,6 @@ class CommentsPlugin extends Plugin $input['path'] = isset($_POST['data']['path']) ? filter_var($_POST['data']['path'], FILTER_SANITIZE_STRING) : null; $input['form-name'] = filter_input(INPUT_POST, 'form-name', FILTER_SANITIZE_STRING); $input['form-nonce'] = filter_input(INPUT_POST, 'form-nonce', FILTER_SANITIZE_STRING); -/* - foreach ($_POST['data'] as $field) { - if (isset($field['name']) && isset($field['value'])) { - switch ($field['name']) { - case 'data[name]': - $input['name'] = filter_var($field['value'], FILTER_SANITIZE_STRING); - break; - case 'data[email]': - $input['email'] = filter_var($field['value'], FILTER_SANITIZE_EMAIL); - break; - case 'data[text]': - $input['text'] = filter_var($field['value'], FILTER_SANITIZE_STRING); - break; - case 'data[date]': - $input['date'] = filter_var($field['value'], FILTER_SANITIZE_STRING); - break; - case 'data[title]': - $input['title'] = filter_var($field['value'], FILTER_SANITIZE_STRING); - break; - case 'data[lang]': - $input['lang'] = filter_var($field['value'], FILTER_SANITIZE_STRING); - break; - case 'data[path]': - $input['path'] = filter_var($field['value'], FILTER_SANITIZE_STRING); - break; - case '__form-name__': - $input['form-name'] = filter_var($field['value'], FILTER_SANITIZE_STRING); - break; - case 'form-nonce': - $input['form-nonce'] = filter_var($field['value'], FILTER_SANITIZE_STRING); //$this->grav['uri']->param('nonce'); - break; - default: - //ignore unexpected fields. - } - } - } -*/ if (!Utils::verifyNonce($input['form-nonce'], 'comments')) { http_response_code(403); return [false, 'Invalid security nonce', [$_POST, $input['form-nonce']]]; @@ -268,31 +233,138 @@ class CommentsPlugin extends Plugin return [false, 'missing either text or title', [0, 0]]; //return [false, $language->translate('PLUGIN_COMMENTS.FAIL'), $data]; } - $language = $this->grav['language']; - //$data = $this->getStars($id); - $data = array( - 'parent_id' => $input['parent_id'], - 'email' => $input['email'], - 'text' => $input['text'], - 'title' => $input['title'], - 'name' => $input['name'], - 'id' => 99, - 'level' => 0, - 'hash' => md5(strtolower(trim($input['email']))), - ); // sanity checks for parents - if ($data['parent_id'] < 0) { - $data['parent_id'] = 0; - } elseif ($data['parent_id'] > 999 ) { //TODO: Change to 'exists in list of comment ids - $data['parent_id'] = 0; + if ($input['parent_id'] < 0) { + $input['parent_id'] = 0; + } elseif ($input['parent_id'] > 999 ) { //TODO: Change to 'exists in list of comment ids + $input['parent_id'] = 0; } - //$this->saveVoteData($id, $rating); - // Set a 500 (internal server error) response code. -// http_response_code(500); - //$data = $this->getStars($id); + $lang = $this->grav['language']->getLanguage(); + $path = $this->grav['page']->path(); + $route = $this->grav['page']->route(); + $comment = $this->saveComment($route, $path, $input['parent_id'], $lang, $input['text'], $input['name'], $input['email'], $input['title']); + //$comments = $this->fetchComments(); + $data = array( + 'parent_id' => $comment['parent'], + 'id' => $comment['id'], + 'text' => $comment['text'], + 'title' => $comment['title'], + 'name' => $comment['author'], + 'date' => $comment['date'], + 'level' => 0, + 'hash' => md5(strtolower(trim($comment['email']))), + 'ADD_REPLY' => $language->translate('PLUGIN_COMMENTS.ADD_REPLY'), + 'WRITTEN_ON' => $language->translate('PLUGIN_COMMENTS.WRITTEN_ON'), + 'BY' => $language->translate('PLUGIN_COMMENTS.BY'), + ); return [true, $language->translate('PLUGIN_COMMENTS.SUCCESS'), $data]; + } else { +// Set a 500 (internal server error) response code. +// http_response_code(500); + + } } + /** + * Handle form processing instructions. + * + * @param Event $event + */ + public function saveComment($route, $path, $parent_id, $lang, $text, $name, $email, $title) + { + $date = date('D, d M Y H:i:s', time()); + + /******************************/ + /** store comments with page **/ + /******************************/ + $localfilename = $path . '/comments.yaml'; + $localfile = CompiledYamlFile::instance($localfilename); + if (file_exists($localfilename)) { + $data = $localfile->content(); + $data['autoincrement']++; + } else { + $data = array( + 'autoincrement' => 1, + 'comments' => array() + ); + } + $localid = $data['autoincrement']; + $newComment = [ + 'id' => $data['autoincrement'], + 'parent' => $parent_id, + 'lang' => $lang, + 'title' => $title, + 'text' => $text, + 'date' => $date, + 'author' => $name, + 'email' => $email + ]; + $data['comments'][] = $newComment; + $localfile->save($data); + /**********************************/ + /** store comments in index file **/ + /**********************************/ + $indexfilename = DATA_DIR . 'comments/index.yaml'; + $indexfile = CompiledYamlFile::instance($indexfilename); + if (file_exists($indexfilename)) { + $dataIndex = $indexfile->content(); + } else { + $dataIndex = array( + 'comments' => array() + ); + } + $dataIndex['comments'][] = [ + 'page' => $route, + 'id' => $localid, + 'parent' => $parent_id, + 'lang' => $lang, + 'title' => $title, + 'text' => $text, + 'date' => $date, + 'author' => $name, + 'email' => $email + ]; + $indexfile->save($dataIndex); + + /**************************************/ + /** store comments in old data files **/ + /** TODO: remove as soon as admin **/ + /** panel uses new index file **/ + /**************************************/ + $filename = DATA_DIR . 'comments'; + $filename .= ($lang ? '/' . $lang : ''); + $filename .= $path . '.yaml'; + $file = CompiledYamlFile::instance($filename); + + if (file_exists($filename)) { + $dataLegacy = $file->content(); + + $dataLegacy['comments'][] = [ + 'text' => $text, + 'date' => $date, + 'author' => $name, + 'email' => $email + ]; + } else { + $dataLegacy = array( + 'title' => $title, + 'lang' => $lang, + 'comments' => array([ + 'text' => $text, + 'date' => $date, + 'author' => $name, + 'email' => $email + ]) + ); + } + + $file->save($dataLegacy); + + //clear cache + $this->grav['cache']->delete($this->comments_cache_id); + + return $newComment; + } /** * Handle form processing instructions. @@ -319,6 +391,7 @@ class CommentsPlugin extends Plugin $name = filter_var(urldecode($post['name']), FILTER_SANITIZE_STRING); $email = filter_var(urldecode($post['email']), FILTER_SANITIZE_STRING); $title = filter_var(urldecode($post['title']), FILTER_SANITIZE_STRING); + $parent_id = 0; if (isset($this->grav['user'])) { $user = $this->grav['user']; @@ -329,117 +402,12 @@ class CommentsPlugin extends Plugin } /** @var Language $language */ - $language = $this->grav['language']; - $lang = $language->getLanguage(); + $lang = $this->grav['language']->getLanguage(); + + $path = $this->grav['page']->path(); + $route = $this->grav['page']->route(); - /******************************/ - /** store comments with page **/ - /******************************/ - $page = $this->grav['page']; - $localfilename = $page->path() . '/comments.yaml'; - $localfile = CompiledYamlFile::instance($localfilename); - if (file_exists($localfilename)) { - $data = $localfile->content(); - $data['autoincrement']++; - $data['comments'][] = [ - 'id' => $data['autoincrement'], - 'parent' => 0, - 'lang' => $lang, - 'title' => $title, - 'text' => $text, - 'date' => date('D, d M Y H:i:s', time()), - 'author' => $name, - 'email' => $email - ]; - } else { - $data = array( - 'autoincrement' => 1, - 'comments' => array([ - 'id' => 1, - 'parent' => 0, - 'lang' => $lang, - 'title' => $title, - 'text' => $text, - 'date' => date('D, d M Y H:i:s', time()), - 'author' => $name, - 'email' => $email - ]) - ); - } - $localfile->save($data); - $localid = $data['autoincrement']; - $data = null; - /**********************************/ - /** store comments in index file **/ - /**********************************/ - $indexfilename = DATA_DIR . 'comments/index.yaml'; - $indexfile = CompiledYamlFile::instance($indexfilename); - if (file_exists($indexfilename)) { - $data = $indexfile->content(); - $data['comments'][] = [ - 'page' => $page->route(), - 'id' => $localid, - 'parent' => 0, - 'lang' => $lang, - 'title' => $title, - 'text' => $text, - 'date' => date('D, d M Y H:i:s', time()), - 'author' => $name, - 'email' => $email - ]; - } else { - $data = array( - 'comments' => array([ - 'page' => $page->route(), - 'id' => $localid, - 'parent' => 0, - 'lang' => $lang, - 'title' => $title, - 'text' => $text, - 'date' => date('D, d M Y H:i:s', time()), - 'author' => $name, - 'email' => $email - ]) - ); - } - $indexfile->save($data); - $data = null; - /**************************************/ - /** store comments in old data files **/ - /** TODO: remove as soon as admin **/ - /** panel uses new index file **/ - /**************************************/ - $filename = DATA_DIR . 'comments'; - $filename .= ($lang ? '/' . $lang : ''); - $filename .= $path . '.yaml'; - $file = CompiledYamlFile::instance($filename); - - if (file_exists($filename)) { - $data = $file->content(); - - $data['comments'][] = [ - 'text' => $text, - 'date' => date('D, d M Y H:i:s', time()), - 'author' => $name, - 'email' => $email - ]; - } else { - $data = array( - 'title' => $title, - 'lang' => $lang, - 'comments' => array([ - 'text' => $text, - 'date' => date('D, d M Y H:i:s', time()), - 'author' => $name, - 'email' => $email - ]) - ); - } - - $file->save($data); - - //clear cache - $this->grav['cache']->delete($this->comments_cache_id); + $this->saveComment($route, $path, $parent_id, $lang, $text, $name, $email, $title); break; } @@ -580,6 +548,9 @@ class CommentsPlugin extends Plugin $levelsflat[$parent_id]['class']->addSubComment($currentChild); } } + //youngest comments first (DESC date), only root comments. Keep replies in ASC date order. + //as long as comments are not editable, it is sufficient to reverse order from comment file + $leveltree = array_reverse($leveltree, true); //reset comment values to nested order $comments = array(); foreach($leveltree as $id => $comment) { diff --git a/languages.yaml b/languages.yaml index 812f22d..65b8db7 100644 --- a/languages.yaml +++ b/languages.yaml @@ -1,6 +1,10 @@ de: PLUGIN_COMMENTS: + ADD_NEW: Kommentar hinzufügen + ADD_REPLY: Antworten ADD_COMMENT: Kommentar hinzufügen + DELETE_COMMENT: Kommentar löschen + SUCCESS: Der Kommentar wurde erfolgreich gespeichert. COMMENTS: Kommentare EMAIL_NOT_CONFIGURED: Email nicht konfiguriert NEW_COMMENT_EMAIL_SUBJECT: 'Neuer Kommentar für %1$s' @@ -22,7 +26,11 @@ de: en: PLUGIN_COMMENTS: + ADD_NEW: Add a comment + ADD_REPLY: Reply ADD_COMMENT: Add a comment + DELETE_COMMENT: Delete comment + SUCCESS: Comment has been saved successfully. COMMENTS: Comments EMAIL_NOT_CONFIGURED: Email not configured NEW_COMMENT_EMAIL_SUBJECT: 'New comment on %1$s' diff --git a/templates/partials/comments.html.twig b/templates/partials/comments.html.twig index 1f61461..e50ead3 100644 --- a/templates/partials/comments.html.twig +++ b/templates/partials/comments.html.twig @@ -6,10 +6,10 @@ {% if grav.twig.comments|length %}

{{'PLUGIN_COMMENTS.COMMENTS'|t}}

- {{'PLUGIN_COMMENTS.ADD_NEW'|t}} + {{'PLUGIN_COMMENTS.ADD_NEW'|t}}
{% for comment in grav.twig.comments %} -
+
user icon @@ -18,7 +18,12 @@

{{comment.title}}

-
+
+ {{'PLUGIN_COMMENTS.ADD_REPLY'|t}} + {% if grav.user.access.admin.super %} + {{'PLUGIN_COMMENTS.DELETE'|t}} + {% endif %} +
{{'PLUGIN_COMMENTS.WRITTEN_ON'|t}} {{comment.date|e}} {{'PLUGIN_COMMENTS.BY'|t}} {{comment.author}}
From 905c04937c29119efc0951c6dce06ce02926f374 Mon Sep 17 00:00:00 2001 From: Thorsten Witteler Date: Wed, 25 Oct 2017 02:58:07 +0200 Subject: [PATCH 25/38] + ajax-delete comments via front end (if logged in with super admin priviledges) --- assets/comments.js | 92 ++++++++-- comments.php | 201 +++++++++++++++++++-- languages.yaml | 12 +- templates/partials/comments.form.html.twig | 5 +- templates/partials/comments.html.twig | 11 +- 5 files changed, 278 insertions(+), 43 deletions(-) diff --git a/assets/comments.js b/assets/comments.js index 3d63de1..c366189 100644 --- a/assets/comments.js +++ b/assets/comments.js @@ -2,9 +2,9 @@ function escapeRegExp(str) { return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); } jQuery(document).ready(function () { - var commentForm = $(document).find('.comments-form'); - var commentSection = $(document).find('.comments').first(); - var commentAlert = $(document).find('.alert').first(); + var commentForm = $('#comments-form'); //$(document).find('.comments-form').first(); + var commentSection = $('#comments-section'); //$(document).find('.comments').first(); + var commentAlert = $('#comments-alert'); //$(document).find('.alert').first(); //hide form, show link commentForm.hide(); @@ -13,20 +13,78 @@ jQuery(document).ready(function () { //show comment form above comments section (new comment thread) $('body').on('click', '.comment-add-new', function (e) { e.preventDefault(); - //commentForm.hide(1000); + if ($(this).prev().filter('#comments-form').length > 0) { + //form is already in the right place. + //just make sure it is visible. + commentForm.show(); + return; + } + commentForm.hide(); //hide it to make sure that it is not shown after move to make "show" transition work. $(this).before(commentForm); commentForm.show('slow'); - commentAlert.slideUp(); + commentAlert.empty().slideUp(); }); //show comment form below selected comment (reply to existing comment) $('body').on('click', '.comment-add-reply', function (e) { e.preventDefault(); - var media = $(this).closest('.comment'); + var comment = $(this).closest('.comment'); + if (comment.find('#comments-form').length > 0) { + //form is already in the right place. + //just make sure it is visible. + commentForm.show(); + return; + } commentForm.hide(); - media.find('>.comment-body>.comment-text').after(commentForm); + comment.find('.comment-body').last().append(commentForm); commentForm.show('slow'); - commentAlert.slideUp(); + commentAlert.empty().slideUp(); + }); + + //delete comment (authorized user only) + $('body').on('click', '.comment-delete', function (e) { + e.preventDefault(); + var comment = $(this).closest('.comment'); + var id = parseInt(comment.attr('data-id'), 10); + var level = parseInt(comment.attr('data-level'), 10); + var nonce = commentForm.find("input[name='form-nonce']").val(); + if (comment.next().filter(".comment[data-level='" + (level + 1) + "']").length > 0) { + alert('Deletion not allowed. There are replies to this comment. Please delete them first.'); + return; + } + var url = commentForm.attr( "action" ); + var posting = $.post(url, { action: 'delete', id: id, nonce: nonce}, null, 'json'); + // Register events to ajax call + posting.done(function (response) { + + //make sure that commentForm is definitely not within the deleted DOM part. + //hide + //temporary move it outside the comment selected for deletion. (this definitely exists, not taking any chances here) + //finally move back to start of commentSection. (preferred target) + //Hint: Don't forget commentAlert as it is not inside the form. + commentAlert.empty().hide(); + commentForm.hide(); + comment.before(commentForm); + comment.before(commentAlert); + commentSection.prepend(commentAlert); + commentSection.prepend(commentForm); + //remove the comment and all content from DOM. + //detach would be a soft delete but as there is no reason to reuse the deleted comment, means should not be provided. + comment.remove(); + }); + posting.fail(function (status, error, title) { +//alert('error'); +//console.log("Response Data (fail)", JSON.parse(JSON.stringify(status))); + commentForm.after(commentAlert); + commentAlert.show(); + commentAlert.empty().append("

Error:

"); + commentAlert.append("

" + JSON.stringify(status) + "

"); + commentAlert.append("

" + JSON.stringify(error) + "

"); + commentAlert.append("

" + JSON.stringify(title) + "

"); + }); + posting.always(function () { + //alert("finished, be it successful or not"); + }); }); // Attach a submit handler to the form @@ -65,14 +123,12 @@ jQuery(document).ready(function () { commentAlert.css('color', 'green').empty().append(document.createTextNode( response.message )).fadeIn(30); var newMedia = "
" + "
" + - "" + - "user icon" + - "" + + "user icon" + "
" + "
" + "
" + "

{{comment.title}}

" + - "" + + "" + "
{{'PLUGIN_COMMENTS.WRITTEN_ON'|t}} {{comment.date|e}} {{'PLUGIN_COMMENTS.BY'|t}} {{comment.author}}
" + "
" + "
" + @@ -92,6 +148,7 @@ jQuery(document).ready(function () { newMedia = newMedia.replace(new RegExp(escapeRegExp("{{comment.date|e}}"), 'g'), response.data.date); newMedia = newMedia.replace(new RegExp(escapeRegExp("{{nested}}"), 'g'), ''); newMedia = newMedia.replace(new RegExp(escapeRegExp("{{'PLUGIN_COMMENTS.ADD_REPLY'|t}}"), 'g'), response.data.ADD_REPLY); + newMedia = newMedia.replace(new RegExp(escapeRegExp("{{'PLUGIN_COMMENTS.REPLY'|t}}"), 'g'), response.data.REPLY); newMedia = newMedia.replace(new RegExp(escapeRegExp("{{'PLUGIN_COMMENTS.WRITTEN_ON'|t}}"), 'g'), response.data.WRITTEN_ON); newMedia = newMedia.replace(new RegExp(escapeRegExp("{{'PLUGIN_COMMENTS.BY'|t}}"), 'g'), response.data.BY); if ($( "div[data-id='" + response.data.parent_id + "']" ).length > 0) { @@ -101,7 +158,7 @@ jQuery(document).ready(function () { } } setTimeout(function () { - commentForm.hide(2000); + commentForm.slideUp(); commentAlert.fadeOut(5000); }, 5000); }); @@ -109,10 +166,11 @@ jQuery(document).ready(function () { //alert('error'); //console.log("Response Data (fail)", JSON.parse(JSON.stringify(status))); commentForm.after(commentAlert); - commentAlert.empty().append("

TEST

"); - commentAlert.append("

" + status + "

"); - commentAlert.append("

" + error + "

"); - commentAlert.append("

" + title + "

"); + commentAlert.show(); + commentAlert.empty().append("

Error:

"); + commentAlert.append("

" + JSON.stringify(status) + "

"); + commentAlert.append("

" + JSON.stringify(error) + "

"); + commentAlert.append("

" + JSON.stringify(title) + "

"); }); posting.always(function () { //alert("finished, be it successful or not"); diff --git a/comments.php b/comments.php index ecfbe11..5e9a67e 100644 --- a/comments.php +++ b/comments.php @@ -180,22 +180,71 @@ class CommentsPlugin extends Plugin // $this->only_full_stars = $this->config->get('plugins.star-ratings.only_full_stars'); $callback = $this->config->get('plugins.comments.ajax_callback'); // Process comment if required - if ($is_ajax || $callback === $this->grav['uri']->path()) { - // try to add the comment - $result = $this->addComment(true); - echo json_encode([ - 'status' => $result[0], - 'message' => $result[1], - 'data' => $result[2], -// 'data' => [ -// 'score' => $result[2][0], -// 'count' => $result[2][1] -// ] - ]); + if ($is_ajax) {// || $callback === $this->grav['uri']->path() + $action = filter_input(INPUT_POST, 'action', FILTER_SANITIZE_STRING); + switch ($action) { + case 'addComment': + case '': + case null: + // try to add the comment + $result = $this->addComment(true); + echo json_encode([ + 'status' => $result[0], + 'message' => $result[1], + 'data' => $result[2], + ]); + break; + case 'delete': + // try to delete the comment + $result = $this->deleteComment(true); + echo json_encode([ + 'status' => $result[0], + 'message' => $result[1], + 'data' => $result[2], + ]); + break; + default: + //request unknown, present error page + //Set a 400 (bad request) response code. + http_response_code(400); + echo 'request malformed - action unknown'; + break; + } exit(); //prevents the page frontend from beeing displayed. } } + public function deleteComment() + { + $language = $this->grav['language']; + if (!$this->grav['user']->authorize('admin.super')) { + http_response_code(403); + return [false, 'access forbidden', [0, 0]]; + } + $id = filter_input(INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT); + $nonce = filter_input(INPUT_POST, 'nonce', FILTER_SANITIZE_STRING); + // ensure both values are sent + if (is_null($id) || is_null($nonce)) { + // Set a 400 (bad request) response code and exit. + http_response_code(400); + return [false, 'request malformed - missing parameter(s)', [0, 0]]; + } + if (!Utils::verifyNonce($nonce, 'comments')) { + http_response_code(403); + return [false, 'Invalid security nonce', [0, $nonce]]; + } + $lang = $this->grav['language']->getLanguage(); + $path = $this->grav['page']->path(); + $route = $this->grav['page']->route(); + $data = $this->removeComment($route, $path, $id, $lang); + if ($data[0]) { + return [true, $language->translate('PLUGIN_COMMENTS.DELETE_SUCCESS'), $data[1]]; + } else { + http_response_code(403); //forbidden + return [false, $language->translate('PLUGIN_COMMENTS.DELETE_FAIL'), $data[1]]; + } + } + public function addComment($is_ajax = false) { if($is_ajax) { @@ -254,6 +303,7 @@ class CommentsPlugin extends Plugin 'level' => 0, 'hash' => md5(strtolower(trim($comment['email']))), 'ADD_REPLY' => $language->translate('PLUGIN_COMMENTS.ADD_REPLY'), + 'REPLY' => $language->translate('PLUGIN_COMMENTS.REPLY'), 'WRITTEN_ON' => $language->translate('PLUGIN_COMMENTS.WRITTEN_ON'), 'BY' => $language->translate('PLUGIN_COMMENTS.BY'), ); @@ -265,6 +315,123 @@ class CommentsPlugin extends Plugin } } + /** + * Handle form processing instructions. + * + * @param Event $event + */ + public function removeComment($route, $path, $id, $lang) + { + $entry_removed = false; + $message = ''; + $date = time();//date('D, d M Y H:i:s', time()); + + /******************************/ + /** store comments with page **/ + /******************************/ + $localfilename = $path . '/comments.yaml'; + $localfile = CompiledYamlFile::instance($localfilename); + if (file_exists($localfilename)) { + $data = $localfile->content(); + if(isset($data['comments']) && is_array($data['comments'])) { + foreach($data['comments'] as $key => $comment) { + if(!empty($comment['parent_id']) && $comment['parent_id'] == $id) { + //hit an existing comment that is a reply to comment selected for deletion. + //deletion of "parent" comment not allowed to preserve integrity of nested comments. + //TODO: Alternatively allow it to mark parent comments as deleted + // and make sure (via Comment class / setCommentLevels) that children are + // filtered out from fetch regardless of their own deletion state. + $data['comments'][$key] = array_merge(array('deleted' => ''), $comment); + //set date after merge + //reason: could be possible that "deleted" already exists (e.g. false or '') in $comment which would overwrite the first (newly added) occurence + $data['comments'][$key]['deleted'] = $date; + //no need to look further as ids are supposed to be unique. + $localfile->save($data); + $entry_removed = false; + $reply_id = empty($comment['id']) ? '' : $comment['id']; + $message = "Found active reply ($reply_id) for selected comment ($id)."; + return [$entry_removed, $message]; + break; + } + } + foreach($data['comments'] as $key => $comment) { + if(!empty($comment['id']) && $comment['id'] == $id) { + //add deleted as first item in array (better readability in file) + $data['comments'][$key] = array_merge(array('deleted' => ''), $comment); + //set date after merge + //reason: could be possible that "deleted" already exists (e.g. false or '') in $comment which would overwrite the first (newly added) occurence + $data['comments'][$key]['deleted'] = $date; + //no need to look further as ids are supposed to be unique. + $localfile->save($data); + $entry_removed = true; + $message = "Deleted comment ($id) via path ($path)"; + break; + } + } + } + } else { + //nothing + } + /**********************************/ + /** store comments in index file **/ + /**********************************/ + $indexfilename = DATA_DIR . 'comments/index.yaml'; + $indexfile = CompiledYamlFile::instance($indexfilename); + if (file_exists($indexfilename)) { + $dataIndex = $indexfile->content(); + if(isset($dataIndex['comments']) && is_array($dataIndex['comments'])) { + foreach($dataIndex['comments'] as $key => $comment) { + if(!empty($comment['page']) && !empty($comment['id']) && $comment['page'] == $route && $comment['id'] == $id) { + //add deleted as first item in array (better readability in file) + $dataIndex['comments'][$key] = array_merge(array('deleted' => ''), $comment); + //set date after merge + //reason: could be possible that "deleted" already exists (e.g. false or '') in $comment which would overwrite the first (newly added) occurence + $dataIndex['comments'][$key]['deleted'] = $date; + //no need to look further as ids are supposed to be unique. + $indexfile->save($dataIndex); + break; + } + } + } + } else { + //nothing + } + /**************************************/ + /** store comments in old data files **/ + /** TODO: remove as soon as admin **/ + /** panel uses new index file **/ + /**************************************/ + $filename = DATA_DIR . 'comments'; + $filename .= ($lang ? '/' . $lang : ''); + $filename .= $path . '.yaml'; + $file = CompiledYamlFile::instance($filename); + + if (file_exists($filename)) { + $dataLegacy = $file->content(); + if(isset($dataLegacy['comments']) && is_array($dataLegacy['comments'])) { + foreach($dataLegacy['comments'] as $key => $comment) { + if(!empty($comment['id']) && $comment['id'] == $id) { + //add deleted as first item in array (better readability in file) + $dataLegacy['comments'][$key] = array_merge(array('deleted' => ''), $comment); + //set date after merge + //reason: could be possible that "deleted" already exists (e.g. false or '') in $comment which would overwrite the first (newly added) occurence + $dataLegacy['comments'][$key]['deleted'] = $date; + //no need to look further as ids are supposed to be unique. + $file->save($dataLegacy); + break; + } + } + } + } else { + //nothing + } + + //clear cache + $this->grav['cache']->delete($this->comments_cache_id); + + return [$entry_removed, $message]; + } + /** * Handle form processing instructions. * @@ -532,8 +699,14 @@ class CommentsPlugin extends Plugin } $levelsflat = array(); foreach($comments as $key => $comment) { - $levelsflat[$comment['id']]['parent'] = $comment['parent']; - $levelsflat[$comment['id']]['class'] = new Comment($comment['id'], $comments[$key]); + if(!empty($comment['deleted'])) { + //if field "deleted" exists and is filled with a true value then ignore the comment completely. + //TODO: This only works on this position as long as it is forbidden to delete comments that have active replies (children). + // Otherwise implement that children get the deleted flag recursively or are ignored via Comment class. + } else { + $levelsflat[$comment['id']]['parent'] = $comment['parent']; + $levelsflat[$comment['id']]['class'] = new Comment($comment['id'], $comments[$key]); + } } //get starting points (entries without valid parent = root element) $leveltree = array(); diff --git a/languages.yaml b/languages.yaml index 65b8db7..efc3d5a 100644 --- a/languages.yaml +++ b/languages.yaml @@ -1,10 +1,12 @@ de: PLUGIN_COMMENTS: ADD_NEW: Kommentar hinzufügen - ADD_REPLY: Antworten + ADD_REPLY: Auf Kommentar antworten ADD_COMMENT: Kommentar hinzufügen DELETE_COMMENT: Kommentar löschen - SUCCESS: Der Kommentar wurde erfolgreich gespeichert. + REPLY: Antworten + DELETE: Löschen + SUCCESS: "Der Kommentar wurde erfolgreich gespeichert." COMMENTS: Kommentare EMAIL_NOT_CONFIGURED: Email nicht konfiguriert NEW_COMMENT_EMAIL_SUBJECT: 'Neuer Kommentar für %1$s' @@ -27,10 +29,12 @@ de: en: PLUGIN_COMMENTS: ADD_NEW: Add a comment - ADD_REPLY: Reply + ADD_REPLY: Reply to comment ADD_COMMENT: Add a comment DELETE_COMMENT: Delete comment - SUCCESS: Comment has been saved successfully. + REPLY: Reply + DELETE: Delete + SUCCESS: "Comment has been saved successfully." COMMENTS: Comments EMAIL_NOT_CONFIGURED: Email not configured NEW_COMMENT_EMAIL_SUBJECT: 'New comment on %1$s' diff --git a/templates/partials/comments.form.html.twig b/templates/partials/comments.form.html.twig index 5be563e..be8d586 100644 --- a/templates/partials/comments.form.html.twig +++ b/templates/partials/comments.form.html.twig @@ -1,5 +1,4 @@ -

{{'PLUGIN_COMMENTS.ADD_COMMENT'|t}}

-
@@ -35,4 +34,4 @@ {{ nonce_field('comments', 'form-nonce')|raw }}
-
{{ form.message }}
+
{{ form.message }}
diff --git a/templates/partials/comments.html.twig b/templates/partials/comments.html.twig index e50ead3..9728aa0 100644 --- a/templates/partials/comments.html.twig +++ b/templates/partials/comments.html.twig @@ -1,27 +1,27 @@ {% if grav.twig.enable_comments_plugin %} {% set scope = scope ?: 'data.' %} +
+

{{'PLUGIN_COMMENTS.COMMENTS'|t}}

+ {% include 'partials/comments.form.html.twig' %} {% if grav.twig.comments|length %} -

{{'PLUGIN_COMMENTS.COMMENTS'|t}}

{{'PLUGIN_COMMENTS.ADD_NEW'|t}}
{% for comment in grav.twig.comments %}
- user icon -

{{comment.title}}

{{'PLUGIN_COMMENTS.WRITTEN_ON'|t}} {{comment.date|e}} {{'PLUGIN_COMMENTS.BY'|t}} {{comment.author}}
@@ -36,6 +36,7 @@
{% endif %} +
{% endif %} From 9179453c85acb88d223ba71db51e4ccdef1a17ec Mon Sep 17 00:00:00 2001 From: codeshell Date: Sun, 29 Oct 2017 01:49:09 +0200 Subject: [PATCH 26/38] fix absolute class path add recent comments widget as twig function add save authentication and admin status with comments --- comments.php | 327 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 225 insertions(+), 102 deletions(-) diff --git a/comments.php b/comments.php index 5e9a67e..0d9813d 100644 --- a/comments.php +++ b/comments.php @@ -1,23 +1,15 @@ grav['twig']->enable_comments_plugin = $this->enable; $this->grav['twig']->comments = $this->fetchComments(); + $this->grav['twig']->recentComments = $this->getRecentComments(); if ($this->config->get('plugins.comments.built_in_css')) { $this->grav['assets'] ->addCss('plugin://comments/assets/comments.css'); @@ -99,7 +92,7 @@ class CommentsPlugin extends Plugin /** * Frontend side initialization */ - public function initializeFrontend() + private function initializeFrontend() { $this->calculateEnable(); @@ -126,7 +119,7 @@ class CommentsPlugin extends Plugin /** * Admin side initialization */ - public function initializeAdmin() + private function initializeAdmin() { /** @var Uri $uri */ $uri = $this->grav['uri']; @@ -134,6 +127,9 @@ class CommentsPlugin extends Plugin $this->enable([ 'onTwigTemplatePaths' => ['onTwigAdminTemplatePaths', 0], 'onAdminMenu' => ['onAdminMenu', 0], + 'onAdminTaskExecute' => ['onAdminTaskExecute', 0], + 'onAdminAfterSave' => ['onAdminAfterSave', 0], + 'onAdminAfterDelete' => ['onAdminAfterDelete', 0], 'onDataTypeExcludeFromDataManagerPluginHook' => ['onDataTypeExcludeFromDataManagerPluginHook', 0], ]); @@ -157,6 +153,22 @@ class CommentsPlugin extends Plugin */ public function onPluginsInitialized() { + if ('/recent' === $this->grav['uri']->path()) { + //TODO TEST + echo PAGES_DIR; + + echo "
"; + + $test = $this->getRecentComments(25); + var_dump($test[0]); + echo '


'; + var_dump($test[1]); + echo '


'; + var_dump($test[2]); + echo '


'; + exit(); + } + if ($this->isAdmin()) { $this->initializeAdmin(); } else { @@ -170,15 +182,7 @@ class CommentsPlugin extends Plugin public function onPageInitialized() { $is_ajax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'; - // initialize with page settings (post-cache) -// if (!$this->isAdmin() && isset($this->grav['page']->header()->{'star-ratings'})) { -// // if not in admin merge potential page-level configs -// $this->config->set('plugins.star-ratings', $this->mergeConfig($page)); -// } -// $this->callback = $this->config->get('plugins.star-ratings.callback'); -// $this->total_stars = $this->config->get('plugins.star-ratings.total_stars'); -// $this->only_full_stars = $this->config->get('plugins.star-ratings.only_full_stars'); - $callback = $this->config->get('plugins.comments.ajax_callback'); + //$callback = $this->config->get('plugins.comments.ajax_callback'); // Process comment if required if ($is_ajax) {// || $callback === $this->grav['uri']->path() $action = filter_input(INPUT_POST, 'action', FILTER_SANITIZE_STRING); @@ -187,7 +191,7 @@ class CommentsPlugin extends Plugin case '': case null: // try to add the comment - $result = $this->addComment(true); + $result = $this->addCommentAjax(true); echo json_encode([ 'status' => $result[0], 'message' => $result[1], @@ -214,7 +218,12 @@ class CommentsPlugin extends Plugin } } - public function deleteComment() + /** + * Validate ajax input before deleting comment + * + * @return boolean[]|string[]|array[][] + */ + private function deleteComment() { $language = $this->grav['language']; if (!$this->grav['user']->authorize('admin.super')) { @@ -237,7 +246,7 @@ class CommentsPlugin extends Plugin $path = $this->grav['page']->path(); $route = $this->grav['page']->route(); $data = $this->removeComment($route, $path, $id, $lang); - if ($data[0]) { + if ($data[0]) { return [true, $language->translate('PLUGIN_COMMENTS.DELETE_SUCCESS'), $data[1]]; } else { http_response_code(403); //forbidden @@ -245,9 +254,13 @@ class CommentsPlugin extends Plugin } } - public function addComment($is_ajax = false) + /** + * Validate ajax input before adding comment + * + * @return boolean[]|string[]|array[][] + */ + private function addCommentAjax() { - if($is_ajax) { $language = $this->grav['language']; if (!$_SERVER["REQUEST_METHOD"] == "POST") { // Not a POST request, set a 403 (forbidden) response code. @@ -273,7 +286,7 @@ class CommentsPlugin extends Plugin $input['form-nonce'] = filter_input(INPUT_POST, 'form-nonce', FILTER_SANITIZE_STRING); if (!Utils::verifyNonce($input['form-nonce'], 'comments')) { http_response_code(403); - return [false, 'Invalid security nonce', [$_POST, $input['form-nonce']]]; + return [false, 'Invalid security nonce', [0, $input['form-nonce']]]; } // ensure both values are sent if (is_null($input['title']) || is_null($input['text'])) { @@ -291,7 +304,9 @@ class CommentsPlugin extends Plugin $lang = $this->grav['language']->getLanguage(); $path = $this->grav['page']->path(); $route = $this->grav['page']->route(); - $comment = $this->saveComment($route, $path, $input['parent_id'], $lang, $input['text'], $input['name'], $input['email'], $input['title']); + $user = $this->grav['user']->authenticated ? $this->grav['user']->username : ''; + $isAdmin = $this->grav['user']->authorize('admin.login'); + $comment = $this->saveComment($route, $path, $input['parent_id'], $lang, $input['text'], $input['name'], $input['email'], $input['title'], $user, $isAdmin); //$comments = $this->fetchComments(); $data = array( 'parent_id' => $comment['parent'], @@ -300,19 +315,15 @@ class CommentsPlugin extends Plugin 'title' => $comment['title'], 'name' => $comment['author'], 'date' => $comment['date'], - 'level' => 0, 'hash' => md5(strtolower(trim($comment['email']))), + 'authenticated' => !empty($comment['user']), + 'isAdmin' => !empty($comment['isAdmin']), 'ADD_REPLY' => $language->translate('PLUGIN_COMMENTS.ADD_REPLY'), 'REPLY' => $language->translate('PLUGIN_COMMENTS.REPLY'), 'WRITTEN_ON' => $language->translate('PLUGIN_COMMENTS.WRITTEN_ON'), 'BY' => $language->translate('PLUGIN_COMMENTS.BY'), ); return [true, $language->translate('PLUGIN_COMMENTS.SUCCESS'), $data]; - } else { -// Set a 500 (internal server error) response code. -// http_response_code(500); - - } } /** @@ -320,7 +331,7 @@ class CommentsPlugin extends Plugin * * @param Event $event */ - public function removeComment($route, $path, $id, $lang) + private function removeComment($route, $path, $id, $lang) { $entry_removed = false; $message = ''; @@ -396,36 +407,6 @@ class CommentsPlugin extends Plugin } else { //nothing } - /**************************************/ - /** store comments in old data files **/ - /** TODO: remove as soon as admin **/ - /** panel uses new index file **/ - /**************************************/ - $filename = DATA_DIR . 'comments'; - $filename .= ($lang ? '/' . $lang : ''); - $filename .= $path . '.yaml'; - $file = CompiledYamlFile::instance($filename); - - if (file_exists($filename)) { - $dataLegacy = $file->content(); - if(isset($dataLegacy['comments']) && is_array($dataLegacy['comments'])) { - foreach($dataLegacy['comments'] as $key => $comment) { - if(!empty($comment['id']) && $comment['id'] == $id) { - //add deleted as first item in array (better readability in file) - $dataLegacy['comments'][$key] = array_merge(array('deleted' => ''), $comment); - //set date after merge - //reason: could be possible that "deleted" already exists (e.g. false or '') in $comment which would overwrite the first (newly added) occurence - $dataLegacy['comments'][$key]['deleted'] = $date; - //no need to look further as ids are supposed to be unique. - $file->save($dataLegacy); - break; - } - } - } - } else { - //nothing - } - //clear cache $this->grav['cache']->delete($this->comments_cache_id); @@ -437,7 +418,7 @@ class CommentsPlugin extends Plugin * * @param Event $event */ - public function saveComment($route, $path, $parent_id, $lang, $text, $name, $email, $title) + private function saveComment($route, $path, $parent_id, $lang, $text, $name, $email, $title, $user = "", $isAdmin = false) { $date = date('D, d M Y H:i:s', time()); @@ -464,7 +445,9 @@ class CommentsPlugin extends Plugin 'text' => $text, 'date' => $date, 'author' => $name, - 'email' => $email + 'email' => $email, + 'user' => $user, + 'isAdmin' => !empty($isAdmin), ]; $data['comments'][] = $newComment; $localfile->save($data); @@ -493,39 +476,6 @@ class CommentsPlugin extends Plugin ]; $indexfile->save($dataIndex); - /**************************************/ - /** store comments in old data files **/ - /** TODO: remove as soon as admin **/ - /** panel uses new index file **/ - /**************************************/ - $filename = DATA_DIR . 'comments'; - $filename .= ($lang ? '/' . $lang : ''); - $filename .= $path . '.yaml'; - $file = CompiledYamlFile::instance($filename); - - if (file_exists($filename)) { - $dataLegacy = $file->content(); - - $dataLegacy['comments'][] = [ - 'text' => $text, - 'date' => $date, - 'author' => $name, - 'email' => $email - ]; - } else { - $dataLegacy = array( - 'title' => $title, - 'lang' => $lang, - 'comments' => array([ - 'text' => $text, - 'date' => $date, - 'author' => $name, - 'email' => $email - ]) - ); - } - - $file->save($dataLegacy); //clear cache $this->grav['cache']->delete($this->comments_cache_id); @@ -560,12 +510,16 @@ class CommentsPlugin extends Plugin $title = filter_var(urldecode($post['title']), FILTER_SANITIZE_STRING); $parent_id = 0; + $username = ''; + $isAdmin = false; if (isset($this->grav['user'])) { $user = $this->grav['user']; if ($user->authenticated) { $name = $user->fullname; $email = $user->email; } + $username = $this->grav['user']->authenticated ? $this->grav['user']->username : ''; + $isAdmin = $this->grav['user']->authorize('admin.login'); } /** @var Language $language */ @@ -574,12 +528,109 @@ class CommentsPlugin extends Plugin $path = $this->grav['page']->path(); $route = $this->grav['page']->route(); - $this->saveComment($route, $path, $parent_id, $lang, $text, $name, $email, $title); + $this->saveComment($route, $path, $parent_id, $lang, $text, $name, $email, $title, $username, $isAdmin); break; } } + private function getRecentComments($limit = 10) { + //TODO +/* + //init cache id + $cache = $this->grav['cache']; + $comments_cache_id = md5('comments-data' . $cache->getKey() . '-' . $uri->url()); + $uri = $this->grav['uri']; + + //search in cache + if ($comments = $cache->fetch($comments_cache_id)) { + return $comments; + } + + $comments = $this->getDataFromFilename($filename)['comments']; + $comments = $this->setCommentLevels($comments); + //save to cache if enabled + $cache->save($this->comments_cache_id, $comments); + */ + $path = PAGES_DIR; + $dirItr = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); + $itrFilter = new \RecursiveIteratorIterator($dirItr, \RecursiveIteratorIterator::SELF_FIRST); + $filesItr = new \RegexIterator($itrFilter, '/^.+comments\.yaml$/i'); + $files = array(); + $global_stats = array( + 'active_entries' => 0, + 'deleted_entries' => 0, + 'active_comments' => 0, + 'deleted_comments' => 0, + 'active_replies' => 0, + 'deleted_replies' => 0, + 'pages_with_active_entries' => 0, + ); + $page_stats = array(); + $comments = array(); + foreach ($filesItr as $filepath => $file) { + if ($file->isDir()) { + // this should never trigger as we are looking vor yamls only + } else { + $page_stats[$filepath] = array( + 'active_entries' => 0, + 'deleted_entries' => 0, + 'active_comments' => 0, + 'deleted_comments' => 0, + 'active_replies' => 0, + 'deleted_replies' => 0, + 'latest_active_entry' => 0, + ); + $localfile = CompiledYamlFile::instance($filepath); + $localcomments = $localfile->content(); + if (!empty($localcomments['comments']) && is_array($localcomments['comments'])) { + foreach ($localcomments['comments'] as $comment) { + if (!empty($comment['deleted'])) { + empty($comment['parent']) ? $page_stats[$filepath]['deleted_comments']++ : $page_stats[$filepath]['deleted_replies']++; + empty($comment['parent']) ? $global_stats['deleted_comments']++ : $global_stats['deleted_replies']++; + $page_stats[$filepath]['deleted_entries']++; + $global_stats['deleted_entries']++; + } else { + empty($comment['parent']) ? $page_stats[$filepath]['active_comments']++ : $page_stats[$filepath]['active_replies']++; + empty($comment['parent']) ? $global_stats['active_comments']++ : $global_stats['active_replies']++; + $page_stats[$filepath]['active_entries']++; + $global_stats['active_entries']++; + + //use unix timestamp for comparing and sorting + if(is_int($comment['date'])) { + $time = $comment['date']; + } else { + $time = \DateTime::createFromFormat('D, d M Y H:i:s', $comment['date'])->getTimestamp(); + } + + if (empty($page_stats[$filepath]['latest_active_entry']) || $page_stats[$filepath]['latest_active_entry'] < $time) { + $page_stats[$filepath]['latest_active_entry'] = $time; + } + + $comments[] = array_merge(array( + 'path' => $filepath, + 'time' => $time, + ), $comment); + } + } + } + if (!empty($page_stats[$filepath]['latest_active_entry'])) { + $global_stats['pages_with_active_entries']++; + } + } + } + usort($comments, function($a, $b) { + if ($a['time'] === $b['time']) return 0; + if ($a['time'] < $b['time']) return 1; + return -1; + }); + if (!empty($limit) && $limit > 0 && $limit < count($comments)) { + return [$global_stats, $page_stats, array_slice($comments, 0, $limit)]; + } else { + return [$global_stats, $page_stats, $comments]; + } + } + private function getFilesOrderedByModifiedDate($path = '') { $files = []; @@ -787,12 +838,84 @@ class CommentsPlugin extends Plugin $this->grav['twig']->twig_paths[] = __DIR__ . '/admin/templates'; } + /** + * Handle the Reindex task from the admin + * + * @param Event $e + */ + public function onAdminTaskExecute(Event $e) + { + if ($e['method'] == 'taskReindexComments') { + $controller = $e['controller']; + header('Content-type: application/json'); + if (!$controller->authorizeTask('reindexComments', ['admin.configuration', 'admin.super'])) { + $json_response = [ + 'status' => 'error', + 'message' => ' Index not created', + 'details' => 'Insufficient permissions to reindex the comments index file.' + ]; + echo json_encode($json_response); + exit; + } +/*TODO // disable warnings + error_reporting(1); + // capture content + ob_start(); + $this->gtnt->createIndex(); + ob_get_clean(); + list($status, $msg) = $this->getIndexCount(); + $json_response = [ + 'status' => $status ? 'success' : 'error', + 'message' => ' ' . $msg + ]; + echo json_encode($json_response); + */ exit; + } + } + + /** + * Perform an 'add' or 'update' for comment data as needed + * + * @param $event + * @return bool + */ + public function onAdminAfterSave($event) + { + $obj = $event['object']; + if ($obj instanceof Page) { + //nothing to do + //save means, the page changed, but still exists + } + return true; + } + /** + * Perform an 'add' or 'update' for comment data as needed + * + * @param $event + * @return bool + */ + public function onAdminAfterDelete($event) + { + $obj = $event['object']; + if ($obj instanceof Page) { + //TODO $this->deleteComment($obj); + } + return true; + } + /** * Add navigation item to the admin plugin */ public function onAdminMenu() { $this->grav['twig']->plugins_hooked_nav['PLUGIN_COMMENTS.COMMENTS'] = ['route' => $this->route, 'icon' => 'fa-file-text']; + $options = [ + 'authorize' => 'taskReindexComments', + 'hint' => 'reindexes the comments index', + 'class' => 'comments-reindex', + 'icon' => 'fa-file-text' + ]; + $this->grav['twig']->plugins_quick_tray['PLUGIN_COMMENTS.COMMENTS'] = $options; } /** From bd1799cc7e377d9770fab3e1ba2ca71b9209679b Mon Sep 17 00:00:00 2001 From: codeshell Date: Mon, 30 Oct 2017 12:18:50 +0100 Subject: [PATCH 27/38] add recent comments widget as plugin template (partials/recentcomments.html.twig) --- comments.php | 103 ++++++++++++-------- languages.yaml | 6 ++ templates/partials/recentcomments.html.twig | 54 ++++++++++ 3 files changed, 124 insertions(+), 39 deletions(-) create mode 100644 templates/partials/recentcomments.html.twig diff --git a/comments.php b/comments.php index 0d9813d..0a6a9d8 100644 --- a/comments.php +++ b/comments.php @@ -9,6 +9,7 @@ use Grav\Common\Filesystem\RecursiveFolderFilterIterator; use Grav\Common\Page\Page; use RocketTheme\Toolbox\Event\Event; use Symfony\Component\Yaml\Yaml; +use Twig_SimpleFunction; require_once PLUGINS_DIR . 'comments/class/Comment.php'; class CommentsPlugin extends Plugin @@ -48,7 +49,10 @@ class CommentsPlugin extends Plugin public function onTwigSiteVariables() { $this->grav['twig']->enable_comments_plugin = $this->enable; $this->grav['twig']->comments = $this->fetchComments(); - $this->grav['twig']->recentComments = $this->getRecentComments(); + //$this->grav['twig']->recent_comments = $this->getRecentComments(); //cannot be used for functions with arguments + $function = new Twig_SimpleFunction('recent_comments', [$this, 'getRecentComments']); + $this->grav['twig']->twig()->addFunction($function); + if ($this->config->get('plugins.comments.built_in_css')) { $this->grav['assets'] ->addCss('plugin://comments/assets/comments.css'); @@ -153,22 +157,6 @@ class CommentsPlugin extends Plugin */ public function onPluginsInitialized() { - if ('/recent' === $this->grav['uri']->path()) { - //TODO TEST - echo PAGES_DIR; - - echo "
"; - - $test = $this->getRecentComments(25); - var_dump($test[0]); - echo '


'; - var_dump($test[1]); - echo '


'; - var_dump($test[2]); - echo '


'; - exit(); - } - if ($this->isAdmin()) { $this->initializeAdmin(); } else { @@ -181,7 +169,7 @@ class CommentsPlugin extends Plugin */ public function onPageInitialized() { - $is_ajax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'; + $is_ajax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'; //$callback = $this->config->get('plugins.comments.ajax_callback'); // Process comment if required if ($is_ajax) {// || $callback === $this->grav['uri']->path() @@ -534,24 +522,34 @@ class CommentsPlugin extends Plugin } } - private function getRecentComments($limit = 10) { - //TODO -/* - //init cache id + /** + * Used to add a recent comments widget. Call {{ recent_comments(123,12) }} specifying an integer representing the result length. + * + * Returns three different arrays with stats and comments. + * + * @param integer $limit max amount of comments in result set + * @param integer $limit_pages max amount of pages in result set + * + * @return array|array|array global stats, page stats, list of recent comments, options + */ + public function getRecentComments($limit, $limit_pages) + { + $routes = $this->grav['pages']->routes(); //routes[route] => path + $paths = array_flip($routes); $cache = $this->grav['cache']; - $comments_cache_id = md5('comments-data' . $cache->getKey() . '-' . $uri->url()); - $uri = $this->grav['uri']; - - //search in cache - if ($comments = $cache->fetch($comments_cache_id)) { - return $comments; + $options = array( + 'comments_limit' => $limit, + 'pages_limit' => $limit_pages, + ); + //use cached stats if possible + $recent_comments_cache_id = md5('comments-stats' . $cache->getKey()); + if ($recent_comments = $cache->fetch($recent_comments_cache_id)) { + //use cache only if limits are big enough + if($recent_comments['options']['comments_limit'] >= $options['comments_limit'] && $recent_comments['options']['pages_limit'] >= $options['pages_limit']) { + return $recent_comments; + } } - $comments = $this->getDataFromFilename($filename)['comments']; - $comments = $this->setCommentLevels($comments); - //save to cache if enabled - $cache->save($this->comments_cache_id, $comments); - */ $path = PAGES_DIR; $dirItr = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); $itrFilter = new \RecursiveIteratorIterator($dirItr, \RecursiveIteratorIterator::SELF_FIRST); @@ -570,8 +568,12 @@ class CommentsPlugin extends Plugin $comments = array(); foreach ($filesItr as $filepath => $file) { if ($file->isDir()) { - // this should never trigger as we are looking vor yamls only + // this should never trigger as we are looking vor yamls only } else { + $route = ''; + $fileFolder = substr($filepath, 0, strlen($filepath) - strlen($file->getFilename()) - 1); + if (!empty($paths[str_replace('/', '\\', $fileFolder)])) $route = $paths[str_replace('/', '\\', $fileFolder)]; + if (!empty($paths[str_replace('\\', '/', $fileFolder)])) $route = $paths[str_replace('\\', '/', $fileFolder)]; $page_stats[$filepath] = array( 'active_entries' => 0, 'deleted_entries' => 0, @@ -580,6 +582,7 @@ class CommentsPlugin extends Plugin 'active_replies' => 0, 'deleted_replies' => 0, 'latest_active_entry' => 0, + 'route' => $route, ); $localfile = CompiledYamlFile::instance($filepath); $localcomments = $localfile->content(); @@ -609,6 +612,7 @@ class CommentsPlugin extends Plugin $comments[] = array_merge(array( 'path' => $filepath, + 'route' => $route, 'time' => $time, ), $comment); } @@ -619,16 +623,34 @@ class CommentsPlugin extends Plugin } } } - usort($comments, function($a, $b) { + + //most recent comments first + usort($comments, function ($a, $b) { if ($a['time'] === $b['time']) return 0; if ($a['time'] < $b['time']) return 1; return -1; }); + + //most recent pages first + usort($page_stats, function ($a, $b) { + if ($a['latest_active_entry'] === $b['latest_active_entry']) return 0; + if ($a['latest_active_entry'] < $b['latest_active_entry']) return 1; + return -1; + }); + + //reduce comments in output to limit if (!empty($limit) && $limit > 0 && $limit < count($comments)) { - return [$global_stats, $page_stats, array_slice($comments, 0, $limit)]; - } else { - return [$global_stats, $page_stats, $comments]; + $comments = array_slice($comments, 0, $limit); } + //reduce pages in output to limit + if (!empty($limit_pages) && $limit_pages > 0 && $limit_pages < count($page_stats)) { + $page_stats = array_slice($page_stats, 0, $limit_pages); + } + + //save to cache if enabled + $cache->save($recent_comments_cache_id, ['global_stats' => $global_stats, 'pages' => $page_stats, 'comments' => $comments, 'options' => $options]); + + return ['global_stats' => $global_stats, 'pages' => $page_stats, 'comments' => $comments, 'options' => $options]; } private function getFilesOrderedByModifiedDate($path = '') { @@ -723,7 +745,7 @@ class CommentsPlugin extends Plugin /** * Return the comments associated to the current route */ - private function fetchComments() { + public function fetchComments() { $cache = $this->grav['cache']; //search in cache if ($comments = $cache->fetch($this->comments_cache_id)) { @@ -899,6 +921,9 @@ class CommentsPlugin extends Plugin $obj = $event['object']; if ($obj instanceof Page) { //TODO $this->deleteComment($obj); + + //clear cache + $this->grav['cache']->delete(md5('comments-stats' . $this->grav['cache']->getKey())); } return true; } diff --git a/languages.yaml b/languages.yaml index efc3d5a..81d7de5 100644 --- a/languages.yaml +++ b/languages.yaml @@ -8,6 +8,9 @@ de: DELETE: Löschen SUCCESS: "Der Kommentar wurde erfolgreich gespeichert." COMMENTS: Kommentare + COMMENTS_STATS: Kommentare + RECENT_COMMENTS: Neue Kommentare + RECENT_PAGES: Kommentierte Seiten EMAIL_NOT_CONFIGURED: Email nicht konfiguriert NEW_COMMENT_EMAIL_SUBJECT: 'Neuer Kommentar für %1$s' NEW_COMMENT_EMAIL_BODY: '

Ein neuer Kommentar am %1$s von %3$s (%4$s).

Seite: %2$s

Text: %5$s

' @@ -36,6 +39,9 @@ en: DELETE: Delete SUCCESS: "Comment has been saved successfully." COMMENTS: Comments + COMMENTS_STATS: Comments + RECENT_COMMENTS: Recent comments + RECENT_PAGES: Commented pages EMAIL_NOT_CONFIGURED: Email not configured NEW_COMMENT_EMAIL_SUBJECT: 'New comment on %1$s' NEW_COMMENT_EMAIL_BODY: '

A new comment was made on %1$s by %3$s (%4$s).

Page: %2$s

Text: %5$s

' diff --git a/templates/partials/recentcomments.html.twig b/templates/partials/recentcomments.html.twig new file mode 100644 index 0000000..d8084e3 --- /dev/null +++ b/templates/partials/recentcomments.html.twig @@ -0,0 +1,54 @@ +{# you may set options when using this partial. Example: include 'partials/recentcomments.html.twig' with {'limit': 5, 'pages_limit': 3} #} +{% if grav.twig.enable_comments_plugin %} +

{'PLUGIN_COMMENTS.COMMENTS_STATS'|t}}

+ {% set stats = recent_comments(limit|default(5), pages_limit|default(3)) %} + {% if stats.global_stats.active_entries %} + {{stats.global_stats.active_entries}} + ( {{stats.global_stats.deleted_entries}}) + - {{stats.global_stats.active_comments}} + ( {{stats.global_stats.deleted_comments}}) + - {{stats.global_stats.active_replies}} + ( {{stats.global_stats.deleted_replies}}) + - {{stats.global_stats.pages_with_active_entries}} + {% endif %} + {% for key, entry in stats.pages %} + {% if loop.first %} +

{'PLUGIN_COMMENTS.RECENT_PAGES'|t}} (limit {{stats.options.pages_limit}})

+ + {% endif %} + {% endfor %} + {% for key, entry in stats.comments %} + {% if loop.first %} +

{'PLUGIN_COMMENTS.RECENT_COMMENTS'|t}} (limit {{stats.options.comments_limit}})

+ + {% endif %} + {% endfor %} +{% endif %} From ee89c2bdcae8c1995513f90217438a4bfa1a4477 Mon Sep 17 00:00:00 2001 From: codeshell Date: Mon, 30 Oct 2017 12:53:13 +0100 Subject: [PATCH 28/38] fix ajax display when posting first comment on a page --- assets/comments.js | 2 +- templates/partials/comments.html.twig | 19 +++++++------------ templates/partials/recentcomments.html.twig | 6 +++--- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/assets/comments.js b/assets/comments.js index c366189..11d6225 100644 --- a/assets/comments.js +++ b/assets/comments.js @@ -154,7 +154,7 @@ jQuery(document).ready(function () { if ($( "div[data-id='" + response.data.parent_id + "']" ).length > 0) { $( "div[data-id='" + response.data.parent_id + "']" ).first().after(newMedia); } else { - $( "div.comments" ).last().prepend(newMedia); + $( "#comments" ).prepend(newMedia); } } setTimeout(function () { diff --git a/templates/partials/comments.html.twig b/templates/partials/comments.html.twig index 9728aa0..a39d91f 100644 --- a/templates/partials/comments.html.twig +++ b/templates/partials/comments.html.twig @@ -2,14 +2,12 @@ {% set scope = scope ?: 'data.' %}

{{'PLUGIN_COMMENTS.COMMENTS'|t}}

- - - {% include 'partials/comments.form.html.twig' %} - - {% if grav.twig.comments|length %} - {{'PLUGIN_COMMENTS.ADD_NEW'|t}} -
+ + {% include 'partials/comments.form.html.twig' %} + +
+ {% if grav.twig.comments|length %} {% for comment in grav.twig.comments %}
@@ -33,10 +31,7 @@
{% endfor %} -
- - {% endif %} + {% endif %} +
{% endif %} - - diff --git a/templates/partials/recentcomments.html.twig b/templates/partials/recentcomments.html.twig index d8084e3..296535d 100644 --- a/templates/partials/recentcomments.html.twig +++ b/templates/partials/recentcomments.html.twig @@ -1,6 +1,6 @@ {# you may set options when using this partial. Example: include 'partials/recentcomments.html.twig' with {'limit': 5, 'pages_limit': 3} #} {% if grav.twig.enable_comments_plugin %} -

{'PLUGIN_COMMENTS.COMMENTS_STATS'|t}}

+

{{'PLUGIN_COMMENTS.COMMENTS_STATS'|t}}

{% set stats = recent_comments(limit|default(5), pages_limit|default(3)) %} {% if stats.global_stats.active_entries %} {{stats.global_stats.active_entries}} @@ -13,7 +13,7 @@ {% endif %} {% for key, entry in stats.pages %} {% if loop.first %} -

{'PLUGIN_COMMENTS.RECENT_PAGES'|t}} (limit {{stats.options.pages_limit}})

+

{{'PLUGIN_COMMENTS.RECENT_PAGES'|t}} (limit {{stats.options.pages_limit}})

    {% endif %}
  • @@ -31,7 +31,7 @@ {% endfor %} {% for key, entry in stats.comments %} {% if loop.first %} -

    {'PLUGIN_COMMENTS.RECENT_COMMENTS'|t}} (limit {{stats.options.comments_limit}})

    +

    {{'PLUGIN_COMMENTS.RECENT_COMMENTS'|t}} (limit {{stats.options.comments_limit}})

      {% endif %} {% set entry_icon = 'fa-comment' %} From a52c1b2f6d5ff098dbd48db0291dd56589447a90 Mon Sep 17 00:00:00 2001 From: Ernst Date: Fri, 1 Dec 2017 17:53:07 -0800 Subject: [PATCH 29/38] Imported Pingbacks can display or be toggled off. You can also display comments but have adding new ones disabled. TODO: allow disabling comments on a per page basis. --- README.md | 7 +- blueprints.yaml | 22 +- comments.php | 59 ++- comments.yaml | 13 +- extern/habariakismet/habariakismet.plugin.php | 148 +++++++ extern/habariakismet/habariakismet.plugin.xml | 10 + extern/habariakismet/vendor/Akismet.class.php | 392 ++++++++++++++++++ languages.yaml | 2 + templates/partials/comments.html.twig | 128 ++++-- 9 files changed, 735 insertions(+), 46 deletions(-) create mode 100644 extern/habariakismet/habariakismet.plugin.php create mode 100644 extern/habariakismet/habariakismet.plugin.xml create mode 100644 extern/habariakismet/vendor/Akismet.class.php diff --git a/README.md b/README.md index a403d84..7c6b54d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ -# Grav Comments Plugin +# Grav Comments Plugin \[Fork\] + +This plugin adds support for displaying pingbacks, toggling the display of pingbacks. I'm not yet sure how pingbacks work, so this may not include support for adding new pingbacks; if it won't take too much work I'll add more support for them. + +The main goal of this fork is to add support for Akismet. Recaptcha guards from bots, but Akismet helps deal with actual spam content. +--- The **Comments Plugin** for [Grav](http://github.com/getgrav/grav) adds the ability to add comments to pages, and moderate them. diff --git a/blueprints.yaml b/blueprints.yaml index 1daf30b..9caf823 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -1,5 +1,5 @@ name: Comments -version: 1.2.7 +version: 1.2.8 description: Adds a commenting functionality to your site icon: comment author: @@ -29,3 +29,23 @@ form: 0: PLUGIN_ADMIN.DISABLED validate: type: bool + pingbacks: + type: toggle + label: Pingbacks + highlight: 1 + default: 0 + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED + validate: + type: bool + commenting: + type: toggle + label: Commenting + highlight: 1 + default: 0 + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED + validate: + type: bool diff --git a/comments.php b/comments.php index 7a59543..b1b0b7f 100644 --- a/comments.php +++ b/comments.php @@ -1,6 +1,9 @@ grav['twig']->enable_comments_plugin = $this->enable; + $this->grav['twig']->commenting_enabled = $this->commenting_enabled; + $this->grav['twig']->pingbacks_enabled = $this->pingbacks_enabled; $this->grav['twig']->comments = $this->fetchComments(); + $this->grav['twig']->pingbacks = $this->fetchPingbacks(); } /** @@ -100,6 +110,13 @@ class CommentsPlugin extends Plugin } } } + + #$blueprint = $this->getBlueprint(); + #$this->commenting_enabled = $blueprint->get('form/fields/commenting', false, '/'); + #$this->pingbacks_enabled = $blueprint->get('form/fields/pingbacks' , false, '/'); + + $this->commenting_enabled = $this->grav['config']->get('plugins.comments.commenting'); + $this->pingbacks_enabled = $this->grav['config']->get('plugins.comments.pingbacks'); } /** @@ -127,6 +144,8 @@ class CommentsPlugin extends Plugin //init cache id $this->comments_cache_id = md5('comments-data' . $cache->getKey() . '-' . $uri->url()); + + $this->pingbacks_cache_id = md5('pingbacks-data' . $cache->getKey() . '-' . $uri->url()); } /** @@ -185,16 +204,21 @@ class CommentsPlugin extends Plugin return; } + if (!$this->commenting_enabled) { + return; + } + switch ($action) { case 'addComment': $post = isset($_POST['data']) ? $_POST['data'] : []; - $lang = filter_var(urldecode($post['lang']), FILTER_SANITIZE_STRING); - $path = filter_var(urldecode($post['path']), FILTER_SANITIZE_STRING); - $text = filter_var(urldecode($post['text']), FILTER_SANITIZE_STRING); - $name = filter_var(urldecode($post['name']), FILTER_SANITIZE_STRING); + $lang = filter_var(urldecode($post['lang']), FILTER_SANITIZE_STRING); + $path = filter_var(urldecode($post['path']), FILTER_SANITIZE_STRING); + $text = filter_var(urldecode($post['text']), FILTER_SANITIZE_STRING); + $name = filter_var(urldecode($post['name']), FILTER_SANITIZE_STRING); $email = filter_var(urldecode($post['email']), FILTER_SANITIZE_STRING); $title = filter_var(urldecode($post['title']), FILTER_SANITIZE_STRING); + $site = filter_var(urldecode($post['site']), FILTER_SANITIZE_STRING); if (isset($this->grav['user'])) { $user = $this->grav['user']; @@ -220,7 +244,8 @@ class CommentsPlugin extends Plugin 'text' => $text, 'date' => date('D, d M Y H:i:s', time()), 'author' => $name, - 'email' => $email + 'email' => $email, + 'site' => $site ]; } else { $data = array( @@ -230,7 +255,8 @@ class CommentsPlugin extends Plugin 'text' => $text, 'date' => date('D, d M Y H:i:s', time()), 'author' => $name, - 'email' => $email + 'email' => $email, + 'site' => $site ]) ); } @@ -239,6 +265,7 @@ class CommentsPlugin extends Plugin //clear cache $this->grav['cache']->delete($this->comments_cache_id); + $this->grav['cache']->delete($this->pingbacks_cache_id); break; } @@ -353,6 +380,26 @@ class CommentsPlugin extends Plugin return $comments; } + /** + * Return the pingbacks associated to the current route + */ + private function fetchPingbacks() { + $cache = $this->grav['cache']; + //search in cache + if ($pingbacks = $cache->fetch($this->pingbacks_cache_id)) { + return $pingbacks; + } + + $lang = $this->grav['language']->getLanguage(); + $filename = $lang ? '/' . $lang : ''; + $filename .= $this->grav['uri']->path() . '.yaml'; + + $pingbacks = $this->getDataFromFilename($filename)['pingbacks']; + //save to cache if enabled + $cache->save($this->pingbacks_cache_id, $pingbacks); + return $pingbacks; + } + /** * Return the latest commented pages */ diff --git a/comments.yaml b/comments.yaml index 5c09966..3115067 100644 --- a/comments.yaml +++ b/comments.yaml @@ -1,4 +1,6 @@ enabled: true +pingbacks: true +commenting: true enable_on_routes: - '/blog' @@ -6,7 +8,6 @@ enable_on_routes: disable_on_routes: - /blog/blog-post-to-ignore - /ignore-this-route - #- '/blog/daring-fireball-link' form: name: comments @@ -26,6 +27,13 @@ form: validate: required: true + - name: blah + label: PLUGIN_COMMENTS.EMAIL_LABEL + placeholder: "https://leetnightshade.com" + type: text + validate: + required: false + - name: text label: PLUGIN_COMMENTS.MESSAGE_LABEL placeholder: PLUGIN_COMMENTS.MESSAGE_PLACEHOLDER @@ -50,6 +58,9 @@ form: type: hidden evaluateDefault: grav.uri.path + - name: blockme + type: honeypot + # - name: g-recaptcha-response # label: Captcha # type: captcha diff --git a/extern/habariakismet/habariakismet.plugin.php b/extern/habariakismet/habariakismet.plugin.php new file mode 100644 index 0000000..aa6399d --- /dev/null +++ b/extern/habariakismet/habariakismet.plugin.php @@ -0,0 +1,148 @@ +plugin_id()) { + $actions[] = _t('Configure'); + } + + return $actions; + } + + public function action_plugin_ui($plugin_id, $action) + { + if ($plugin_id == $this->plugin_id()) { + switch ($action) { + case _t('Configure'): + + $form = new FormUI(strtolower(get_class($this))); + $form->append('select', 'provider', 'habariakismet__provider', _t('Service')); + $form->provider->options = array( + 'Akismet' => 'Akismet', + 'TypePad AntiSpam' => 'TypePad AntiSpam' + ); + $api_key = $form->append('text', 'api_key', 'habariakismet__api_key', _t('API Key')); + $api_key->add_validator('validate_required'); + $api_key->add_validator(array($this, 'validate_api_key')); + $form->append('submit', 'save', 'Save'); + $form->out(); + break; + } + } + } + + public function validate_api_key($key, $control, $form) + { + $endpoint = ($form->provider->value == 'Akismet') ? self::SERVER_AKISMET : self::SERVER_TYPEPAD; + + $a = new Akismet(Site::get_url('habari'), $key); + $a->setAkismetServer($endpoint); + + if (!$a->isKeyValid()) { + return array(sprintf(_t('Sorry, the %s API key %s is invalid. Please check to make sure the key is entered correctly.'), $form->provider->value, $key)); + } + + return array(); + } + + public function set_priorities() + { + return array( + 'action_comment_insert_before' => 1 + ); + } + + public function action_comment_insert_before(Comment $comment) + { + $api_key = Options::get('habariakismet__api_key'); + $provider = Options::get('habariakismet__provider'); + + if ($api_key == null || $provider == null) { + return; + } + + $endpoint = ($provider == 'Akismet') ? self::SERVER_AKISMET : self::SERVER_TYPEPAD; + + $a = new Akismet(Site::get_url('habari'), $api_key); + $a->setAkismetServer($endpoint); + $a->setCommentAuthor($comment->name); + $a->setCommentAuthorEmail($comment->email); + $a->setCommentAuthorURL($comment->url); + $a->setCommentContent($comment->content); + $a->setPermalink($comment->post->permalink); + + try { + $comment->status = ($a->isCommentSpam()) ? 'spam' : 'ham'; + return; + } catch (Exception $e) { + EventLog::log($e->getMessage(), 'notice', 'comment', 'HabariAkismet'); + } + } + + public function action_admin_moderate_comments($action, $comments, $handler) + { + $false_negatives = 0; + $false_positives = 0; + + $provider = Options::get('habariakismet__provider'); + $endpoint = ($provider == 'Akismet') ? self::SERVER_AKISMET : self::SERVER_TYPEPAD; + + $a = new Akismet(Site::get_url('habari'), Options::get('habariakismet__api_key')); + $a->setAkismetServer($endpoint); + + foreach ($comments as $comment) { + switch ($action) { + case 'spam': + if ($comment->status == Comment::STATUS_APPROVED || $comment->status == Comment::STATUS_UNAPPROVED) { + $a->setCommentAuthor($comment->name); + $a->setCommentAuthorEmail($comment->email); + $a->setCommentAuthorURL($comment->url); + $a->setCommentContent($comment->content); + + $a->submitSpam(); + + $false_negatives++; + } + + break; + case 'approved': + if ($comment->status == Comment::STATUS_SPAM) { + $a->setCommentAuthor($comment->name); + $a->setCommentAuthorEmail($comment->email); + $a->setCommentAuthorURL($comment->url); + $a->setCommentContent($comment->content); + + $a->submitHam(); + + $false_positives++; + } + + break; + } + } + + if ($false_negatives) { + Session::notice(_t('Reported %d false negatives to %s.', array($false_negatives, $provider))); + } + + if ($false_positives) { + Session::notice(_t('Reported %d false positives to %s.', array($false_positives, $provider))); + } + } +} + +?> diff --git a/extern/habariakismet/habariakismet.plugin.xml b/extern/habariakismet/habariakismet.plugin.xml new file mode 100644 index 0000000..b1c59a4 --- /dev/null +++ b/extern/habariakismet/habariakismet.plugin.xml @@ -0,0 +1,10 @@ + + + Habari Akismet + Apache Software License 2.0 + http://habariproject.org + The Habari Community + 0.2 + 98bdb03a-a7e5-4c2a-a875-dcfe1d89f130 + + \ No newline at end of file diff --git a/extern/habariakismet/vendor/Akismet.class.php b/extern/habariakismet/vendor/Akismet.class.php new file mode 100644 index 0000000..3a63922 --- /dev/null +++ b/extern/habariakismet/vendor/Akismet.class.php @@ -0,0 +1,392 @@ +Usage: + * + * $akismet = new Akismet('http://www.example.com/blog/', 'aoeu1aoue'); + * $akismet->setCommentAuthor($name); + * $akismet->setCommentAuthorEmail($email); + * $akismet->setCommentAuthorURL($url); + * $akismet->setCommentContent($comment); + * $akismet->setPermalink('http://www.example.com/blog/alex/someurl/'); + * if($akismet->isCommentSpam()) + * // store the comment but mark it as spam (in case of a mis-diagnosis) + * else + * // store the comment normally + * + * + * Optionally you may wish to check if your WordPress API key is valid as in the example below. + * + * + * $akismet = new Akismet('http://www.example.com/blog/', 'aoeu1aoue'); + * + * if($akismet->isKeyValid()) { + * // api key is okay + * } else { + * // api key is invalid + * } + * + * + * @package akismet + * @name Akismet + * @version 0.4 + * @author Alex Potsides + * @link http://www.achingbrain.net/ + */ +class Akismet + { + private $version = '0.4'; + private $wordPressAPIKey; + private $blogURL; + private $comment; + private $apiPort; + private $akismetServer; + private $akismetVersion; + + // This prevents some potentially sensitive information from being sent accross the wire. + private $ignore = array('HTTP_COOKIE', + 'HTTP_X_FORWARDED_FOR', + 'HTTP_X_FORWARDED_HOST', + 'HTTP_MAX_FORWARDS', + 'HTTP_X_FORWARDED_SERVER', + 'REDIRECT_STATUS', + 'SERVER_PORT', + 'PATH', + 'DOCUMENT_ROOT', + 'SERVER_ADMIN', + 'QUERY_STRING', + 'PHP_SELF' ); + + /** + * @param string $blogURL The URL of your blog. + * @param string $wordPressAPIKey WordPress API key. + */ + public function __construct($blogURL, $wordPressAPIKey) { + $this->blogURL = $blogURL; + $this->wordPressAPIKey = $wordPressAPIKey; + + // Set some default values + $this->apiPort = 80; + $this->akismetServer = 'rest.akismet.com'; + $this->akismetVersion = '1.1'; + + // Start to populate the comment data + $this->comment['blog'] = $blogURL; + $this->comment['user_agent'] = $_SERVER['HTTP_USER_AGENT']; + + if(isset($_SERVER['HTTP_REFERER'])) { + $this->comment['referrer'] = $_SERVER['HTTP_REFERER']; + } + + /* + * This is necessary if the server PHP5 is running on has been set up to run PHP4 and + * PHP5 concurently and is actually running through a separate proxy al a these instructions: + * http://www.schlitt.info/applications/blog/archives/83_How_to_run_PHP4_and_PHP_5_parallel.html + * and http://wiki.coggeshall.org/37.html + * Otherwise the user_ip appears as the IP address of the PHP4 server passing the requests to the + * PHP5 one... + */ + $this->comment['user_ip'] = $_SERVER['REMOTE_ADDR'] != getenv('SERVER_ADDR') ? $_SERVER['REMOTE_ADDR'] : getenv('HTTP_X_FORWARDED_FOR'); + } + + /** + * Makes a request to the Akismet service to see if the API key passed to the constructor is valid. + * + * Use this method if you suspect your API key is invalid. + * + * @return bool True is if the key is valid, false if not. + */ + public function isKeyValid() { + // Check to see if the key is valid + $response = $this->sendRequest('key=' . $this->wordPressAPIKey . '&blog=' . $this->blogURL, $this->akismetServer, '/' . $this->akismetVersion . '/verify-key'); + return $response[1] == 'valid'; + } + + // makes a request to the Akismet service + private function sendRequest($request, $host, $path) { + $http_request = "POST " . $path . " HTTP/1.0\r\n"; + $http_request .= "Host: " . $host . "\r\n"; + $http_request .= "Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n"; + $http_request .= "Content-Length: " . strlen($request) . "\r\n"; + $http_request .= "User-Agent: Akismet PHP5 Class " . $this->version . " | Akismet/1.11\r\n"; + $http_request .= "\r\n"; + $http_request .= $request; + + $socketWriteRead = new SocketWriteRead($host, $this->apiPort, $http_request); + $socketWriteRead->send(); + + return explode("\r\n\r\n", $socketWriteRead->getResponse(), 2); + } + + // Formats the data for transmission + private function getQueryString() { + foreach($_SERVER as $key => $value) { + if(!in_array($key, $this->ignore)) { + if($key == 'REMOTE_ADDR') { + $this->comment[$key] = $this->comment['user_ip']; + } else { + $this->comment[$key] = $value; + } + } + } + + $query_string = ''; + + foreach($this->comment as $key => $data) { + if(!is_array($data)) { + $query_string .= $key . '=' . urlencode(stripslashes($data)) . '&'; + } + } + + return $query_string; + } + + /** + * Tests for spam. + * + * Uses the web service provided by {@link http://www.akismet.com Akismet} to see whether or not the submitted comment is spam. Returns a boolean value. + * + * @return bool True if the comment is spam, false if not + * @throws Will throw an exception if the API key passed to the constructor is invalid. + */ + public function isCommentSpam() { + $response = $this->sendRequest($this->getQueryString(), $this->wordPressAPIKey . '.' . $this->akismetServer, '/' . $this->akismetVersion . '/comment-check'); + + if($response[1] == 'invalid' && !$this->isKeyValid()) { + throw new exception('The Wordpress API key passed to the Akismet constructor is invalid. Please obtain a valid one from http://wordpress.com/api-keys/'); + } + + return ($response[1] == 'true'); + } + + /** + * Submit spam that is incorrectly tagged as ham. + * + * Using this function will make you a good citizen as it helps Akismet to learn from its mistakes. This will improve the service for everybody. + */ + public function submitSpam() { + $this->sendRequest($this->getQueryString(), $this->wordPressAPIKey . '.' . $this->akismetServer, '/' . $this->akismetVersion . '/submit-spam'); + } + + /** + * Submit ham that is incorrectly tagged as spam. + * + * Using this function will make you a good citizen as it helps Akismet to learn from its mistakes. This will improve the service for everybody. + */ + public function submitHam() { + $this->sendRequest($this->getQueryString(), $this->wordPressAPIKey . '.' . $this->akismetServer, '/' . $this->akismetVersion . '/submit-ham'); + } + + /** + * To override the user IP address when submitting spam/ham later on + * + * @param string $userip An IP address. Optional. + */ + public function setUserIP($userip) { + $this->comment['user_ip'] = $userip; + } + + /** + * To override the referring page when submitting spam/ham later on + * + * @param string $referrer The referring page. Optional. + */ + public function setReferrer($referrer) { + $this->comment['referrer'] = $referrer; + } + + /** + * A permanent URL referencing the blog post the comment was submitted to. + * + * @param string $permalink The URL. Optional. + */ + public function setPermalink($permalink) { + $this->comment['permalink'] = $permalink; + } + + /** + * The type of comment being submitted. + * + * May be blank, comment, trackback, pingback, or a made up value like "registration" or "wiki". + */ + public function setCommentType($commentType) { + $this->comment['comment_type'] = $commentType; + } + + /** + * The name that the author submitted with the comment. + */ + public function setCommentAuthor($commentAuthor) { + $this->comment['comment_author'] = $commentAuthor; + } + + /** + * The email address that the author submitted with the comment. + * + * The address is assumed to be valid. + */ + public function setCommentAuthorEmail($authorEmail) { + $this->comment['comment_author_email'] = $authorEmail; + } + + /** + * The URL that the author submitted with the comment. + */ + public function setCommentAuthorURL($authorURL) { + $this->comment['comment_author_url'] = $authorURL; + } + + /** + * The comment's body text. + */ + public function setCommentContent($commentBody) { + $this->comment['comment_content'] = $commentBody; + } + + /** + * Defaults to 80 + */ + public function setAPIPort($apiPort) { + $this->apiPort = $apiPort; + } + + /** + * Defaults to rest.akismet.com + */ + public function setAkismetServer($akismetServer) { + $this->akismetServer = $akismetServer; + } + + /** + * Defaults to '1.1' + */ + public function setAkismetVersion($akismetVersion) { + $this->akismetVersion = $akismetVersion; + } +} + +/** + * Utility class used by Akismet + * + * This class is used by Akismet to do the actual sending and receiving of data. It opens a connection to a remote host, sends some data and the reads the response and makes it available to the calling program. + * + * The code that makes up this class originates in the Akismet WordPress plugin, which is {@link http://akismet.com/download/ available on the Akismet website}. + * + * N.B. It is not necessary to call this class directly to use the Akismet class. This is included here mainly out of a sense of completeness. + * + * @package akismet + * @name SocketWriteRead + * @version 0.1 + * @author Alex Potsides + * @link http://www.achingbrain.net/ + */ +class SocketWriteRead { + private $host; + private $port; + private $request; + private $response; + private $responseLength; + private $errorNumber; + private $errorString; + + /** + * @param string $host The host to send/receive data. + * @param int $port The port on the remote host. + * @param string $request The data to send. + * @param int $responseLength The amount of data to read. Defaults to 1160 bytes. + */ + public function __construct($host, $port, $request, $responseLength = 1160) { + $this->host = $host; + $this->port = $port; + $this->request = $request; + $this->responseLength = $responseLength; + $this->errorNumber = 0; + $this->errorString = ''; + } + + /** + * Sends the data to the remote host. + * + * @throws An exception is thrown if a connection cannot be made to the remote host. + */ + public function send() { + $this->response = ''; + + $fs = fsockopen($this->host, $this->port, $this->errorNumber, $this->errorString, 3); + + if($this->errorNumber != 0) { + throw new Exception('Error connecting to host: ' . $this->host . ' Error number: ' . $this->errorNumber . ' Error message: ' . $this->errorString); + } + + if($fs !== false) { + @fwrite($fs, $this->request); + + while(!feof($fs)) { + $this->response .= fgets($fs, $this->responseLength); + } + + fclose($fs); + } + } + + /** + * Returns the server response text + * + * @return string + */ + public function getResponse() { + return $this->response; + } + + /** + * Returns the error number + * + * If there was no error, 0 will be returned. + * + * @return int + */ + public function getErrorNumner() { + return $this->errorNumber; + } + + /** + * Returns the error string + * + * If there was no error, an empty string will be returned. + * + * @return string + */ + public function getErrorString() { + return $this->errorString; + } +} + +?> \ No newline at end of file diff --git a/languages.yaml b/languages.yaml index 812f22d..173ba3c 100644 --- a/languages.yaml +++ b/languages.yaml @@ -24,6 +24,8 @@ en: PLUGIN_COMMENTS: ADD_COMMENT: Add a comment COMMENTS: Comments + COMMENTS_NONE: There are no comments yet. + COMMENTS_PINGBACKS: Pingbacks EMAIL_NOT_CONFIGURED: Email not configured NEW_COMMENT_EMAIL_SUBJECT: 'New comment on %1$s' NEW_COMMENT_EMAIL_BODY: '

      A new comment was made on %1$s by %3$s (%4$s).

      Page: %2$s

      Text: %5$s

      ' diff --git a/templates/partials/comments.html.twig b/templates/partials/comments.html.twig index 29d79b9..b4153a0 100644 --- a/templates/partials/comments.html.twig +++ b/templates/partials/comments.html.twig @@ -1,60 +1,114 @@ {% if grav.twig.enable_comments_plugin %} {% set scope = scope ?: 'data.' %} -

      {{'PLUGIN_COMMENTS.ADD_COMMENT'|t}}

      + {% if grav.twig.pingbacks_enabled %} + {% if grav.twig.pingbacks|length %} + {% set comments_visible = false %} + {% for pingback in grav.twig.pingbacks %} + {% if pingback.approved == "true" %} + {% set comments_visible = true %} + {% endif %} + {% endfor %} -
      + {% if comments_visible %} +

      {{'PLUGIN_COMMENTS.COMMENTS_PINGBACKS'|t}}

      - {% for field in grav.config.plugins.comments.form.fields %} - {% set value = form.value(field.name) %} - {% if field.evaluateDefault %} - {% set value = evaluate(field.evaluateDefault) %} - {% endif %} - {% if config.plugins.login.enabled and grav.user.authenticated %} - {% if field.name == 'name' %} - - {% elseif field.name == 'email' %} - + + {% for pingback in grav.twig.pingbacks %} + {% if pingback.approved == "true" %} + + + + {% endif %} + {% endfor %} +
      + {{pingback.text}} +
      + {{'PLUGIN_COMMENTS.WRITTEN_ON'|t}} {{pingback.date|e}} {{'PLUGIN_COMMENTS.BY'|t}} + {% if pingback.site %} + {{pingback.author}} + {% else %} + {{pingback.author}} + {% endif %} +
      + {% endif %} + {% endif %} + + {% if grav.twig.commenting_enabled %} +

      {{'PLUGIN_COMMENTS.ADD_COMMENT'|t}}

      + + + + {% for field in grav.config.plugins.comments.form.fields %} + {% set value = form.value(field.name) %} + {% if field.evaluateDefault %} + {% set value = evaluate(field.evaluateDefault) %} + {% endif %} + {% if config.plugins.login.enabled and grav.user.authenticated %} + {% if field.name == 'name' %} + + {% elseif field.name == 'email' %} + + {% else %} +
      + {% include "forms/fields/#{field.type}/#{field.type}.html.twig" %} +
      + {% endif %} {% else %}
      {% include "forms/fields/#{field.type}/#{field.type}.html.twig" %}
      {% endif %} - {% else %} -
      - {% include "forms/fields/#{field.type}/#{field.type}.html.twig" %} -
      - {% endif %} - {% endfor %} - {% include "forms/fields/formname/formname.html.twig" %} + {% endfor %} + {% include "forms/fields/formname/formname.html.twig" %} -
      - {% for button in grav.config.plugins.comments.form.buttons %} - - {% endfor %} -
      +
      + {% for button in grav.config.plugins.comments.form.buttons %} + + {% endfor %} +
      - {{ nonce_field('form', 'form-nonce')|raw }} -
      + {{ nonce_field('form', 'form-nonce')|raw }} + -
      {{ form.message }}
      +
      {{ form.message }}
      + {% endif %} {% if grav.twig.comments|length %}

      {{'PLUGIN_COMMENTS.COMMENTS'|t}}

      - {% for comment in grav.twig.comments|array_reverse %} - - - + {% set comments_visible = false %} + {% for comment in grav.twig.comments %} + {% if comment.approved == "true" %} + {% set comments_visible = true %} + + + + {% endif %} {% endfor %} + + {% if not comments_visible %} + + + + {% endif %}
      - {{comment.text}} -
      - {{'PLUGIN_COMMENTS.WRITTEN_ON'|t}} {{comment.date|e}} {{'PLUGIN_COMMENTS.BY'|t}} {{comment.author}} -
      + {{comment.text}} +
      + {{'PLUGIN_COMMENTS.WRITTEN_ON'|t}} {{comment.date|e}} {{'PLUGIN_COMMENTS.BY'|t}} + {% if comment.site %} + {{comment.author}} + {% else %} + {{comment.author}} + {% endif %} +
      + {{'PLUGIN_COMMENTS.COMMENTS_NONE'|t}} +
      {% endif %} {% endif %} From 607e0fa6d40bf668fe0b0e619069040f5b0390e7 Mon Sep 17 00:00:00 2001 From: Ernst Date: Fri, 1 Dec 2017 17:54:29 -0800 Subject: [PATCH 30/38] Format was messed up. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7c6b54d..848726c 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ This plugin adds support for displaying pingbacks, toggling the display of pingbacks. I'm not yet sure how pingbacks work, so this may not include support for adding new pingbacks; if it won't take too much work I'll add more support for them. The main goal of this fork is to add support for Akismet. Recaptcha guards from bots, but Akismet helps deal with actual spam content. + --- The **Comments Plugin** for [Grav](http://github.com/getgrav/grav) adds the ability to add comments to pages, and moderate them. From 19d5ef9f1f2a46f148a20d6571d8e86dd7001ced Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 4 Dec 2017 18:17:55 -0800 Subject: [PATCH 31/38] Added screenshot, updated readme. Other forks don't update readme or show what they did, so I thought I'd take the first [rough] step. --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 848726c..c441f58 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,13 @@ # Grav Comments Plugin \[Fork\] -This plugin adds support for displaying pingbacks, toggling the display of pingbacks. I'm not yet sure how pingbacks work, so this may not include support for adding new pingbacks; if it won't take too much work I'll add more support for them. +This plugin adds support for displaying pingbacks, toggling the display of pingbacks. I'm not yet sure how pingbacks work, so this may not include support for adding new pingbacks; if it won't take too much work I'll add more support for them. Also added support for user entered URLs. The main goal of this fork is to add support for Akismet. Recaptcha guards from bots, but Akismet helps deal with actual spam content. +Preview with commenting disabled but comment plugin still enabled: + +![](https://raw.githubusercontent.com/leetNightshade/leetNightshade.github.io/master/grav-plugin-comments/Screenshots/blog.default-theme.png) + --- The **Comments Plugin** for [Grav](http://github.com/getgrav/grav) adds the ability to add comments to pages, and moderate them. From 6a6b1973a1fb4748bebf2ae34487bb6eedd9417b Mon Sep 17 00:00:00 2001 From: Ernst Date: Tue, 5 Dec 2017 17:03:12 -0800 Subject: [PATCH 32/38] Adding basic Akismet stuff. Seems to work. Still working on it, make sure it's okay. --- admin/templates/comments.html.twig | 7 + blueprints.yaml | 68 ++++++-- comments.php | 54 ++++++- comments.yaml | 4 +- .../vendor => akismet}/Akismet.class.php | 0 extern/habariakismet/habariakismet.plugin.php | 148 ------------------ extern/habariakismet/habariakismet.plugin.xml | 10 -- languages.yaml | 100 ++++++++++++ 8 files changed, 216 insertions(+), 175 deletions(-) rename extern/{habariakismet/vendor => akismet}/Akismet.class.php (100%) delete mode 100644 extern/habariakismet/habariakismet.plugin.php delete mode 100644 extern/habariakismet/habariakismet.plugin.xml diff --git a/admin/templates/comments.html.twig b/admin/templates/comments.html.twig index 6fea510..b78920f 100644 --- a/admin/templates/comments.html.twig +++ b/admin/templates/comments.html.twig @@ -75,6 +75,13 @@ }); + {% if grav.twig.warning_message|length %} +
      +

      {{ "PLUGIN_COMMENTS.WARNINGS"|tu }}

      +

      {{ grav.twig.warning_message }}

      +
      + {% endif %} +

      Comments in the last 7 days

      diff --git a/blueprints.yaml b/blueprints.yaml index 9caf823..4a188aa 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -29,19 +29,9 @@ form: 0: PLUGIN_ADMIN.DISABLED validate: type: bool - pingbacks: - type: toggle - label: Pingbacks - highlight: 1 - default: 0 - options: - 1: PLUGIN_ADMIN.ENABLED - 0: PLUGIN_ADMIN.DISABLED - validate: - type: bool commenting: type: toggle - label: Commenting + label: PLUGIN_COMMENTS.COMMENTS highlight: 1 default: 0 options: @@ -49,3 +39,59 @@ form: 0: PLUGIN_ADMIN.DISABLED validate: type: bool + nested: + type: toggle + label: PLUGIN_COMMENTS.COMMENTS_NESTED + highlight: 1 + default: 0 + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED + validate: + type: bool + pingbacks: + type: toggle + label: PLUGIN_COMMENTS.COMMENTS_PINGBACKS + highlight: 1 + default: 0 + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED + validate: + type: bool + recaptcha: + type: toggle + label: PLUGIN_COMMENTS.RECAPTCHA + highlight: 1 + default: 0 + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED + validate: + type: bool + akismet: + type: toggle + label: PLUGIN_COMMENTS.AKISMET + highlight: 1 + default: 0 + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED + validate: + type: bool + akismet_apikey: + type: text + label: PLUGIN_COMMENTS.AKISMET_APIKEY + highlight: 1 + default: + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED + akismet_site: + type: text + label: PLUGIN_COMMENTS.AKISMET_SITE_OVERRIDE + highlight: 1 + default: + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED diff --git a/comments.php b/comments.php index b1b0b7f..2070221 100644 --- a/comments.php +++ b/comments.php @@ -17,6 +17,8 @@ use RocketTheme\Toolbox\File\File; use RocketTheme\Toolbox\Event\Event; use Symfony\Component\Yaml\Yaml; +require_once 'extern/akismet/Akismet.class.php'; + class CommentsPlugin extends Plugin { protected $route = 'comments'; @@ -27,6 +29,8 @@ class CommentsPlugin extends Plugin protected $comments_cache_id; protected $pingbacks_cache_id; + protected $akismet_enabled; + /** * @return array */ @@ -85,6 +89,7 @@ class CommentsPlugin extends Plugin $this->grav['twig']->pingbacks_enabled = $this->pingbacks_enabled; $this->grav['twig']->comments = $this->fetchComments(); $this->grav['twig']->pingbacks = $this->fetchPingbacks(); + $this->grav['twig']->akismet_enabled = $this->grav['config']->get('plugins.comments.akismet'); } /** @@ -117,6 +122,8 @@ class CommentsPlugin extends Plugin $this->commenting_enabled = $this->grav['config']->get('plugins.comments.commenting'); $this->pingbacks_enabled = $this->grav['config']->get('plugins.comments.pingbacks'); + + $this->akismet_enabled = $this->grav['config']->get('plugins.comments.akismet'); } /** @@ -124,8 +131,6 @@ class CommentsPlugin extends Plugin */ public function initializeFrontend() { - $this->calculateEnable(); - $this->enable([ 'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0], ]); @@ -182,6 +187,8 @@ class CommentsPlugin extends Plugin */ public function onPluginsInitialized() { + $this->calculateEnable(); + if ($this->isAdmin()) { $this->initializeAdmin(); } else { @@ -218,7 +225,7 @@ class CommentsPlugin extends Plugin $name = filter_var(urldecode($post['name']), FILTER_SANITIZE_STRING); $email = filter_var(urldecode($post['email']), FILTER_SANITIZE_STRING); $title = filter_var(urldecode($post['title']), FILTER_SANITIZE_STRING); - $site = filter_var(urldecode($post['site']), FILTER_SANITIZE_STRING); + $site = isset($post['site']) ? filter_var(urldecode($post['site']), FILTER_SANITIZE_STRING) : ""; if (isset($this->grav['user'])) { $user = $this->grav['user']; @@ -232,6 +239,25 @@ class CommentsPlugin extends Plugin $language = $this->grav['language']; $lang = $language->getLanguage(); + if ($this->akismet_enabled) { + $key = $this->grav['config']->get('plugins.comments.akismet_apikey'); + $url_override = $this->grav['config']->get('plugins.comments.akismet_site'); + $url = !empty($url_override) ? $url_override : $_SERVER['HTTP_HOST']; + + $akismet = new \Akismet($url, $key); + $akismet->setCommentAuthor($name); + $akismet->setCommentAuthorEmail($email); + $akismet->setCommentAuthorURL($site); + $akismet->setCommentContent($text); + //$akismet->setPermalink($comment->post->permalink); + //try { + $spam = ($akismet->isCommentSpam()) ? 'spam' : 'ham'; + //return; + //} catch (Exception $e) { + // EventLog::log($e->getMessage(), 'notice', 'comment', 'HabariAkismet'); + //} + } + $filename = DATA_DIR . 'comments'; $filename .= ($lang ? '/' . $lang : ''); $filename .= $path . '.yaml'; @@ -245,7 +271,8 @@ class CommentsPlugin extends Plugin 'date' => date('D, d M Y H:i:s', time()), 'author' => $name, 'email' => $email, - 'site' => $site + 'site' => $site, + 'approved' => ( !isset($spam) || $spam == 'ham' ? 'true' : 'false' ) ]; } else { $data = array( @@ -256,7 +283,8 @@ class CommentsPlugin extends Plugin 'date' => date('D, d M Y H:i:s', time()), 'author' => $name, 'email' => $email, - 'site' => $site + 'site' => $site, + 'approved' => ( !isset($spam) || $spam == 'ham' ? 'true' : 'false' ) ]) ); } @@ -451,6 +479,20 @@ class CommentsPlugin extends Plugin public function onTwigAdminTemplatePaths() { $this->grav['twig']->twig_paths[] = __DIR__ . '/admin/templates'; + $this->grav['twig']->akismet_enabled = $this->akismet_enabled; + + if ($this->akismet_enabled) { + $key = $this->grav['config']->get('plugins.comments.akismet_apikey'); + $url_override = $this->grav['config']->get('plugins.comments.akismet_site'); + $url = !empty($url_override) ? $url_override : $_SERVER['HTTP_HOST']; + + $akismet = new \Akismet($url, $key); + if ($akismet->isKeyValid()) { + $this->grav['twig']->warning_message = ""; + } else { + $this->grav['twig']->warning_message = sprintf("Akismet API key \"%s\" is invalid for the url \"%s\". Provide a correct url override or make sure you're registered. Please check to make sure the key is entered correctly.", $key, $url); + } + } } /** @@ -459,6 +501,8 @@ class CommentsPlugin extends Plugin public function onAdminMenu() { $this->grav['twig']->plugins_hooked_nav['PLUGIN_COMMENTS.COMMENTS'] = ['route' => $this->route, 'icon' => 'fa-file-text']; + + } /** diff --git a/comments.yaml b/comments.yaml index 3115067..7c414f2 100644 --- a/comments.yaml +++ b/comments.yaml @@ -1,6 +1,8 @@ enabled: true pingbacks: true commenting: true +akismet_enabled: true +warning_message: "" enable_on_routes: - '/blog' @@ -28,7 +30,7 @@ form: required: true - name: blah - label: PLUGIN_COMMENTS.EMAIL_LABEL + label: PLUGIN_COMMENTS.SITE_LABEL placeholder: "https://leetnightshade.com" type: text validate: diff --git a/extern/habariakismet/vendor/Akismet.class.php b/extern/akismet/Akismet.class.php similarity index 100% rename from extern/habariakismet/vendor/Akismet.class.php rename to extern/akismet/Akismet.class.php diff --git a/extern/habariakismet/habariakismet.plugin.php b/extern/habariakismet/habariakismet.plugin.php deleted file mode 100644 index aa6399d..0000000 --- a/extern/habariakismet/habariakismet.plugin.php +++ /dev/null @@ -1,148 +0,0 @@ -plugin_id()) { - $actions[] = _t('Configure'); - } - - return $actions; - } - - public function action_plugin_ui($plugin_id, $action) - { - if ($plugin_id == $this->plugin_id()) { - switch ($action) { - case _t('Configure'): - - $form = new FormUI(strtolower(get_class($this))); - $form->append('select', 'provider', 'habariakismet__provider', _t('Service')); - $form->provider->options = array( - 'Akismet' => 'Akismet', - 'TypePad AntiSpam' => 'TypePad AntiSpam' - ); - $api_key = $form->append('text', 'api_key', 'habariakismet__api_key', _t('API Key')); - $api_key->add_validator('validate_required'); - $api_key->add_validator(array($this, 'validate_api_key')); - $form->append('submit', 'save', 'Save'); - $form->out(); - break; - } - } - } - - public function validate_api_key($key, $control, $form) - { - $endpoint = ($form->provider->value == 'Akismet') ? self::SERVER_AKISMET : self::SERVER_TYPEPAD; - - $a = new Akismet(Site::get_url('habari'), $key); - $a->setAkismetServer($endpoint); - - if (!$a->isKeyValid()) { - return array(sprintf(_t('Sorry, the %s API key %s is invalid. Please check to make sure the key is entered correctly.'), $form->provider->value, $key)); - } - - return array(); - } - - public function set_priorities() - { - return array( - 'action_comment_insert_before' => 1 - ); - } - - public function action_comment_insert_before(Comment $comment) - { - $api_key = Options::get('habariakismet__api_key'); - $provider = Options::get('habariakismet__provider'); - - if ($api_key == null || $provider == null) { - return; - } - - $endpoint = ($provider == 'Akismet') ? self::SERVER_AKISMET : self::SERVER_TYPEPAD; - - $a = new Akismet(Site::get_url('habari'), $api_key); - $a->setAkismetServer($endpoint); - $a->setCommentAuthor($comment->name); - $a->setCommentAuthorEmail($comment->email); - $a->setCommentAuthorURL($comment->url); - $a->setCommentContent($comment->content); - $a->setPermalink($comment->post->permalink); - - try { - $comment->status = ($a->isCommentSpam()) ? 'spam' : 'ham'; - return; - } catch (Exception $e) { - EventLog::log($e->getMessage(), 'notice', 'comment', 'HabariAkismet'); - } - } - - public function action_admin_moderate_comments($action, $comments, $handler) - { - $false_negatives = 0; - $false_positives = 0; - - $provider = Options::get('habariakismet__provider'); - $endpoint = ($provider == 'Akismet') ? self::SERVER_AKISMET : self::SERVER_TYPEPAD; - - $a = new Akismet(Site::get_url('habari'), Options::get('habariakismet__api_key')); - $a->setAkismetServer($endpoint); - - foreach ($comments as $comment) { - switch ($action) { - case 'spam': - if ($comment->status == Comment::STATUS_APPROVED || $comment->status == Comment::STATUS_UNAPPROVED) { - $a->setCommentAuthor($comment->name); - $a->setCommentAuthorEmail($comment->email); - $a->setCommentAuthorURL($comment->url); - $a->setCommentContent($comment->content); - - $a->submitSpam(); - - $false_negatives++; - } - - break; - case 'approved': - if ($comment->status == Comment::STATUS_SPAM) { - $a->setCommentAuthor($comment->name); - $a->setCommentAuthorEmail($comment->email); - $a->setCommentAuthorURL($comment->url); - $a->setCommentContent($comment->content); - - $a->submitHam(); - - $false_positives++; - } - - break; - } - } - - if ($false_negatives) { - Session::notice(_t('Reported %d false negatives to %s.', array($false_negatives, $provider))); - } - - if ($false_positives) { - Session::notice(_t('Reported %d false positives to %s.', array($false_positives, $provider))); - } - } -} - -?> diff --git a/extern/habariakismet/habariakismet.plugin.xml b/extern/habariakismet/habariakismet.plugin.xml deleted file mode 100644 index b1c59a4..0000000 --- a/extern/habariakismet/habariakismet.plugin.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - Habari Akismet - Apache Software License 2.0 - http://habariproject.org - The Habari Community - 0.2 - 98bdb03a-a7e5-4c2a-a875-dcfe1d89f130 - - \ No newline at end of file diff --git a/languages.yaml b/languages.yaml index 173ba3c..ebb2ecc 100644 --- a/languages.yaml +++ b/languages.yaml @@ -1,7 +1,13 @@ de: PLUGIN_COMMENTS: ADD_COMMENT: Kommentar hinzufügen + AKISMET: Akismet + AKISMET_APIKEY: Akismet API Key + AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Kommentare + COMMENTS_NESTED: Nested Comments + COMMENTS_NONE: There are no comments yet. + COMMENTS_PINGBACKS: Pingbacks EMAIL_NOT_CONFIGURED: Email nicht konfiguriert NEW_COMMENT_EMAIL_SUBJECT: 'Neuer Kommentar für %1$s' NEW_COMMENT_EMAIL_BODY: '

      Ein neuer Kommentar am %1$s von %3$s (%4$s).

      Seite: %2$s

      Text: %5$s

      ' @@ -16,14 +22,21 @@ de: EMAIL_PLACEHOLDER: "Email-Adresse eingeben" MESSAGE_LABEL: "Kommentar" MESSAGE_PLACEHOLDER: "Kommentar eingeben" + RECAPTCHA: reCAPTCHA + SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Absenden" EMAIL_NEW_COMMENT_SUBJECT: "[Neuer Kommentar] von {{ form.value.name|e }}" THANK_YOU_MESSAGE: "Vielen Dank für den Kommentar!" + WARNINGS: Warnings en: PLUGIN_COMMENTS: ADD_COMMENT: Add a comment + AKISMET: Akismet + AKISMET_APIKEY: Akismet API Key + AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Comments + COMMENTS_NESTED: Nested Comments COMMENTS_NONE: There are no comments yet. COMMENTS_PINGBACKS: Pingbacks EMAIL_NOT_CONFIGURED: Email not configured @@ -40,14 +53,23 @@ en: EMAIL_PLACEHOLDER: "Enter your email address" MESSAGE_LABEL: "Comment" MESSAGE_PLACEHOLDER: "Enter your comment" + RECAPTCHA: reCAPTCHA + SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Submit" EMAIL_NEW_COMMENT_SUBJECT: "[New Comment] from {{ form.value.name|e }}" THANK_YOU_MESSAGE: "Thank you for writing your comment!" + WARNINGS: Warnings es: PLUGIN_COMMENTS: ADD_COMMENT: Agregar un comentario + AKISMET: Akismet + AKISMET_APIKEY: Akismet API Key + AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Comentarios + COMMENTS_NESTED: Nested Comments + COMMENTS_NONE: There are no comments yet. + COMMENTS_PINGBACKS: Pingbacks EMAIL_NOT_CONFIGURED: El Email no está configurado NEW_COMMENT_EMAIL_SUBJECT: 'Nuevo comentario en %1$s' NEW_COMMENT_EMAIL_BODY: '

      Un nuevo comentario se hizo en %1$s por %3$s (%4$s).

      Page: %2$s

      Text: %5$s

      ' @@ -62,14 +84,23 @@ es: EMAIL_PLACEHOLDER: "Escriba su email" MESSAGE_LABEL: "Comentario" MESSAGE_PLACEHOLDER: "Escriba su comentario" + RECAPTCHA: reCAPTCHA + SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Enviar" EMAIL_NEW_COMMENT_SUBJECT: "[Nuevo comentario] de {{ form.value.name|e }}" THANK_YOU_MESSAGE: "Gracias por escribir su comentario!" + WARNINGS: Warnings fr: PLUGIN_COMMENTS: ADD_COMMENT: Ajouter un commentaire + AKISMET: Akismet + AKISMET_APIKEY: Akismet API Key + AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Commentaires + COMMENTS_NESTED: Nested Comments + COMMENTS_NONE: There are no comments yet. + COMMENTS_PINGBACKS: Pingbacks EMAIL_NOT_CONFIGURED: E-mail non configuré NEW_COMMENT_EMAIL_SUBJECT: 'Nouveau commentaire sur %1$s' NEW_COMMENT_EMAIL_BODY: '

      Un nouveau commentaire a été publié sur %1$s par %3$s (%4$s).

      Page : %2$s

      Texte : %5$s

      ' @@ -84,14 +115,23 @@ fr: EMAIL_PLACEHOLDER: "Indiquez votre adresse e-mail" MESSAGE_LABEL: "Commentaire" MESSAGE_PLACEHOLDER: "Rédigez votre commentaire" + RECAPTCHA: reCAPTCHA + SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Envoyer" EMAIL_NEW_COMMENT_SUBJECT: "[Nouveau commentaire] de {{ form.value.name|e }}" THANK_YOU_MESSAGE: "Merci d'avoir rédigé votre commentaire !" + WARNINGS: Warnings hr: PLUGIN_COMMENTS: ADD_COMMENT: Dodaj komentar + AKISMET: Akismet + AKISMET_APIKEY: Akismet API Key + AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Komentari + COMMENTS_NESTED: Nested Comments + COMMENTS_NONE: There are no comments yet. + COMMENTS_PINGBACKS: Pingbacks EMAIL_NOT_CONFIGURED: Email adresa nije podešena NEW_COMMENT_EMAIL_SUBJECT: 'Novi komentar na %1$s' NEW_COMMENT_EMAIL_BODY: '

      Novi komentar je napisan na %1$s od %3$s (%4$s).

      Stranica:: %2$s

      Tekst: %5$s

      ' @@ -106,14 +146,23 @@ hr: EMAIL_PLACEHOLDER: "Unesite email adresu" MESSAGE_LABEL: "Komentar" MESSAGE_PLACEHOLDER: "Unesite komentar" + RECAPTCHA: reCAPTCHA + SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Pošalji" EMAIL_NEW_COMMENT_SUBJECT: "[Novi komentar] od {{ form.value.name|e }}" THANK_YOU_MESSAGE: "Hvala Vam što ste napisali svoj komentar!" + WARNINGS: Warnings it: PLUGIN_COMMENTS: ADD_COMMENT: Aggiungi un commento + AKISMET: Akismet + AKISMET_APIKEY: Akismet API Key + AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Commenti + COMMENTS_NESTED: Nested Comments + COMMENTS_NONE: There are no comments yet. + COMMENTS_PINGBACKS: Pingbacks EMAIL_NOT_CONFIGURED: Email non configurata NEW_COMMENT_EMAIL_SUBJECT: 'Nuovo commento su %1$s' NEW_COMMENT_EMAIL_BODY: '

      Un nuovo commento è stato postato su %1$s da %3$s (%4$s).

      Pagina: %2$s

      Testo: %5$s

      ' @@ -128,14 +177,23 @@ it: EMAIL_PLACEHOLDER: "Inserisci il tuo indirizzo email" MESSAGE_LABEL: "Messaggio" MESSAGE_PLACEHOLDER: "Inserisci il tuo commento" + RECAPTCHA: reCAPTCHA + SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Invia" EMAIL_NEW_COMMENT_SUBJECT: "[Nuovo commento] da {{ form.value.name|e }}" THANK_YOU_MESSAGE: "Grazie per il tuo commento!" + WARNINGS: Warnings ja: PLUGIN_COMMENTS: ADD_COMMENT: コメントを追加する + AKISMET: Akismet + AKISMET_APIKEY: Akismet API Key + AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: コメント + COMMENTS_NESTED: Nested Comments + COMMENTS_NONE: There are no comments yet. + COMMENTS_PINGBACKS: Pingbacks EMAIL_NOT_CONFIGURED: メールアドレスは設定さていません NEW_COMMENT_EMAIL_SUBJECT: '%1$sについて新しいコメント' NEW_COMMENT_EMAIL_BODY: '

      新しいコメントが%1$sについて%3$sから(%4$s)書かれた.

      ページー : %2$s

      文書 : %5$s

      ' @@ -150,14 +208,23 @@ ja: EMAIL_PLACEHOLDER: "ご自分のメールアドレスをここに..." MESSAGE_LABEL: "コメント" MESSAGE_PLACEHOLDER: "コメントをここに" + RECAPTCHA: reCAPTCHA + SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "送信する" EMAIL_NEW_COMMENT_SUBJECT: "[新しいコメント]、 {{ form.value.name|e }}から" THANK_YOU_MESSAGE: "コメントを書いてくださいましてありがとうございました!" + WARNINGS: Warnings pl: PLUGIN_COMMENTS: ADD_COMMENT: Dodaj komentarz + AKISMET: Akismet + AKISMET_APIKEY: Akismet API Key + AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Komentarzy + COMMENTS_NESTED: Nested Comments + COMMENTS_NONE: There are no comments yet. + COMMENTS_PINGBACKS: Pingbacks EMAIL_NOT_CONFIGURED: Email jest nie skofigurowany NEW_COMMENT_EMAIL_SUBJECT: 'Nowy komentarz %1$s' NEW_COMMENT_EMAIL_BODY: '

      Pojawił się nowy komentarz, napisany %1$s przez %3$s (%4$s).

      Strona: %2$s

      Treść: %5$s

      ' @@ -170,7 +237,13 @@ pl: ru: PLUGIN_COMMENTS: ADD_COMMENT: Добавить комментарий + AKISMET: Akismet + AKISMET_APIKEY: Akismet API Key + AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Комментарии + COMMENTS_NESTED: Nested Comments + COMMENTS_NONE: There are no comments yet. + COMMENTS_PINGBACKS: Pingbacks EMAIL_NOT_CONFIGURED: Email не настроен NEW_COMMENT_EMAIL_SUBJECT: 'Новый комментарий к %1$s' NEW_COMMENT_EMAIL_BODY: '

      Новый комментарий был сделан на %1$s by %3$s (%4$s).

      Страница: %2$s

      Текст: %5$s

      ' @@ -183,7 +256,13 @@ ru: pt-br: PLUGIN_COMMENTS: ADD_COMMENT: Escreva um comentário + AKISMET: Akismet + AKISMET_APIKEY: Akismet API Key + AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Comentários + COMMENTS_NESTED: Nested Comments + COMMENTS_NONE: There are no comments yet. + COMMENTS_PINGBACKS: Pingbacks EMAIL_NOT_CONFIGURED: E-mail não configurado NEW_COMMENT_EMAIL_SUBJECT: 'Novo comentário em %1$s' NEW_COMMENT_EMAIL_BODY: '

      Um novo comentário foi feito em %1$s por %3$s (%4$s).

      Página: %2$s

      Texto: %5$s

      ' @@ -198,14 +277,23 @@ pt-br: EMAIL_PLACEHOLDER: "Escreva seu e-mail. Ex.: seunome@provedor.com.br" MESSAGE_LABEL: "Comentário" MESSAGE_PLACEHOLDER: "Escreva seu comentário" + RECAPTCHA: reCAPTCHA + SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Enviar" EMAIL_NEW_COMMENT_SUBJECT: "[Novo comentário] de {{ form.value.name|e }}" THANK_YOU_MESSAGE: "Obrigada por enviar seu comentário!" + WARNINGS: Warnings ro: PLUGIN_COMMENTS: ADD_COMMENT: 'Adăugați un comentariu' + AKISMET: Akismet + AKISMET_APIKEY: Akismet API Key + AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: 'Comentarii' + COMMENTS_NESTED: Nested Comments + COMMENTS_NONE: There are no comments yet. + COMMENTS_PINGBACKS: Pingbacks EMAIL_NOT_CONFIGURED: 'Adresa de email nu este configurată' NEW_COMMENT_EMAIL_SUBJECT: 'Comentariu nou pentru %1$s' NEW_COMMENT_EMAIL_BODY: '

      Un nou comentariu a fost adăugat la %1$s de către %3$s (%4$s).

      Pagină: %2$s

      Text: %5$s

      ' @@ -220,14 +308,23 @@ ro: EMAIL_PLACEHOLDER: "Introduceți adresa Dvs. de email" MESSAGE_LABEL: "Comentariu" MESSAGE_PLACEHOLDER: "Scrieți comentariul Dvs." + RECAPTCHA: reCAPTCHA + SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Trimiteți" EMAIL_NEW_COMMENT_SUBJECT: "[Comentariu nou] from {{ form.value.name|e }}" THANK_YOU_MESSAGE: "Vă mulțumim pentru comentariu!" + WARNINGS: Warnings no: PLUGIN_COMMENTS: ADD_COMMENT: Skriv en kommentar + AKISMET: Akismet + AKISMET_APIKEY: Akismet API Key + AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Kommentarer + COMMENTS_NESTED: Nested Comments + COMMENTS_NONE: There are no comments yet. + COMMENTS_PINGBACKS: Pingbacks EMAIL_NOT_CONFIGURED: Epost er ikke konfigurert NEW_COMMENT_EMAIL_SUBJECT: 'Ny kommentar på %1$s' NEW_COMMENT_EMAIL_BODY: '

      En ny kommentar er skrevet på %1$s av %3$s (%4$s).

      Side: %2$s

      Tekst: %5$s

      ' @@ -242,6 +339,9 @@ no: EMAIL_PLACEHOLDER: "Skriv din epost adresse" MESSAGE_LABEL: "Kommentar" MESSAGE_PLACEHOLDER: "Skriv din kommentar" + RECAPTCHA: reCAPTCHA + SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Send" EMAIL_NEW_COMMENT_SUBJECT: "[Ny kommentar] fra {{ form.value.name|e }}" THANK_YOU_MESSAGE: "Takk for din kommentar!" + WARNINGS: Warnings From f6a4204ed2a8ab774329c731f224a522be9c1aa3 Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 5 Dec 2017 17:06:23 -0800 Subject: [PATCH 33/38] Akismet support. Stood up Akismet support. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c441f58..810ec1f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This plugin adds support for displaying pingbacks, toggling the display of pingbacks. I'm not yet sure how pingbacks work, so this may not include support for adding new pingbacks; if it won't take too much work I'll add more support for them. Also added support for user entered URLs. -The main goal of this fork is to add support for Akismet. Recaptcha guards from bots, but Akismet helps deal with actual spam content. +This fork adds support for Akismet, which is now basically working. Consider it alpha. Also has admin panel functionality, can toggle it, add api key, shows error on comment page if api key doesn't match site. Going to add amin panel checkbox for recaptcha. Preview with commenting disabled but comment plugin still enabled: From b56c5927f76c8396a742d1ba183d4fa4f469ec39 Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 5 Dec 2017 17:08:25 -0800 Subject: [PATCH 34/38] Minor readme update. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 810ec1f..241882a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Grav Comments Plugin \[Fork\] -This plugin adds support for displaying pingbacks, toggling the display of pingbacks. I'm not yet sure how pingbacks work, so this may not include support for adding new pingbacks; if it won't take too much work I'll add more support for them. Also added support for user entered URLs. +This plugin adds support for displaying pingbacks, toggling the display of pingbacks. I'm not yet sure how pingbacks work, so this may not include support for adding new pingbacks; if it won't take too much work I'll add more support for them. Also added support for user entered URLs, though I haven't gotten the form input to show up yet. This fork adds support for Akismet, which is now basically working. Consider it alpha. Also has admin panel functionality, can toggle it, add api key, shows error on comment page if api key doesn't match site. Going to add amin panel checkbox for recaptcha. From 3632251cea8b94766b993030cb27819b0b50bd82 Mon Sep 17 00:00:00 2001 From: Ernst Date: Wed, 6 Dec 2017 12:58:44 -0800 Subject: [PATCH 35/38] Cleaning up a little, reorganizing. Was Trying to look into recatpcha support, but decided not to bother, keep things simple with verification server side for now with no added javascript. --- blueprints.yaml | 20 +++----------------- comments.php | 30 ++++++++++++++---------------- languages.yaml | 34 ++++++++++++---------------------- 3 files changed, 29 insertions(+), 55 deletions(-) diff --git a/blueprints.yaml b/blueprints.yaml index 4a188aa..0079e13 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -59,16 +59,7 @@ form: 0: PLUGIN_ADMIN.DISABLED validate: type: bool - recaptcha: - type: toggle - label: PLUGIN_COMMENTS.RECAPTCHA - highlight: 1 - default: 0 - options: - 1: PLUGIN_ADMIN.ENABLED - 0: PLUGIN_ADMIN.DISABLED - validate: - type: bool + akismet: type: toggle label: PLUGIN_COMMENTS.AKISMET @@ -79,19 +70,14 @@ form: 0: PLUGIN_ADMIN.DISABLED validate: type: bool - akismet_apikey: + akismet_key_api: type: text - label: PLUGIN_COMMENTS.AKISMET_APIKEY + label: PLUGIN_COMMENTS.AKISMET_KEY_API highlight: 1 default: options: - 1: PLUGIN_ADMIN.ENABLED - 0: PLUGIN_ADMIN.DISABLED akismet_site: type: text label: PLUGIN_COMMENTS.AKISMET_SITE_OVERRIDE highlight: 1 default: - options: - 1: PLUGIN_ADMIN.ENABLED - 0: PLUGIN_ADMIN.DISABLED diff --git a/comments.php b/comments.php index 2070221..b2acc84 100644 --- a/comments.php +++ b/comments.php @@ -89,7 +89,7 @@ class CommentsPlugin extends Plugin $this->grav['twig']->pingbacks_enabled = $this->pingbacks_enabled; $this->grav['twig']->comments = $this->fetchComments(); $this->grav['twig']->pingbacks = $this->fetchPingbacks(); - $this->grav['twig']->akismet_enabled = $this->grav['config']->get('plugins.comments.akismet'); + $this->grav['twig']->akismet_enabled = $this->akismet_enabled; } /** @@ -116,10 +116,6 @@ class CommentsPlugin extends Plugin } } - #$blueprint = $this->getBlueprint(); - #$this->commenting_enabled = $blueprint->get('form/fields/commenting', false, '/'); - #$this->pingbacks_enabled = $blueprint->get('form/fields/pingbacks' , false, '/'); - $this->commenting_enabled = $this->grav['config']->get('plugins.comments.commenting'); $this->pingbacks_enabled = $this->grav['config']->get('plugins.comments.pingbacks'); @@ -240,7 +236,7 @@ class CommentsPlugin extends Plugin $lang = $language->getLanguage(); if ($this->akismet_enabled) { - $key = $this->grav['config']->get('plugins.comments.akismet_apikey'); + $key = $this->grav['config']->get('plugins.comments.akismet_key_api'); $url_override = $this->grav['config']->get('plugins.comments.akismet_site'); $url = !empty($url_override) ? $url_override : $_SERVER['HTTP_HOST']; @@ -250,12 +246,14 @@ class CommentsPlugin extends Plugin $akismet->setCommentAuthorURL($site); $akismet->setCommentContent($text); //$akismet->setPermalink($comment->post->permalink); - //try { + try { $spam = ($akismet->isCommentSpam()) ? 'spam' : 'ham'; //return; - //} catch (Exception $e) { - // EventLog::log($e->getMessage(), 'notice', 'comment', 'HabariAkismet'); - //} + } catch (Exception $e) { + //EventLog::log($e->getMessage(), 'notice', 'comment', 'HabariAkismet'); + // TODO: admin needs to approve comment + $spam = "pending"; + } } $filename = DATA_DIR . 'comments'; @@ -292,9 +290,11 @@ class CommentsPlugin extends Plugin $file->save(Yaml::dump($data)); //clear cache - $this->grav['cache']->delete($this->comments_cache_id); - $this->grav['cache']->delete($this->pingbacks_cache_id); - + if (!isset($spam) || $spam == 'ham') + { + $this->grav['cache']->delete($this->comments_cache_id); + $this->grav['cache']->delete($this->pingbacks_cache_id); + } break; } } @@ -482,7 +482,7 @@ class CommentsPlugin extends Plugin $this->grav['twig']->akismet_enabled = $this->akismet_enabled; if ($this->akismet_enabled) { - $key = $this->grav['config']->get('plugins.comments.akismet_apikey'); + $key = $this->grav['config']->get('plugins.comments.akismet_key_api'); $url_override = $this->grav['config']->get('plugins.comments.akismet_site'); $url = !empty($url_override) ? $url_override : $_SERVER['HTTP_HOST']; @@ -501,8 +501,6 @@ class CommentsPlugin extends Plugin public function onAdminMenu() { $this->grav['twig']->plugins_hooked_nav['PLUGIN_COMMENTS.COMMENTS'] = ['route' => $this->route, 'icon' => 'fa-file-text']; - - } /** diff --git a/languages.yaml b/languages.yaml index ebb2ecc..5a83143 100644 --- a/languages.yaml +++ b/languages.yaml @@ -2,7 +2,7 @@ de: PLUGIN_COMMENTS: ADD_COMMENT: Kommentar hinzufügen AKISMET: Akismet - AKISMET_APIKEY: Akismet API Key + AKISMET_KEY_API: Akismet API Key AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Kommentare COMMENTS_NESTED: Nested Comments @@ -22,7 +22,6 @@ de: EMAIL_PLACEHOLDER: "Email-Adresse eingeben" MESSAGE_LABEL: "Kommentar" MESSAGE_PLACEHOLDER: "Kommentar eingeben" - RECAPTCHA: reCAPTCHA SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Absenden" EMAIL_NEW_COMMENT_SUBJECT: "[Neuer Kommentar] von {{ form.value.name|e }}" @@ -33,7 +32,7 @@ en: PLUGIN_COMMENTS: ADD_COMMENT: Add a comment AKISMET: Akismet - AKISMET_APIKEY: Akismet API Key + AKISMET_KEY_API: Akismet API Key AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Comments COMMENTS_NESTED: Nested Comments @@ -53,7 +52,6 @@ en: EMAIL_PLACEHOLDER: "Enter your email address" MESSAGE_LABEL: "Comment" MESSAGE_PLACEHOLDER: "Enter your comment" - RECAPTCHA: reCAPTCHA SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Submit" EMAIL_NEW_COMMENT_SUBJECT: "[New Comment] from {{ form.value.name|e }}" @@ -64,7 +62,7 @@ es: PLUGIN_COMMENTS: ADD_COMMENT: Agregar un comentario AKISMET: Akismet - AKISMET_APIKEY: Akismet API Key + AKISMET_KEY_API: Akismet API Key AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Comentarios COMMENTS_NESTED: Nested Comments @@ -84,7 +82,6 @@ es: EMAIL_PLACEHOLDER: "Escriba su email" MESSAGE_LABEL: "Comentario" MESSAGE_PLACEHOLDER: "Escriba su comentario" - RECAPTCHA: reCAPTCHA SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Enviar" EMAIL_NEW_COMMENT_SUBJECT: "[Nuevo comentario] de {{ form.value.name|e }}" @@ -95,7 +92,7 @@ fr: PLUGIN_COMMENTS: ADD_COMMENT: Ajouter un commentaire AKISMET: Akismet - AKISMET_APIKEY: Akismet API Key + AKISMET_KEY_API: Akismet API Key AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Commentaires COMMENTS_NESTED: Nested Comments @@ -115,7 +112,6 @@ fr: EMAIL_PLACEHOLDER: "Indiquez votre adresse e-mail" MESSAGE_LABEL: "Commentaire" MESSAGE_PLACEHOLDER: "Rédigez votre commentaire" - RECAPTCHA: reCAPTCHA SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Envoyer" EMAIL_NEW_COMMENT_SUBJECT: "[Nouveau commentaire] de {{ form.value.name|e }}" @@ -126,7 +122,7 @@ hr: PLUGIN_COMMENTS: ADD_COMMENT: Dodaj komentar AKISMET: Akismet - AKISMET_APIKEY: Akismet API Key + AKISMET_KEY_API: Akismet API Key AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Komentari COMMENTS_NESTED: Nested Comments @@ -146,7 +142,6 @@ hr: EMAIL_PLACEHOLDER: "Unesite email adresu" MESSAGE_LABEL: "Komentar" MESSAGE_PLACEHOLDER: "Unesite komentar" - RECAPTCHA: reCAPTCHA SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Pošalji" EMAIL_NEW_COMMENT_SUBJECT: "[Novi komentar] od {{ form.value.name|e }}" @@ -157,7 +152,7 @@ it: PLUGIN_COMMENTS: ADD_COMMENT: Aggiungi un commento AKISMET: Akismet - AKISMET_APIKEY: Akismet API Key + AKISMET_KEY_API: Akismet API Key AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Commenti COMMENTS_NESTED: Nested Comments @@ -177,7 +172,6 @@ it: EMAIL_PLACEHOLDER: "Inserisci il tuo indirizzo email" MESSAGE_LABEL: "Messaggio" MESSAGE_PLACEHOLDER: "Inserisci il tuo commento" - RECAPTCHA: reCAPTCHA SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Invia" EMAIL_NEW_COMMENT_SUBJECT: "[Nuovo commento] da {{ form.value.name|e }}" @@ -188,7 +182,7 @@ ja: PLUGIN_COMMENTS: ADD_COMMENT: コメントを追加する AKISMET: Akismet - AKISMET_APIKEY: Akismet API Key + AKISMET_KEY_API: Akismet API Key AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: コメント COMMENTS_NESTED: Nested Comments @@ -208,7 +202,6 @@ ja: EMAIL_PLACEHOLDER: "ご自分のメールアドレスをここに..." MESSAGE_LABEL: "コメント" MESSAGE_PLACEHOLDER: "コメントをここに" - RECAPTCHA: reCAPTCHA SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "送信する" EMAIL_NEW_COMMENT_SUBJECT: "[新しいコメント]、 {{ form.value.name|e }}から" @@ -219,7 +212,7 @@ pl: PLUGIN_COMMENTS: ADD_COMMENT: Dodaj komentarz AKISMET: Akismet - AKISMET_APIKEY: Akismet API Key + AKISMET_KEY_API: Akismet API Key AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Komentarzy COMMENTS_NESTED: Nested Comments @@ -238,7 +231,7 @@ ru: PLUGIN_COMMENTS: ADD_COMMENT: Добавить комментарий AKISMET: Akismet - AKISMET_APIKEY: Akismet API Key + AKISMET_KEY_API: Akismet API Key AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Комментарии COMMENTS_NESTED: Nested Comments @@ -257,7 +250,7 @@ pt-br: PLUGIN_COMMENTS: ADD_COMMENT: Escreva um comentário AKISMET: Akismet - AKISMET_APIKEY: Akismet API Key + AKISMET_KEY_API: Akismet API Key AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Comentários COMMENTS_NESTED: Nested Comments @@ -277,7 +270,6 @@ pt-br: EMAIL_PLACEHOLDER: "Escreva seu e-mail. Ex.: seunome@provedor.com.br" MESSAGE_LABEL: "Comentário" MESSAGE_PLACEHOLDER: "Escreva seu comentário" - RECAPTCHA: reCAPTCHA SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Enviar" EMAIL_NEW_COMMENT_SUBJECT: "[Novo comentário] de {{ form.value.name|e }}" @@ -288,7 +280,7 @@ ro: PLUGIN_COMMENTS: ADD_COMMENT: 'Adăugați un comentariu' AKISMET: Akismet - AKISMET_APIKEY: Akismet API Key + AKISMET_KEY_API: Akismet API Key AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: 'Comentarii' COMMENTS_NESTED: Nested Comments @@ -308,7 +300,6 @@ ro: EMAIL_PLACEHOLDER: "Introduceți adresa Dvs. de email" MESSAGE_LABEL: "Comentariu" MESSAGE_PLACEHOLDER: "Scrieți comentariul Dvs." - RECAPTCHA: reCAPTCHA SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Trimiteți" EMAIL_NEW_COMMENT_SUBJECT: "[Comentariu nou] from {{ form.value.name|e }}" @@ -319,7 +310,7 @@ no: PLUGIN_COMMENTS: ADD_COMMENT: Skriv en kommentar AKISMET: Akismet - AKISMET_APIKEY: Akismet API Key + AKISMET_KEY_API: Akismet API Key AKISMET_SITE_OVERRIDE: Akismet Site Override COMMENTS: Kommentarer COMMENTS_NESTED: Nested Comments @@ -339,7 +330,6 @@ no: EMAIL_PLACEHOLDER: "Skriv din epost adresse" MESSAGE_LABEL: "Kommentar" MESSAGE_PLACEHOLDER: "Skriv din kommentar" - RECAPTCHA: reCAPTCHA SITE_LABEL: Site SUBMIT_COMMENT_BUTTON_TEXT: "Send" EMAIL_NEW_COMMENT_SUBJECT: "[Ny kommentar] fra {{ form.value.name|e }}" From 94134e2575f0674b61078b070674fe6fb612b4fc Mon Sep 17 00:00:00 2001 From: leetNightshade <> Date: Fri, 29 Jun 2018 17:06:21 -0700 Subject: [PATCH 36/38] Adding gravatar support and nice time display, and classes and div's for customizing comment formatting. --- languages.yaml | 46 +++--- templates/partials/comments.html.twig | 227 +++++++++++++++----------- 2 files changed, 151 insertions(+), 122 deletions(-) diff --git a/languages.yaml b/languages.yaml index 5a83143..5c0cc87 100644 --- a/languages.yaml +++ b/languages.yaml @@ -14,8 +14,8 @@ de: EMAIL_FOOTER: '' NAME: Name: EMAIL: Email: - WRITTEN_ON: geschrieben am - BY: von + WRITTEN_ON: am + BY: Von NAME_LABEL: "Name" NAME_PLACEHOLDER: "Namen eingeben" EMAIL_LABEL: "Email" @@ -44,8 +44,8 @@ en: EMAIL_FOOTER: '' NAME: Name: EMAIL: Email: - WRITTEN_ON: Written on - BY: by + WRITTEN_ON: on + BY: By NAME_LABEL: "Name" NAME_PLACEHOLDER: "Enter your name" EMAIL_LABEL: "Email" @@ -74,8 +74,8 @@ es: EMAIL_FOOTER: '' NAME: Nombre: EMAIL: Email: - WRITTEN_ON: Escrito en - BY: por + WRITTEN_ON: en + BY: Por NAME_LABEL: "Nombre" NAME_PLACEHOLDER: "Escriba su nombre" EMAIL_LABEL: "Email" @@ -104,8 +104,8 @@ fr: EMAIL_FOOTER: '' NAME: Nom : EMAIL: E-mail : - WRITTEN_ON: Écrit le - BY: par + WRITTEN_ON: le + BY: Par NAME_LABEL: "Nom" NAME_PLACEHOLDER: "Indiquez votre nom" EMAIL_LABEL: "E-mail" @@ -134,8 +134,8 @@ hr: EMAIL_FOOTER: '' NAME: Ime: EMAIL: Email: - WRITTEN_ON: Napisano je na - BY: od + WRITTEN_ON: na + BY: Od NAME_LABEL: "Ime" NAME_PLACEHOLDER: "Unesite ime" EMAIL_LABEL: "Email adresa" @@ -164,8 +164,8 @@ it: EMAIL_FOOTER: '' NAME: Nome: EMAIL: Email: - WRITTEN_ON: Scritto il - BY: da + WRITTEN_ON: il + BY: Da NAME_LABEL: "Nome" NAME_PLACEHOLDER: "Inserisci il tuo nome" EMAIL_LABEL: "Email" @@ -194,7 +194,7 @@ ja: EMAIL_FOOTER: '' NAME: 名前 : EMAIL: メールアドレス : - WRITTEN_ON: に書かれた + WRITTEN_ON: に BY: に NAME_LABEL: "名前" NAME_PLACEHOLDER: "お名前を" @@ -224,8 +224,8 @@ pl: EMAIL_FOOTER: '' NAME: Imię: EMAIL: Email: - WRITTEN_ON: Napisany przez - BY: przez + WRITTEN_ON: na + BY: Przez ru: PLUGIN_COMMENTS: @@ -243,8 +243,8 @@ ru: EMAIL_FOOTER: '' NAME: Имя: EMAIL: Email: - WRITTEN_ON: Написан в - BY: от + WRITTEN_ON: на + BY: От pt-br: PLUGIN_COMMENTS: @@ -262,8 +262,8 @@ pt-br: EMAIL_FOOTER: '' NAME: Name: EMAIL: Email: - WRITTEN_ON: Publicado em - BY: por + WRITTEN_ON: em + BY: Por NAME_LABEL: "Nome" NAME_PLACEHOLDER: "Escreva seu nome" EMAIL_LABEL: "E-mail" @@ -292,8 +292,8 @@ ro: EMAIL_FOOTER: '' NAME: 'Nume:' EMAIL: 'Adresă de email:' - WRITTEN_ON: 'Scris în data de' - BY: 'de către' + WRITTEN_ON: 'pe' + BY: 'De' NAME_LABEL: "Numele" NAME_PLACEHOLDER: "Introduceți numele Dvs." EMAIL_LABEL: "Email" @@ -322,8 +322,8 @@ no: EMAIL_FOOTER: '' NAME: Navn: EMAIL: Epost: - WRITTEN_ON: Skrevet på - BY: av + WRITTEN_ON: på + BY: Av NAME_LABEL: "Navn" NAME_PLACEHOLDER: "Skriv ditt navn" EMAIL_LABEL: "Epost" diff --git a/templates/partials/comments.html.twig b/templates/partials/comments.html.twig index b4153a0..8d58c29 100644 --- a/templates/partials/comments.html.twig +++ b/templates/partials/comments.html.twig @@ -1,114 +1,143 @@ {% if grav.twig.enable_comments_plugin %} - {% set scope = scope ?: 'data.' %} + {% set scope = scope ?: 'data.' %} - {% if grav.twig.pingbacks_enabled %} - {% if grav.twig.pingbacks|length %} - {% set comments_visible = false %} - {% for pingback in grav.twig.pingbacks %} - {% if pingback.approved == "true" %} - {% set comments_visible = true %} - {% endif %} - {% endfor %} + {% if grav.twig.pingbacks_enabled %} + {% if grav.twig.pingbacks|length %} + {% set comments_visible = false %} + {% for pingback in grav.twig.pingbacks %} + {% if pingback.approved == "true" %} + {% set comments_visible = true %} + {% endif %} + {% endfor %} - {% if comments_visible %} -

      {{'PLUGIN_COMMENTS.COMMENTS_PINGBACKS'|t}}

      + {% if comments_visible %} +

      {{'PLUGIN_COMMENTS.COMMENTS_PINGBACKS'|t}}

      - - {% for pingback in grav.twig.pingbacks %} - {% if pingback.approved == "true" %} - - - - {% endif %} - {% endfor %} -
      - {{pingback.text}} -
      - {{'PLUGIN_COMMENTS.WRITTEN_ON'|t}} {{pingback.date|e}} {{'PLUGIN_COMMENTS.BY'|t}} - {% if pingback.site %} - {{pingback.author}} - {% else %} - {{pingback.author}} - {% endif %} -
      - {% endif %} - {% endif %} + + {% for pingback in grav.twig.pingbacks %} + {% if pingback.approved == "true" %} + + + + {% endif %} + {% endfor %} +
      + +
      + {% endif %} + {% endif %} - {% if grav.twig.commenting_enabled %} -

      {{'PLUGIN_COMMENTS.ADD_COMMENT'|t}}

      + {% if grav.twig.commenting_enabled %} +

      {{'PLUGIN_COMMENTS.ADD_COMMENT'|t}}

      -
      + - {% for field in grav.config.plugins.comments.form.fields %} - {% set value = form.value(field.name) %} - {% if field.evaluateDefault %} - {% set value = evaluate(field.evaluateDefault) %} - {% endif %} - {% if config.plugins.login.enabled and grav.user.authenticated %} - {% if field.name == 'name' %} - - {% elseif field.name == 'email' %} - - {% else %} -
      - {% include "forms/fields/#{field.type}/#{field.type}.html.twig" %} -
      - {% endif %} - {% else %} -
      - {% include "forms/fields/#{field.type}/#{field.type}.html.twig" %} -
      - {% endif %} - {% endfor %} - {% include "forms/fields/formname/formname.html.twig" %} + {% for field in grav.config.plugins.comments.form.fields %} + {% set value = form.value(field.name) %} + {% if field.evaluateDefault %} + {% set value = evaluate(field.evaluateDefault) %} + {% endif %} + {% if config.plugins.login.enabled and grav.user.authenticated %} + {% if field.name == 'name' %} + + {% elseif field.name == 'email' %} + + {% else %} +
      + {% include "forms/fields/#{field.type}/#{field.type}.html.twig" %} +
      + {% endif %} + {% else %} +
      + {% include "forms/fields/#{field.type}/#{field.type}.html.twig" %} +
      + {% endif %} + {% endfor %} + {% include "forms/fields/formname/formname.html.twig" %} -
      - {% for button in grav.config.plugins.comments.form.buttons %} - - {% endfor %} -
      +
      + {% for button in grav.config.plugins.comments.form.buttons %} + + {% endfor %} +
      - {{ nonce_field('form', 'form-nonce')|raw }} -
      + {{ nonce_field('form', 'form-nonce')|raw }} + -
      {{ form.message }}
      - {% endif %} +
      {{ form.message }}
      + {% endif %} - {% if grav.twig.comments|length %} + {% if grav.twig.comments|length %} -

      {{'PLUGIN_COMMENTS.COMMENTS'|t}}

      +

      {{'PLUGIN_COMMENTS.COMMENTS'|t}}

      - - {% set comments_visible = false %} - {% for comment in grav.twig.comments %} - {% if comment.approved == "true" %} - {% set comments_visible = true %} - - - - {% endif %} - {% endfor %} +
      - {{comment.text}} -
      - {{'PLUGIN_COMMENTS.WRITTEN_ON'|t}} {{comment.date|e}} {{'PLUGIN_COMMENTS.BY'|t}} - {% if comment.site %} - {{comment.author}} - {% else %} - {{comment.author}} - {% endif %} -
      + {% set comments_visible = false %} + {% for comment in grav.twig.comments %} + {% if comment.approved == "true" %} + {% set comments_visible = true %} + + + + {% endif %} + {% endfor %} - {% if not comments_visible %} - - - - {% endif %} -
      + +
      - {{'PLUGIN_COMMENTS.COMMENTS_NONE'|t}} -
      - {% endif %} + {% if not comments_visible %} + + + {{'PLUGIN_COMMENTS.COMMENTS_NONE'|t}} + + + {% endif %} + + {% endif %} {% endif %} From 4698549b88f668bab32bf2b625581a3e516ef753 Mon Sep 17 00:00:00 2001 From: leetNightshade <> Date: Thu, 3 Jan 2019 19:49:02 -0800 Subject: [PATCH 37/38] Changed it so you can only nest on comments with an ID, which means old comments can't be replied to. OH, I reverted the change from using CompiledYamlFile because I don't like how the RocketTheme Yaml dump is setup, it breaks comments up onto multiple lines. Thus, I also removed the reply button on those comments. You can still attempt to reply to a comment if you're hacking around the frontend, but as long as the comment doesn't have an ID it shouldn't work. TODO: Make the backend comment panel work again. --- comments.php | 105 ++++++++++++-------------- templates/partials/comments.html.twig | 2 +- 2 files changed, 48 insertions(+), 59 deletions(-) diff --git a/comments.php b/comments.php index e5ebf86..b0ef78e 100644 --- a/comments.php +++ b/comments.php @@ -3,7 +3,7 @@ namespace Grav\Plugin; use Grav\Common\Blueprint; use Grav\Common\Blueprints; use Grav\Common\BlueprintSchema; -use Grav\Common\File\CompiledYamlFile; +//use Grav\Common\File\CompiledYamlFile; use Grav\Common\Filesystem\Folder; use Grav\Common\Filesystem\RecursiveFolderFilterIterator; use Grav\Common\GPM\GPM; @@ -12,6 +12,7 @@ use Grav\Common\Page\Page; use Grav\Common\Page\Pages; use Grav\Common\Plugin; use Grav\Common\Utils; +use RocketTheme\Toolbox\File\File; use RocketTheme\Toolbox\Event\Event; use Symfony\Component\Yaml\Yaml; use Twig_SimpleFunction; @@ -284,9 +285,11 @@ class CommentsPlugin extends Plugin { /** store comments with page **/ /******************************/ $localfilename = $path . '/comments.yaml'; - $localfile = CompiledYamlFile::instance($localfilename); + //$localfile = CompiledYamlFile::instance($localfilename); + $localfile = File::instance($localfilename); if (file_exists($localfilename)) { - $data = $localfile->content(); + //$data = $localfile->content(); + $data = Yaml::parse($localfile->content()); if (isset($data['comments']) && is_array($data['comments'])) { foreach ($data['comments'] as $key => $comment) { if (!empty($comment['parent']) && $comment['parent'] == $id) { @@ -300,7 +303,8 @@ class CommentsPlugin extends Plugin { //reason: could be possible that "deleted" already exists (e.g. false or '') in $comment which would overwrite the first (newly added) occurence $data['comments'][$key]['deleted'] = $date; //no need to look further as ids are supposed to be unique. - $localfile->save($data); + //$localfile->save($data); + $localfile->save(Yaml::dump($data)); $entry_removed = false; $reply_id = empty($comment['id']) ? '' : $comment['id']; $message = "Found active reply ($reply_id) for selected comment ($id)."; @@ -316,7 +320,8 @@ class CommentsPlugin extends Plugin { //reason: could be possible that "deleted" already exists (e.g. false or '') in $comment which would overwrite the first (newly added) occurence $data['comments'][$key]['deleted'] = $date; //no need to look further as ids are supposed to be unique. - $localfile->save($data); + //$localfile->save($data); + $localfile->save(Yaml::dump($data)); $entry_removed = true; $message = "Deleted comment ($id) via path ($path)"; break; @@ -326,31 +331,6 @@ class CommentsPlugin extends Plugin { } else { //nothing - } - /**********************************/ - /** store comments in index file **/ - /**********************************/ - $indexfilename = DATA_DIR . 'comments/index.yaml'; - $indexfile = CompiledYamlFile::instance($indexfilename); - if (file_exists($indexfilename)) { - $dataIndex = $indexfile->content(); - if (isset($dataIndex['comments']) && is_array($dataIndex['comments'])) { - foreach ($dataIndex['comments'] as $key => $comment) { - if (!empty($comment['page']) && !empty($comment['id']) && $comment['page'] == $route && $comment['id'] == $id) { - //add deleted as first item in array (better readability in file) - $dataIndex['comments'][$key] = array_merge(array('deleted' => ''), $comment); - //set date after merge - //reason: could be possible that "deleted" already exists (e.g. false or '') in $comment which would overwrite the first (newly added) occurence - $dataIndex['comments'][$key]['deleted'] = $date; - //no need to look further as ids are supposed to be unique. - $indexfile->save($dataIndex); - break; - } - } - } - } else { - //nothing - } //clear cache $this->grav['cache']->delete($this->comments_cache_id); @@ -392,29 +372,25 @@ class CommentsPlugin extends Plugin { /** store comments with page **/ /******************************/ $localfilename = $path . '/comments.yaml'; - $localfile = CompiledYamlFile::instance($localfilename); + //$localfile = CompiledYamlFile::instance($localfilename); + $localfile = File::instance($localfilename); if (file_exists($localfilename)) { - $data = $localfile->content(); - $data['autoincrement']++; + //$data = $localfile->content(); + $data = Yaml::parse($localfile->content()); + if (isset($data['autoincrement'])) { + $data['autoincrement']++; + } else { + $data['autoincrement'] = max( sizeof($data['comments']), 1 ); + } + } else { $data = array('autoincrement' => 1, 'comments' => array()); } $localid = $data['autoincrement']; - $newComment = ['id' => $data['autoincrement'], 'ip' => $ip, 'parent' => $parent, 'lang' => $lang, 'text' => $text, 'date' => $date, 'author' => $name, 'email' => $email, 'site' => $site, 'user' => $user, 'approved' => $approved, 'isAdmin' => !empty($isAdmin), ]; + $newComment = ['id' => $data['autoincrement'], 'ip' => $ip, 'parent' => $parent, 'lang' => $lang, 'text' => $text, 'date' => $date, 'author' => $name, 'email' => $email, 'site' => $site, 'user' => $user, 'approved' => $approved, 'isAdmin' => !empty($isAdmin) ]; $data['comments'][] = $newComment; - $localfile->save($data); - /**********************************/ - /** store comments in index file **/ - /**********************************/ - $indexfilename = DATA_DIR . 'comments/index.yaml'; - $indexfile = CompiledYamlFile::instance($indexfilename); - if (file_exists($indexfilename)) { - $data = $indexfile->content(); - } else { - $data = array('comments' => array()); - } - $data['comments'][] = ['page' => $route, 'id' => $localid, 'parent' => $parent, 'lang' => $lang, 'text' => $text, 'date' => $date, 'author' => $name, 'email' => $email, 'site' => $site, 'approved' => $approved, ]; - $indexfile->save($data); + //$localfile->save($data); + $localfile->save(Yaml::dump($data)); //clear cache, don't let incoming spam thrash the cache. if ($approved == 'true') { $this->grav['cache']->delete($this->comments_cache_id); @@ -479,8 +455,10 @@ class CommentsPlugin extends Plugin { if (!empty($paths[str_replace('/', '\\', $fileFolder) ])) $route = $paths[str_replace('/', '\\', $fileFolder) ]; if (!empty($paths[str_replace('\\', '/', $fileFolder) ])) $route = $paths[str_replace('\\', '/', $fileFolder) ]; $page_stats[$filepath] = array('active_entries' => 0, 'deleted_entries' => 0, 'active_comments' => 0, 'deleted_comments' => 0, 'active_replies' => 0, 'deleted_replies' => 0, 'latest_active_entry' => 0, 'route' => $route,); - $localfile = CompiledYamlFile::instance($filepath); - $localcomments = $localfile->content(); + //$localfile = CompiledYamlFile::instance($filepath); + $localfile = File::instance($filepath); + //$localcomments = $localfile->content(); + $localcomments = Yaml::parse($localfile->content()); if (!empty($localcomments['comments']) && is_array($localcomments['comments'])) { foreach ($localcomments['comments'] as $comment) { if (!empty($comment['deleted'])) { @@ -538,7 +516,8 @@ class CommentsPlugin extends Plugin { private function getFilesOrderedByModifiedDate($path = '') { $files = []; if (!$path) { - $path = DATA_DIR . 'comments'; + //$path = DATA_DIR . 'comments'; + $path = $this->grav['page']->path() . '/comments.yaml'; } if (!file_exists($path)) { Folder::mkdir($path); @@ -605,9 +584,10 @@ class CommentsPlugin extends Plugin { return $comments; } $lang = $this->grav['language']->getLanguage(); - $filename = $lang ? '/' . $lang : ''; - $filename.= $this->grav['uri']->path() . '.yaml'; - $comments = $this->getDataFromFilename($filename) ['comments']; + //$filename = $lang ? '/' . $lang : ''; + //$filename.= $this->grav['uri']->path() . '.yaml'; + $filename = $this->grav['page']->path() . '/comments.yaml'; + $comments = $this->getDataFromFilename($filename)['comments']; $comments = $this->setCommentLevels($comments); //save to cache if enabled $cache->save($this->comments_cache_id, $comments); @@ -621,15 +601,22 @@ class CommentsPlugin extends Plugin { return $comments; } $levelsflat = array(); + $commentId = 0; foreach ($comments as $key => $comment) { + $commentId += 1; if (!empty($comment['deleted'])) { //if field "deleted" exists and is filled with a true value then ignore the comment completely. //TODO: This only works on this position as long as it is forbidden to delete comments that have active replies (children). // Otherwise implement that children get the deleted flag recursively or are ignored via Comment class. } else { - $levelsflat[$comment['id']]['parent'] = $comment['parent']; - $levelsflat[$comment['id']]['class'] = new Comment($comment['id'], $comments[$key]); + if (isset($comment['id'])) { + $levelsflat[$comment['id']]['parent'] = $comment['parent']; + $levelsflat[$comment['id']]['class'] = new Comment($comment['id'], $comments[$key]); + } else { + $levelsflat[$commentId]['parent'] = $comment['parent']; + $levelsflat[$commentId]['class'] = new Comment($commentId, $comments[$key]); + } } } //get starting points (entries without valid parent = root element) @@ -667,7 +654,7 @@ class CommentsPlugin extends Plugin { $lang = $this->grav['language']->getLanguage(); $filename = $lang ? '/' . $lang : ''; $filename.= $this->grav['uri']->path() . '.yaml'; - $pingbacks = $this->getDataFromFilenameOld($filename) ['pingbacks']; + $pingbacks = $this->getDataFromFilenameOld($filename)['pingbacks']; //save to cache if enabled $cache->save($this->pingbacks_cache_id, $pingbacks); return $pingbacks; @@ -691,12 +678,14 @@ class CommentsPlugin extends Plugin { //Single item details //$fileInstance = CompiledYamlFile::instance(DATA_DIR . 'comments/' . $fileRoute); //Use comment file in page folder - $fileInstance = CompiledYamlFile::instance($this->grav['page']->path() . '/comments.yaml'); + //$fileInstance = CompiledYamlFile::instance($this->grav['page']->path() . '/comments.yaml'); + $fileInstance = File::instance($this->grav['page']->path() . '/comments.yaml'); if (!$fileInstance->content()) { //Item not found return; } - return $fileInstance->content(); + //return $fileInstance->content(); + return Yaml::parse($fileInstance->content()); } private function getDataFromFilenameOld($fileRoute) { diff --git a/templates/partials/comments.html.twig b/templates/partials/comments.html.twig index c686d73..599cc05 100644 --- a/templates/partials/comments.html.twig +++ b/templates/partials/comments.html.twig @@ -48,7 +48,7 @@ {{nested}}