From 51c1096c7b9f4240895f3a857488f48300341366 Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Fri, 17 Jan 2020 11:30:12 -0500 Subject: [PATCH] Several upgrades to ProcessCommentsManager. Lots of improvements here, but biggest functional additions are the ability to change the Page that any comment lives on, as well as the ability to change what comment another is replying to. --- .../ProcessCommentsManager.css | 2 +- .../ProcessCommentsManager.js | 14 + .../ProcessCommentsManager.module | 557 ++++++++++++------ .../ProcessCommentsManager.scss | 29 +- 4 files changed, 430 insertions(+), 172 deletions(-) diff --git a/wire/modules/Process/ProcessCommentsManager/ProcessCommentsManager.css b/wire/modules/Process/ProcessCommentsManager/ProcessCommentsManager.css index 38d4040f..0444b43f 100644 --- a/wire/modules/Process/ProcessCommentsManager/ProcessCommentsManager.css +++ b/wire/modules/Process/ProcessCommentsManager/ProcessCommentsManager.css @@ -1 +1 @@ -#CommentListHeader{padding-top:.5em;padding-bottom:.5em}#CommentListHeader>p{line-height:2.25em;padding-right:1em;margin-top:.25em;margin-bottom:.25em}#CommentListHeader .MarkupPagerNav{margin-top:0;margin-bottom:.5em}#CommentListHeader .CommentLimitSelect{float:left}#CommentListHeader .CommentCheckAll{display:block;float:left;padding-left:.75em}#CommentListHeader .CommentActions{float:left}#CommentListHeader .CommentSorts{float:left}.CommentItems{clear:both;border-bottom:1px solid #777}.CommentItems+button.ui-button{margin-top:1em}.CommentItem{clear:both;border-top:1px solid #777;padding:.75em 0;margin:0;width:100%}.CommentItem.CommentChecked{background-color:#eee}.CommentItem.CommentChecked th{color:#999}.CommentItem table{width:45%;float:left}.CommentItem table th,.CommentItem table td{padding:4px 10px 4px 0;border-bottom:1px solid #ddd;vertical-align:top}.CommentItem table th{font-weight:bold;white-space:nowrap;text-align:left;padding-left:.75em !important}.CommentItem table th:first-child{width:80px}.CommentItem table tr:last-child td,.CommentItem table tr:last-child th{border-bottom:none}.CommentItem .CommentTitle th,.CommentItem .CommentTitle td{padding-top:0}.CommentItem .CommentChangedIcon{display:none}.CommentItem.CommentItemChanged .CommentChangedIcon{display:inline;float:right;opacity:.3}.CommentItem input[type=number]{width:4em}.CommentItem .CommentVotes label{margin-right:1em}.CommentItem .CommentVotes .CommentUpvotes span{font-weight:bold;color:green}.CommentItem .CommentVotes .CommentDownvotes span{font-weight:bold;color:red}.CommentItem .CommentStars>span{display:inline;cursor:pointer;padding-right:3px}.CommentItem .CommentStars>span i.fa{font-size:18px !important}.CommentItem .CommentContent{float:left;width:55%;padding-left:1em;border:none}.CommentItem .CommentContent .CommentChildrenInfo{margin-top:.5em}.CommentItem .CommentContent .CommentReplyInfo{margin-bottom:0}.CommentItem .CommentContent .CommentText{cursor:pointer;padding-right:1em}.CommentItem .CommentContent .CommentText textarea{font-size:1em;width:100%;min-height:15em;margin-top:1em;margin-bottom:1em}.CommentItem .CommentContent .CommentText .CommentTextEdit{white-space:nowrap}.CommentItem .CommentContent .CommentTextOverflow{overflow-y:scroll;max-height:15em}.CommentItem label.CommentStatus{white-space:nowrap}.CommentItem.CommentItemStatus999{display:none}.CommentCheckAll label .detail,.CommentItem .CommentTitle th label .detail{font-weight:bold;color:#444}.pw-content .MarkupPagerNav{padding-top:.5em;float:right}.pw-content .MarkupPagerNav+button{margin-top:1em}.AdminThemeReno .pw-content .MarkupPagerNav li a,.AdminThemeReno .pw-content .MarkupPagerNav li:first-child a,.AdminThemeReno .pw-content .MarkupPagerNav li.MarkupPagerNavOn a{border-color:#fff !important;border:none !important;border-left:1px solid #fff !important}.AdminThemeReno .pw-content .MarkupPagerNav li{margin-bottom:1px}.AdminThemeReno .pw-content #CommentListHeader{border-top:1px solid #eee}.AdminThemeReno .pw-content #CommentListHeader .MarkupPagerNav{margin-top:.5em}@media only screen and (max-width: 960px){.pw-content .MarkupPagerNav{float:none}#CommentListHeader{padding-bottom:1em;padding-top:1em}#CommentListHeader .MarkupPagerNav{float:none}#CommentListHeader p{margin-top:0;margin-bottom:0;padding-right:1em;padding-left:0 !important}#CommentListHeader .CommentCheckAll{width:auto}}@media only screen and (max-width: 768px){.CommentItem table{width:100%;float:none}.CommentItem table th{padding-left:0 !important}.CommentItem .CommentContent{width:100%;float:none;padding-left:0}.CommentItem .CommentContent .CommentText{padding-right:0}.CommentItem .CommentContent .CommentTextOverflow{overflow-y:auto;max-height:inherit}}.WireTabs{opacity:0} +#CommentListHeader{padding-top:.5em;padding-bottom:.5em}#CommentListHeader>p{line-height:2.25em;padding-right:1em;margin-top:.25em;margin-bottom:.25em}#CommentListHeader .MarkupPagerNav{margin-top:0;margin-bottom:.5em}#CommentListHeader .CommentLimitSelect{float:left}#CommentListHeader .CommentCheckAll{display:block;float:left;padding-left:.75em}#CommentListHeader .CommentActions{float:left}#CommentListHeader .CommentSorts{float:left}.CommentItems{clear:both;border-bottom:1px solid #777}.CommentItems+button.ui-button{margin-top:1em}.CommentItem{clear:both;border-top:1px solid #777;padding:.75em 0;margin:0;width:100%}.CommentItem.CommentChecked{background-color:#eee}.CommentItem.CommentChecked th{color:#999}.CommentItem table{width:45%;float:left}.CommentItem table th,.CommentItem table td{padding:4px 10px 4px 0;border-bottom:1px solid #ddd;vertical-align:top;line-height:1.8}.CommentItem table th{font-weight:bold;white-space:nowrap;text-align:left;padding-left:.75em !important}.CommentItem table th:first-child{width:80px}.CommentItem table tr:last-child td,.CommentItem table tr:last-child th{border-bottom:none}.CommentItem .CommentTitle th,.CommentItem .CommentTitle td{padding-top:0}.CommentItem .CommentInput{max-width:80%}.CommentItem .CommentChangedIcon{display:none}.CommentItem.CommentItemChanged .CommentChangedIcon{display:inline;float:right;opacity:.3}.CommentItem input[type=number]{width:4em}.CommentItem .CommentVotes label{margin-right:5px}.CommentItem .CommentVotes .CommentUpvotes span{font-weight:bold;color:green;margin-right:2px}.CommentItem .CommentVotes .CommentDownvotes span{font-weight:bold;color:red;margin-right:2px}.CommentItem .CommentID{font-weight:normal;color:#333}.CommentItem .CommentWhere small{margin-right:2px}.CommentItem .CommentWhere input{width:80px}.CommentItem .CommentStars>span{display:inline;cursor:pointer;padding-right:3px}.CommentItem .CommentStars>span i.fa{font-size:18px !important}.CommentItem .CommentContent{float:left;width:55%;padding-left:1em;border:none}.CommentItem .CommentContent .CommentChildrenInfo{margin-top:.5em}.CommentItem .CommentContent .CommentReplyInfo{margin-bottom:0}.CommentItem .CommentContent .CommentText{cursor:pointer;padding-right:1em}.CommentItem .CommentContent .CommentText textarea{font-size:1em;width:100%;min-height:18em;margin-top:1em;margin-bottom:1em}.CommentItem .CommentContent .CommentText .CommentTextEdit{white-space:nowrap}.CommentItem .CommentContent .CommentTextOverflow{overflow-y:scroll;max-height:18em}.CommentItem label.CommentStatus{white-space:nowrap;display:inline-block}.CommentItem.CommentItemStatus999{display:none}.CommentCheckAll label .detail,.CommentItem .CommentTitle th label .detail{font-weight:bold;color:#444}.pw-content .MarkupPagerNav{padding-top:.5em;float:right}.pw-content .MarkupPagerNav+button{margin-top:1em}.AdminThemeReno .pw-content .MarkupPagerNav li a,.AdminThemeReno .pw-content .MarkupPagerNav li:first-child a,.AdminThemeReno .pw-content .MarkupPagerNav li.MarkupPagerNavOn a{border-color:#fff !important;border:none !important;border-left:1px solid #fff !important}.AdminThemeReno .pw-content .MarkupPagerNav li{margin-bottom:1px}.AdminThemeReno .pw-content #CommentListHeader{border-top:1px solid #eee}.AdminThemeReno .pw-content #CommentListHeader .MarkupPagerNav{margin-top:.5em}@media only screen and (max-width: 960px){.pw-content .MarkupPagerNav{float:none}#CommentListHeader{padding-bottom:1em;padding-top:1em}#CommentListHeader .MarkupPagerNav{float:none}#CommentListHeader p{margin-top:0;margin-bottom:0;padding-right:1em;padding-left:0 !important}#CommentListHeader .CommentCheckAll{width:auto}}@media only screen and (max-width: 768px){.CommentItem table{width:100%;float:none}.CommentItem table th{padding-left:0 !important}.CommentItem .CommentContent{width:100%;float:none;padding-left:0}.CommentItem .CommentContent .CommentText{padding-right:0}.CommentItem .CommentContent .CommentTextOverflow{overflow-y:auto;max-height:inherit}}.WireTabs{opacity:0} diff --git a/wire/modules/Process/ProcessCommentsManager/ProcessCommentsManager.js b/wire/modules/Process/ProcessCommentsManager/ProcessCommentsManager.js index 2f73c3cf..b4650c57 100644 --- a/wire/modules/Process/ProcessCommentsManager/ProcessCommentsManager.js +++ b/wire/modules/Process/ProcessCommentsManager/ProcessCommentsManager.js @@ -38,6 +38,20 @@ $(document).ready(function() { $text.addClass('CommentTextOverflow'); } }); + + /* + // for cite, email and website editor inputs (@todo) + $("a.CommentToggleSiblings").click(function() { + $(this).siblings().each(function() { + var $item = $(this); + if($item.attr('hidden')) { + $item.attr('hidden', false); + } else { + $item.attr('hidden', true); + } + }); + }); + */ $("#CommentLimitSelect").change(function() { window.location = './?limit=' + parseInt($(this).val()); diff --git a/wire/modules/Process/ProcessCommentsManager/ProcessCommentsManager.module b/wire/modules/Process/ProcessCommentsManager/ProcessCommentsManager.module index 40e5b2e7..ec8dd583 100644 --- a/wire/modules/Process/ProcessCommentsManager/ProcessCommentsManager.module +++ b/wire/modules/Process/ProcessCommentsManager/ProcessCommentsManager.module @@ -5,8 +5,8 @@ * * Manage all comments field data in chronological order. * - * ProcessWire 2.x - * Copyright (C) 2015 by Ryan Cramer + * ProcessWire 3.x + * Copyright (C) 2019 by Ryan Cramer * This file licensed under Mozilla Public License v2.0 http://mozilla.org/MPL/2.0/ * * https://processwire.com @@ -23,7 +23,7 @@ class ProcessCommentsManager extends Process { return array( 'title' => __('Comments', __FILE__), 'summary' => __('Manage comments in your site outside of the page editor.', __FILE__), - 'version' => 8, + 'version' => 10, 'author' => 'Ryan Cramer', 'icon' => 'comments', 'requires' => 'FieldtypeComments', @@ -167,24 +167,28 @@ class ProcessCommentsManager extends Process { public function ___executeList() { if(wireClassExists("CommentStars")) { - $cssFile = $this->wire('config')->urls->FieldtypeComments . 'comments.css'; - $jsFile = $this->wire('config')->urls->FieldtypeComments . 'comments.js'; - $this->wire('config')->styles->add($cssFile); - $this->wire('config')->scripts->add($jsFile); - CommentStars::setDefault('star', ""); + $config = $this->config; + $cssFile = $config->urls('FieldtypeComments') . 'comments.css'; + $jsFile = $config->urls('FieldtypeComments') . 'comments.js'; + $config->styles->add($cssFile); + $config->scripts->add($jsFile); + CommentStars::setDefault('star', wireIconMarkup('star')); } - - $session = $this->wire('session'); + + $session = $this->wire('session'); /** @var Session $session */ + $input = $this->wire('input'); /** @var WireInput $input */ + $sanitizer = $this->wire('sanitizer'); /** @var Sanitizer $sanitizer */ + $page = $this->wire('page'); /** @var Page $page */ - $commentID = (int) $this->input->get('id'); - $name = $this->sanitizer->fieldName($this->input->urlSegment2); + $commentID = (int) $input->get('id'); + $name = $sanitizer->fieldName($input->urlSegment2); if(!$name) return $this->error($this->_('No comments field specified in URL')); $field = $this->fields->get($name); if(!$field || !$field->type instanceof FieldtypeComments) return $this->error($this->_('Unrecognized field')); - $status = $this->input->urlSegment3; + $status = $input->urlSegment3; if(empty($status) || ($status != 'all' && !in_array($status, $this->statuses))) { - $redirectUrl = $this->wire('page')->url . "list/$field->name/all/"; + $redirectUrl = $page->url() . "list/$field->name/all/"; if($commentID) $redirectUrl .= "?id=$commentID"; $session->redirect($redirectUrl); } @@ -194,7 +198,7 @@ class ProcessCommentsManager extends Process { if($headline === 'all') $headline = $this->labelAll; $this->breadcrumb('../', $field->getLabel()); - $limit = (int) $this->wire('input')->get('limit'); + $limit = (int) $input->get('limit'); if($limit) { $session->setFor($this, 'limit', $limit); $session->redirect('./'); @@ -202,7 +206,7 @@ class ProcessCommentsManager extends Process { $limit = (int) $session->getFor($this, 'limit'); if(!$limit) $limit = (int) $this->limit; } - $sort = $this->wire('sanitizer')->name($this->wire('input')->get('sort')); + $sort = $sanitizer->name($input->get('sort')); if($sort) { $session->setFor($this, 'sort', $sort); $session->redirect('./'); @@ -211,7 +215,7 @@ class ProcessCommentsManager extends Process { if(!$sort) $sort = '-created'; } - $start = ($this->input->pageNum - 1) * $limit; + $start = ($input->pageNum() - 1) * $limit; $selector = "start=$start, limit=$limit, sort=$sort"; if($status != 'all') $selector .= ", status=$statusNum"; @@ -219,18 +223,28 @@ class ProcessCommentsManager extends Process { $filterLabels = array( 'id' => $this->_('ID'), 'parent_id' => $this->_('Replies to'), - ); + 'pages_id' => $this->_('Page'), + ); - $properties = array('cite', 'email', 'text', 'ip', 'id', 'parent_id', 'stars'); + $properties = array( + 'cite', + 'email', + 'text', + 'ip', + 'id', + 'pages_id', + 'parent_id', + 'stars' + ); - $q = $this->input->get('q'); + $q = $input->get('q'); if($q !== null) { // query $q that contain a selector - $q = trim($this->sanitizer->text($q, array('stripTags' => false))); + $q = trim($sanitizer->text($q, array('stripTags' => false))); $op = Selectors::stringHasOperator($q, true); if($op) { list($property, $value) = explode($op, $q, 2); - $property = $this->sanitizer->fieldName($property); + $property = $sanitizer->fieldName($property); if(!in_array($property, $properties)) $property = ''; } else { $property = 'text'; @@ -238,32 +252,33 @@ class ProcessCommentsManager extends Process { $value = $q; } if($property && $value) { - $selector .= ", $property$op" . $this->sanitizer->selectorValue($value); - $this->input->whitelist('q', "$property$op$value"); + $selector .= ", $property$op" . $sanitizer->selectorValue($value); + $input->whitelist('q', "$property$op$value"); } - $filterOut .= $this->wire('sanitizer')->entities(", $property$op$value"); + $filterOut .= $sanitizer->entities(", $property$op$value"); } foreach($properties as $key) { - $value = $this->input->get->$key; + $value = $input->get($key); if(is_null($value)) continue; - if($key == 'id' || $key == 'parent_id' || $key == 'stars') { + if($key == 'id' || $key == 'pages_id' || $key == 'parent_id' || $key == 'stars') { $value = (int) $value; } else { - $value = trim($this->sanitizer->text($value)); + $value = trim($sanitizer->text($value)); } - $this->input->whitelist($key, $value); - $value = $this->sanitizer->selectorValue($value); + $input->whitelist($key, $value); + $value = $sanitizer->selectorValue($value); $selector .= ", $key=$value"; - //$this->message(ucfirst($key) . ": " . htmlentities($value, ENT_QUOTES, "UTF-8") . " (" . $this->_('remove filter') . ")", Notice::allowMarkup); $filterLabel = isset($filterLabels[$key]) ? $filterLabels[$key] : ucfirst($key); - $filterOut .= $this->wire('sanitizer')->entities(", $filterLabel: $value"); + $filterOut .= $sanitizer->entities(", $filterLabel: $value"); } /** @var FieldtypeComments $fieldtype */ $fieldtype = $field->type; $comments = $fieldtype->find($field, $selector); - if($this->wire('input')->post('processComments')) $this->processComments($comments, $field); + if($input->post('processComments')) { + $this->processComments($comments, $field); + } if($filterOut) { $this->breadcrumb('./', $headline); $headline = trim($filterOut, ", "); @@ -284,23 +299,32 @@ class ProcessCommentsManager extends Process { $numDeleted = 0; $numChanged = 0; + $isSuperuser = $this->user->isSuperuser(); + $allowChangeParent = $isSuperuser && $field->get('depth') > 0; + $allowChangePage = $isSuperuser; + $commentField = $field instanceof CommentField ? $field : null; + /** @var FieldtypeComments $fieldtype */ $fieldtype = $field->type; + + /** @var WireInput $input */ + $input = $this->wire('input'); foreach($comments as $comment) { + /** @var Comment $comment */ $properties = array(); - $text = $this->input->post("CommentText{$comment->id}"); + $text = $input->post("CommentText{$comment->id}"); if(!is_null($text) && $text != $comment->text) { $comment->text = $text; // cleans it $properties['text'] = $comment->text; $numChanged++; } - if($field->useVotes) { + if($field->get('useVotes')) { foreach(array("upvotes", "downvotes") as $name) { - $votes = (int) $this->input->post("Comment" . ucfirst($name) . $comment->id); + $votes = (int) $input->post("Comment" . ucfirst($name) . $comment->id); if($votes != $comment->$name) { $comment->set($name, $votes); $properties[$name] = $comment->$name; @@ -309,8 +333,8 @@ class ProcessCommentsManager extends Process { } } - if($field->useStars) { - $stars = (int) $this->input->post("CommentStars$comment->id"); + if($field->get('useStars')) { + $stars = (int) $input->post("CommentStars$comment->id"); if($stars != $comment->stars) { $comment->set('stars', $stars); $properties['stars'] = $comment->stars; @@ -318,30 +342,102 @@ class ProcessCommentsManager extends Process { } } - $_status = $this->input->post("CommentStatus{$comment->id}"); + $_status = $input->post("CommentStatus{$comment->id}"); $status = (int) $_status; - if($status === Comment::statusDelete) { + if($status === Comment::statusDelete && (!$commentField || $commentField->allowDeleteComment($comment))) { if($fieldtype->deleteComment($comment->getPage(), $field, $comment)) { $this->message(sprintf($this->_('Deleted comment #%d'), $comment->id)); $numDeleted++; } continue; } + if($_status !== null && $status !== (int) $comment->status && array_key_exists($status, $this->statuses)) { $comment->status = $status; $numChanged++; $properties['status'] = $comment->status; } + + $changePage = null; + if($allowChangePage) { + // check for change of Page ID + $pageID = (int) $input->post("CommentPage{$comment->id}"); + if($pageID > 0 && "$pageID" !== "$comment->page") { + $page = $this->wire('pages')->get($pageID); + $parentID = $comment->parent_id; + if($parentID) $comment->parent_id = 0; // temporarily set to 0 for page change + if(!$page->id) { + $this->error( + sprintf($this->_('Unable to find page: %d'), $pageID) + ); + } else if(!$page->hasField($field)) { + $this->error( + sprintf($this->_('Page %d does not have field: %s'), $pageID, "$field") + ); + } else if($commentField && !$commentField->allowCommentPage($comment, $page, true)) { + // this one reports errors on its own + } else { + $this->message( + sprintf($this->_('Moved comment #%1$d from page %2$d to page %3$d'), $comment->id, $comment->page->id, $pageID) + ); + $properties['pages_id'] = $pageID; + if($comment->parent_id) { + $comment->parent_id = 0; + $properties['parent_id'] = 0; + } + $changePage = $page; + $numChanged++; + } + if($changePage === null) { + // if page was not changed, restore back to original parent + if($parentID) $comment->parent_id = $parentID; + } + } + } + + $changeParentID = null; + if($allowChangeParent) { + // check for change of parent on threaded comment + $parentID = $input->post("CommentParent$comment->id"); + if(strlen("$parentID") && ctype_digit("$parentID")) { + // allows for parent_id "0" but ignore blank + $parentID = (int) $parentID; + if($parentID != $comment->parent_id) { + if(!empty($properties['pages_id'])) { + // we will apply the parent change after the Page change has applied + $changeParentID = $parentID; + } else { + // parent ID has changed to another parent, or to no parent (0) + if($commentField && $commentField->allowCommentParent($comment, $parentID, true)) { + $comment->parent_id = $parentID; + $properties['parent_id'] = $parentID; + $numChanged++; + } + } + } + } else { + $parentID = null; + } + } if(count($properties)) { $fieldtype->updateComment($comment->getPage(), $field, $comment, $properties); $this->message(sprintf($this->_('Updated comment #%d'), $comment->id) . " (" . implode(', ', array_keys($properties)) . ")"); } - + + if($changeParentID !== null && $changePage !== null) { + // parent ID has changed at the same time that Page ID changed, so we apply parentID change afterwards + $comment->setPage($changePage); + if($commentField && $commentField->allowCommentParent($comment, $changeParentID, true)) { + $comment->parent_id = $changeParentID; + $fieldtype->updateComment($changePage, $field, $comment, array('parent_id' => $changeParentID)); + $numChanged++; + } + } } if($numDeleted || $numChanged) { - $pageNum = $this->input->pageNum > 1 ? 'page' . $this->input->pageNum : ''; + $pageNum = $input->pageNum() > 1 ? 'page' . $input->pageNum() : ''; $this->session->redirect('./' . $pageNum . $this->getQueryString()); } } @@ -355,165 +451,290 @@ class ProcessCommentsManager extends Process { */ protected function renderComment(Comment $comment) { - $type = ''; + $sanitizer = $this->sanitizer; + $page = $comment->getPage(); + $pageTitle = $sanitizer->entities1($page->get('title|name')); + $field = $comment->getField(); + $adminTheme = $this->wire('adminTheme'); + $isSuperuser = $this->user->isSuperuser(); + $allowDepth = $field->depth > 0; + $allowDepthChange = $isSuperuser && $allowDepth; + $allowPageChange = $isSuperuser; + $parent = $comment->parent(); $numChildren = 0; - if($comment->getField()->depth > 0) { + $text = $this->renderCommentText($comment); + $id = $comment->id; + + $icons = array( + 'edit' => 'edit', + 'upvote' => 'arrow-up', + 'downvote' => 'arrow-down', + 'changed' => 'dot-circle-o', + 'reply' => 'angle-double-down', + 'replies' => 'angle-double-right', + ); + + $outs = array( + 'status' => '', + 'website' => '', + 'stars' => '', + 'votes' => '', + 'page' => '', + 'parent' => '', + 'reply' => '', + 'where' => '', + 'children' => '', + ); + + $classes = array( + 'input' => 'CommentInput', + 'textarea' => '', + 'radio' => '', + 'checkbox' => '', + 'table' => '' + ); + + $labels = array( + 'edit' => $this->_('edit'), + 'view' => $this->_('view'), + 'page' => $this->_('Page'), + 'date' => $this->_('When'), + 'status' => $this->_('Status'), + 'cite' => $this->_('Cite'), + 'website' => $this->_('Web'), + 'email' => $this->_('Mail'), + 'none' => $this->_('None'), + 'parent' => $this->_('Parent'), + 'where' => $this->_('Where'), + 'replyTo' => $this->_('Reply to %s'), + 'stars' => $this->_('Stars'), + 'votes' => $this->_('Votes'), + 'commentId' => $this->_('Comment ID'), + ); + + $values = array( + 'cite' => $comment->cite, + 'email' => $comment->email, + 'website' => $comment->website, + 'ip' => $comment->ip, + 'date' => wireDate($this->_('Y/m/d g:i a'), $comment->created), + 'dateRelative' => wireDate('relative', $comment->created), + 'parent' => $parent && $parent->id ? $parent->id : '', + 'parentPlaceholder' => $parent && $parent->id ? '' : $labels['none'], + 'parentCite' => sprintf($labels['replyTo'], $labels['commentId']), + 'stars' => $comment->stars ? $comment->stars : '', + 'upvotes' => $comment->upvotes, + 'downvotes' => $comment->downvotes, + 'page' => (int) "$comment->page", + ); + + $urls = array( + 'parent' => $parent ? "../all/?id=$parent->id" : '', + 'children' => "../all/?parent_id=$id", + 'siblings' => "../all/?pages_id=$page->id", + 'pageView' => "$page->url#Comment$id", + 'pageEdit' => $page->editUrl(), + 'email' => "./?email=" . urlencode($values['email']), + 'cite' => "../all/?cite=" . urlencode($values['cite']), + 'ip' => "../all/?ip=" . urlencode($values['ip']), + ); + + $tooltips = array( + 'parent' => $this->_('ID of the Comment that this one is replying to.'), + 'page' => $this->_('ID of the Page that this comment lives on.'), + 'viewAll' => $this->_('View all having value'), + 'edit' => $this->_('Edit value'), + 'pageFilter' => $this->_('Show only comments from page'), + ); + + foreach($values as $key => $value) { + $values[$key] = $sanitizer->entities($value); + } + + foreach($icons as $key => $value) { + $icons[$key] = wireIconMarkup($value); + } + + if($allowDepth) { $children = $comment->children(); $numChildren = count($children); } - $adminTheme = $this->wire('adminTheme'); if($adminTheme && $adminTheme instanceof AdminThemeFramework) { - $inputClass = $adminTheme->getClass('input-small'); - $textareaClass = $adminTheme->getClass('textarea'); - $radioClass = $adminTheme->getClass('input-radio'); - $checkboxClass = $adminTheme->getClass('input-checkbox'); - $tableClass = $adminTheme->getClass('table'); - } else { - $inputClass = ''; - $textareaClass = ''; - $radioClass = ''; - $checkboxClass = ''; - $tableClass = ''; + $classes['input'] = trim("$classes[input] " . $adminTheme->getClass('input-small')); + $classes['textarea'] = $adminTheme->getClass('textarea'); + $classes['radio'] = $adminTheme->getClass('input-radio'); + $classes['checkbox'] = $adminTheme->getClass('input-checkbox'); + $classes['table'] = $adminTheme->getClass('table'); + // if(strpos($classes['input'], 'uk-input') !== false) $classes['input'] .= " uk-form-blank"; } - + foreach($this->statusTranslations as $status => $label) { - $checked = $comment->status == $status ? " checked='checked'" : ''; if($status == Comment::statusDelete && $numChildren) continue; - $type .= + $checked = $comment->status == $status ? "checked='checked' " : ''; + $outs['status'] .= "   "; + " " . + "$label" . + "  "; } - $cite = htmlentities($comment->cite, ENT_QUOTES, "UTF-8"); - $email = htmlentities($comment->email, ENT_QUOTES, "UTF-8"); - $website = htmlentities($comment->website, ENT_QUOTES, "UTF-8"); - $ip = htmlentities($comment->ip, ENT_QUOTES, "UTF-8"); - $date = wireDate($this->_('Y/m/d g:i a'), $comment->created) . " "; // comment date format - $date .= "" . wireDate('relative', $comment->created) . ""; - - $text = htmlentities($comment->get('text'), ENT_QUOTES, "UTF-8"); - $text = str_replace('\r', ' ', $text); - $text = preg_replace('/\r?(\n)/', '\r', $text); - $text = str_replace('\r\r', "
\n
\n", $text); - $text = str_replace('\r', "
\n", $text); - - $page = $comment->getPage(); - - if($page->editable()) { - $text = - "
" . - "

