From 9179453c85acb88d223ba71db51e4ccdef1a17ec Mon Sep 17 00:00:00 2001 From: codeshell Date: Sun, 29 Oct 2017 01:49:09 +0200 Subject: [PATCH] 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; } /**