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 {