$text  " . $this->_('edit') . "

" . - "
"; - } + if($page->editable()) $text = + "
" . + "

$text $icons[edit] $labels[edit]

" . + "
"; + + if($allowPageChange) $outs['page'] = + "Page # " . + " "; + if($allowDepthChange) $outs['parent'] = + "" . $this->_('Reply to #') . " " . + ""; - $out = - "" . - "" . - "" . - "" . - "" . - "" . - "" . - "" . - "" . - "" . - "" . - "" . - "" . - "" . - "" . - "" . - "" . - "" . - "" . - "" . - ""; - - if($website) $out .= - "" . - "" . - "" . - ""; - - - if($comment->getField()->useStars) { - $stars = $comment->stars; - if(!$stars) $stars = ''; - $out .= - "" . - "" . - "" . - ""; - } - - if($comment->getField()->useVotes) $out .= + if($outs['parent'] || $outs['page']) $outs['where'] = "" . - "" . - "" . + "" . + "" . + ""; + + if($values['website']) $outs['website'] = + "" . + "" . + "" . + ""; + + if($field->useStars) $outs['stars'] = + "" . + "" . + "" . + ""; + + if($field->useVotes) $outs['votes'] = + "" . + "" . + "" . ""; - $out .= "
" . - "" . - "" . - "{$page->title} " . - "" . - "
" . $this->_('Status') . "$type
" . $this->_('Date') . "$date
" . $this->_('Cite') . "" . - "$cite " . - "$ip" . - "
" . $this->_('Mail') . "$email
" . $this->_('Web') . "$website
" . $this->_('Stars') . "" . - "" . - $comment->renderStars(array('input' => true)) . - "
" . $this->_('Votes') . "" . - " " . - " " . - "$labels[where]$outs[page]$outs[parent]
$labels[website]$values[website]
$labels[stars]" . + "" . + $comment->renderStars(array('input' => true)) . + "
$labels[votes]" . + " " . + " " . + "
"; - - $parentOut = ''; - $parent = $comment->parent(); if($parent) { - $parentLink = $this->wire('sanitizer')->entities($parent->cite) . " #$parent->id"; - $parentOut = - "

