mirror of
https://github.com/processwire/processwire.git
synced 2025-08-13 18:24:57 +02:00
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.
This commit is contained in:
@@ -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}
|
||||
|
@@ -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());
|
||||
|
@@ -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', "<i class='fa fa-star'></i>");
|
||||
$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") . " (<a href='./'>" . $this->_('remove filter') . "</a>)", 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 class='CommentStatus'>" .
|
||||
"<input clas='$radioClass' type='radio' name='CommentStatus{$comment->id}' value='$status'$checked /> " .
|
||||
"<small>$label</small>" .
|
||||
"</label> ";
|
||||
"<input class='$classes[radio]' type='radio' name='CommentStatus$id' value='$status' $checked/> " .
|
||||
"<small>$label</small>" .
|
||||
"</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 .= "<span class='detail'>" . wireDate('relative', $comment->created) . "</span>";
|
||||
|
||||
$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', "<br />\n<br />\n", $text);
|
||||
$text = str_replace('\r', "<br />\n", $text);
|
||||
|
||||
$page = $comment->getPage();
|
||||
|
||||
if($page->editable()) {
|
||||
$text =
|
||||
"<div class='CommentTextEditable' id='CommentText$comment->id' data-textarea-class='$textareaClass'>" .
|
||||
"<p>$text <a class='CommentTextEdit' href='#'><i class='fa fa-edit'></i> " . $this->_('edit') . "</a></p>" .
|
||||
"</div>";
|
||||
}
|
||||
if($page->editable()) $text =
|
||||
"<div class='CommentTextEditable' id='CommentText$id' data-textarea-class='$classes[textarea]'>" .
|
||||
"<p>$text <a class='CommentTextEdit' href='#'>$icons[edit] $labels[edit]</a></p>" .
|
||||
"</div>";
|
||||
|
||||
if($allowPageChange) $outs['page'] =
|
||||
"<span class='detail ui-priority-secondary'>Page #</span> " .
|
||||
"<input class='$classes[input] pw-tooltip' title='$tooltips[page]' name='CommentPage$id' value='$values[page]' /> ";
|
||||
|
||||
if($allowDepthChange) $outs['parent'] =
|
||||
"<span class='detail ui-priority-secondary'>" . $this->_('Reply to #') . "</span> " .
|
||||
"<input class='$classes[input] pw-tooltip' title='$tooltips[parent]' placeholder='$values[parentPlaceholder]' name='CommentParent$id' value='$values[parent]' />";
|
||||
|
||||
$out =
|
||||
"<table class='CommentItemInfo $tableClass' cellspacing='0'>" .
|
||||
"<tr class='CommentTitle'>" .
|
||||
"<th>" .
|
||||
"<label>" .
|
||||
"<input class='CommentCheckbox $checkboxClass' type='checkbox' name='comment[]' value='$comment->id' /> " .
|
||||
"<span class='detail pw-no-select'>#$comment->id</span>" .
|
||||
"</label>" .
|
||||
"</th>" .
|
||||
"<td>" .
|
||||
"<a href='{$page->url}#Comment$comment->id'><strong>{$page->title}</strong></a> " .
|
||||
"<span class='CommentChangedIcon'><i class='fa fa-dot-circle-o'></i></span>" .
|
||||
"</td>" .
|
||||
"</tr>" .
|
||||
"<tr class='CommentItemStatus'>" .
|
||||
"<th>" . $this->_('Status') . "</th>" .
|
||||
"<td class='CommentStatus'>$type</td>" .
|
||||
"</tr>" .
|
||||
"<tr>" .
|
||||
"<th>" . $this->_('Date') . "</th>" .
|
||||
"<td>$date</td>" .
|
||||
"</tr>" .
|
||||
"<tr>" .
|
||||
"<th>" . $this->_('Cite') . "</th>" .
|
||||
"<td>" .
|
||||
"<a href='./?cite=" . urlencode($cite) . "'>$cite</a> " .
|
||||
"<a class='detail' href='./?ip=" . urlencode($ip) . "'>$ip</a>" .
|
||||
"</td>" .
|
||||
"</tr>" .
|
||||
"<tr>" .
|
||||
"<th>" . $this->_('Mail') . "</th>" .
|
||||
"<td><a href='./?email=" . urlencode($email) . "'>$email</a></td>" .
|
||||
"</tr>";
|
||||
|
||||
if($website) $out .=
|
||||
"<tr>" .
|
||||
"<th>" . $this->_('Web') . "</th>" .
|
||||
"<td><a target='_blank' href='$website'>$website</a></td>" .
|
||||
"</tr>";
|
||||
|
||||
|
||||
if($comment->getField()->useStars) {
|
||||
$stars = $comment->stars;
|
||||
if(!$stars) $stars = '';
|
||||
$out .=
|
||||
"<tr>" .
|
||||
"<th>" . $this->_('Stars') . "</th>" .
|
||||
"<td class='CommentStars'>" .
|
||||
"<input type='hidden' name='CommentStars$comment->id' value='$stars' />" .
|
||||
$comment->renderStars(array('input' => true)) .
|
||||
"</td>" .
|
||||
"</tr>";
|
||||
}
|
||||
|
||||
if($comment->getField()->useVotes) $out .=
|
||||
if($outs['parent'] || $outs['page']) $outs['where'] =
|
||||
"<tr>" .
|
||||
"<th>" . $this->_('Votes') . "</th>" .
|
||||
"<td class='CommentVotes'>" .
|
||||
"<label class='CommentUpvotes'>" .
|
||||
"<input class='$inputClass' title='upvotes' type='number' min='0' name='CommentUpvotes$comment->id' value='$comment->upvotes' /> " .
|
||||
"<span><i class='fa fa-arrow-up'></i></span>" .
|
||||
"</label> " .
|
||||
"<label class='CommentDownvotes'>" .
|
||||
"<input class='$inputClass' title='downvotes' type='number' min='0' name='CommentDownvotes$comment->id' value='$comment->downvotes' /> " .
|
||||
"<span><i class='fa fa-arrow-down'></i></span>" .
|
||||
"</label> " .
|
||||
"</td>" .
|
||||
"<th>$labels[where]</th>" .
|
||||
"<td class='CommentWhere'>$outs[page]$outs[parent]</td>" .
|
||||
"</tr>";
|
||||
|
||||
if($values['website']) $outs['website'] =
|
||||
"<tr>" .
|
||||
"<th>$labels[website]</th>" .
|
||||
"<td><a target='_blank' href='$values[website]'>$values[website]</a></td>" .
|
||||
"</tr>";
|
||||
|
||||
if($field->useStars) $outs['stars'] =
|
||||
"<tr>" .
|
||||
"<th>$labels[stars]</th>" .
|
||||
"<td class='CommentStars'>" .
|
||||
"<input type='hidden' name='CommentStars$id' value='$values[stars]' />" .
|
||||
$comment->renderStars(array('input' => true)) .
|
||||
"</td>" .
|
||||
"</tr>";
|
||||
|
||||
if($field->useVotes) $outs['votes'] =
|
||||
"<tr>" .
|
||||
"<th>$labels[votes]</th>" .
|
||||
"<td class='CommentVotes'>" .
|
||||
"<label class='CommentUpvotes'>" .
|
||||
"<span>$icons[upvote]</span>" .
|
||||
"<input class='$classes[input]' title='upvotes' type='number' min='0' name='CommentUpvotes$id' value='$values[upvotes]' />" .
|
||||
"</label> " .
|
||||
"<label class='CommentDownvotes'>" .
|
||||
"<span>$icons[downvote]</span>" .
|
||||
"<input class='$classes[input]' title='downvotes' type='number' min='0' name='CommentDownvotes$id' value='$values[downvotes]' />" .
|
||||
"</label> " .
|
||||
"</td>" .
|
||||
"</tr>";
|
||||
|
||||
$out .= "</table>";
|
||||
|
||||
$parentOut = '';
|
||||
$parent = $comment->parent();
|
||||
if($parent) {
|
||||
$parentLink = $this->wire('sanitizer')->entities($parent->cite) . " <a href='../all/?id=$parent->id'>#$parent->id</a>";
|
||||
$parentOut =
|
||||
"<p class='CommentReplyInfo detail'>" .
|
||||
"<i class='fa fa-angle-double-down'></i> " .
|
||||
sprintf($this->_('In reply to %s'), $parentLink) .
|
||||
$a = $sanitizer->entities($parent->cite) . " <a href='$urls[parent]'>$parent->id</a>";
|
||||
$outs['reply'] = // displayed after table
|
||||
"<p class='CommentReplyInfo detail'>" .
|
||||
"$icons[reply] " .
|
||||
sprintf($labels['replyTo'], $a) .
|
||||
"</p>";
|
||||
}
|
||||
|
||||
$childrenOut = '';
|
||||
if($numChildren) {
|
||||
$childrenLink = "<a href='../all/?parent_id=$comment->id'><i class='fa fa-angle-double-right'></i> " .
|
||||
sprintf($this->_n('%d reply', '%d replies', $numChildren), $numChildren) . "</a>";
|
||||
$childrenOut = "<p class='CommentChildrenInfo detail'>$childrenLink</p>";
|
||||
}
|
||||
if($numChildren) $outs['children'] = // displayed after table
|
||||
"<p class='CommentChildrenInfo detail'>" .
|
||||
"<a href='$urls[children]'>" .
|
||||
"$icons[replies] " .
|
||||
sprintf($this->_n('%d reply', '%d replies', $numChildren), $numChildren) .
|
||||
"</a>" .
|
||||
"</p>";
|
||||
|
||||
|
||||
$out =
|
||||
"<div class='CommentItem ui-helper-clearfix CommentItemStatus$comment->status'>" .
|
||||
"<table class='CommentItemInfo $classes[table]' cellspacing='0'>" .
|
||||
"<tr class='CommentTitle'>" .
|
||||
"<th>" .
|
||||
"<label>" .
|
||||
"<input class='CommentCheckbox $classes[checkbox]' type='checkbox' name='comment[]' value='$id' /> " .
|
||||
"<span class='CommentID'>$id</span>" .
|
||||
"</label>" .
|
||||
"</th>" .
|
||||
"<td>" .
|
||||
"<a href='$urls[siblings]' class='pw-tooltip' title='$tooltips[pageFilter]'><strong>$pageTitle</strong></a> " .
|
||||
"<span class='detail'> " .
|
||||
"<a class='detail' href='$urls[pageView]'>$labels[view]</a> / " .
|
||||
"<a class='detail' href='$urls[pageEdit]'>$labels[edit]</a>" .
|
||||
"</span>" .
|
||||
"<span class='CommentChangedIcon'>$icons[changed]</span>" .
|
||||
"</td>" .
|
||||
"</tr>" .
|
||||
"<tr class='CommentItemStatus'>" .
|
||||
"<th>$labels[status]</th>" .
|
||||
"<td class='CommentStatus'>$outs[status]</td>" .
|
||||
"</tr>" .
|
||||
"<tr>" .
|
||||
"<th>$labels[date]</th>" .
|
||||
"<td>$values[date] <span class='detail'>$values[dateRelative]</span></td>" .
|
||||
"</tr>" .
|
||||
"<tr>" .
|
||||
"<th>$labels[cite]</th>" .
|
||||
"<td class='CommentCite'>" .
|
||||
"<a href='$urls[cite]' class='pw-tooltip' title='$tooltips[viewAll]'>$values[cite]</a> " .
|
||||
//"<input type='text' class='$classes[input]' name='CommentCite$id' value='$values[cite]' hidden /> " . // @todo
|
||||
//"<a href='#' class='CommentToggleSiblings pw-tooltip' title='$tooltips[edit]'>$icons[edit]</a> " .
|
||||
"<a class='detail pw-tooltip' title='$tooltips[viewAll]' href='$urls[ip]'>$values[ip]</a> " .
|
||||
"</td>" .
|
||||
"</tr>" .
|
||||
"<tr>" .
|
||||
"<th>$labels[email]</th>" .
|
||||
"<td>" .
|
||||
"<a href='$urls[email]' class='pw-tooltip' title='$tooltips[viewAll]'>$values[email]</a> " .
|
||||
//"<input type='email' class='$classes[input]' name='CommentEmail$id' value='$values[email]' hidden /> " . // @todo
|
||||
//"<a href='#' class='CommentToggleSiblings pw-tooltip' title='$tooltips[edit]'>$icons[edit]</a>" .
|
||||
"</td>" .
|
||||
"</tr>" .
|
||||
$outs['website'] .
|
||||
$outs['stars'] .
|
||||
$outs['votes'] .
|
||||
$outs['where'] .
|
||||
"</table>" .
|
||||
"<div class='CommentContent'>" .
|
||||
"$outs[reply]" .
|
||||
"<div class='CommentText'>$text</div>" .
|
||||
"$outs[children]" .
|
||||
"</div>" .
|
||||
"</div>";
|
||||
|
||||
/*
|
||||
$out =
|
||||
"<div class='CommentItem ui-helper-clearfix CommentItemStatus{$comment->status}'>" .
|
||||
"$out " .
|
||||
"<div class='CommentContent'>" .
|
||||
"$parentOut" .
|
||||
"$outs[parent]" .
|
||||
"<div class='CommentText'>$text</div>" .
|
||||
"$childrenOut" .
|
||||
"$outs[children]" .
|
||||
"</div>" .
|
||||
"</div>";
|
||||
*/
|
||||
|
||||
$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', "<br />\n<br />\n", $text);
|
||||
$text = str_replace('\r', "<br />\n", $text);
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the comments list header
|
||||
*
|
||||
@@ -544,7 +765,7 @@ class ProcessCommentsManager extends Process {
|
||||
$checkAllLabel = $this->_('Check/uncheck all');
|
||||
$checkAll =
|
||||
"<label title='$checkAllLabel'><input class='$checkboxClass' type='checkbox' id='CommentCheckAll' /> " .
|
||||
"<span class='detail'>#</span></label>";
|
||||
"<span class='detail'></span></label>";
|
||||
|
||||
$noCheckedLabel = $this->_('There are no checked items');
|
||||
$actionsOut =
|
||||
@@ -664,7 +885,7 @@ class ProcessCommentsManager extends Process {
|
||||
"</form>";
|
||||
}
|
||||
|
||||
$commentsHeader = $this->renderCommentsHeader($limit, $field->useVotes, $field->useStars);
|
||||
$commentsHeader = $this->renderCommentsHeader($limit, $field->get('useVotes'), $field->get('useStars'));
|
||||
|
||||
return
|
||||
"<form id='CommentListForm' action='$queryString' method='post' data-unsaved='$unsavedChangesLabel'>" .
|
||||
|
@@ -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 {
|
||||
|
Reference in New Issue
Block a user