" . - " " . - sprintf($this->_('In reply to %s'), $parentLink) . + $a = $sanitizer->entities($parent->cite) . " $parent->id"; + $outs['reply'] = // displayed after table + "

" . + "$icons[reply] " . + sprintf($labels['replyTo'], $a) . "

"; } - $childrenOut = ''; - if($numChildren) { - $childrenLink = " " . - sprintf($this->_n('%d reply', '%d replies', $numChildren), $numChildren) . ""; - $childrenOut = "

$childrenLink

"; - } + if($numChildren) $outs['children'] = // displayed after table + "

" . + "" . + "$icons[replies] " . + sprintf($this->_n('%d reply', '%d replies', $numChildren), $numChildren) . + "" . + "

"; + + $out = + "
" . + "" . + "" . + "" . + "" . + "" . + "" . + "" . + "" . + "" . + "" . + "" . + "" . + "" . + "" . + "" . + "" . + "" . + "" . + "" . + "" . + "" . + $outs['website'] . + $outs['stars'] . + $outs['votes'] . + $outs['where'] . + "
" . + "" . + "" . + "$pageTitle " . + " " . + "$labels[view] / " . + "$labels[edit]" . + "" . + "$icons[changed]" . + "
$labels[status]$outs[status]
$labels[date]$values[date] $values[dateRelative]
$labels[cite]" . + "$values[cite] " . + //" " . // @todo + //"$icons[edit] " . + "$values[ip] " . + "
$labels[email]" . + "$values[email] " . + //" " . // @todo + //"$icons[edit]" . + "
" . + "
" . + "$outs[reply]" . + "
$text
" . + "$outs[children]" . + "
" . + "
"; + + /* $out = "
" . "$out " . "
" . - "$parentOut" . + "$outs[parent]" . "
$text
" . - "$childrenOut" . + "$outs[children]" . "
" . "
"; + */ $page->of(false); return $out; } + /** + * Prep comment text for output in editor + * + * @param Comment $comment + * @return string + * + */ + protected function renderCommentText(Comment $comment) { + $text = $this->sanitizer->entities($comment->get('text')); + $text = str_replace('\r', ' ', $text); + $text = preg_replace('/\r?(\n)/', '\r', $text); + $text = str_replace('\r\r', "
\n
\n", $text); + $text = str_replace('\r', "
\n", $text); + return $text; + } + /** * Render the comments list header * @@ -544,7 +765,7 @@ class ProcessCommentsManager extends Process { $checkAllLabel = $this->_('Check/uncheck all'); $checkAll = ""; + ""; $noCheckedLabel = $this->_('There are no checked items'); $actionsOut = @@ -664,7 +885,7 @@ class ProcessCommentsManager extends Process { ""; } - $commentsHeader = $this->renderCommentsHeader($limit, $field->useVotes, $field->useStars); + $commentsHeader = $this->renderCommentsHeader($limit, $field->get('useVotes'), $field->get('useStars')); return "
" . diff --git a/wire/modules/Process/ProcessCommentsManager/ProcessCommentsManager.scss b/wire/modules/Process/ProcessCommentsManager/ProcessCommentsManager.scss index 91afe3bd..b45be179 100644 --- a/wire/modules/Process/ProcessCommentsManager/ProcessCommentsManager.scss +++ b/wire/modules/Process/ProcessCommentsManager/ProcessCommentsManager.scss @@ -68,6 +68,7 @@ padding: 4px 10px 4px 0; border-bottom: 1px solid #ddd; vertical-align: top; + line-height: 1.8; } th { @@ -94,6 +95,11 @@ padding-top: 0; } + .CommentInput { + max-width: 80%; + } + + .CommentChangedIcon { display: none; } @@ -110,15 +116,31 @@ .CommentVotes { label { - margin-right: 1em; + margin-right: 5px; } .CommentUpvotes span { font-weight: bold; color: green; + margin-right: 2px; } .CommentDownvotes span { font-weight: bold; color: red; + margin-right: 2px; + } + } + + .CommentID { + font-weight: normal; + color: #333; + } + + .CommentWhere { + small { + margin-right: 2px; + } + input { + width: 80px; } } @@ -153,7 +175,7 @@ textarea { font-size: 1em; width: 100%; - min-height: 15em; + min-height: 18em; margin-top: 1em; margin-bottom: 1em; } @@ -174,13 +196,14 @@ .CommentTextOverflow { overflow-y: scroll; - max-height: 15em; + max-height: 18em; } } label.CommentStatus { white-space: nowrap; + display: inline-block; } &.CommentItemStatus999 {