praiadeseselle/wire/modules/Process/ProcessCommentsManager/ProcessCommentsManager.module

1670 lines
52 KiB
Text
Raw Permalink Normal View History

2022-03-08 15:55:41 +01:00
<?php namespace ProcessWire;
/**
* Comments Manager
*
* Manage all comments field data in chronological order.
*
* ProcessWire 3.x
2023-03-10 19:41:40 +01:00
* Copyright (C) 2022 by Ryan Cramer
2022-03-08 15:55:41 +01:00
* This file licensed under Mozilla Public License v2.0 http://mozilla.org/MPL/2.0/
*
* https://processwire.com
2023-03-10 19:41:40 +01:00
*
* @method string renderComment(Comment $comment, array $options = array())
2022-03-08 15:55:41 +01:00
*
*/
class ProcessCommentsManager extends Process {
/**
* Return information about this module (required)
*
*/
public static function getModuleInfo() {
return array(
'title' => __('Comments', __FILE__),
'summary' => __('Manage comments in your site outside of the page editor.', __FILE__),
2023-03-10 19:41:40 +01:00
'version' => 12,
2022-03-08 15:55:41 +01:00
'author' => 'Ryan Cramer',
'icon' => 'comments',
'requires' => 'FieldtypeComments',
'searchable' => 'comments',
'permission' => 'comments-manager',
'permissions' => array(
'comments-manager' => 'Use the comments manager',
),
'page' => array(
'name' => 'comments',
'parent' => 'setup',
'title' => 'Comments',
),
'nav' => array(
array(
'url' => '?go=approved',
'label' => __('Approved', __FILE__),
),
array(
'url' => '?go=pending',
'label' => __('Pending', __FILE__),
),
array(
'url' => '?go=spam',
'label' => __('Spam', __FILE__),
),
array(
'url' => '?go=all',
'label' => __('All', __FILE__),
)
)
);
}
/**
* Statuses and names that a Comment can have
*
*/
protected $statuses = array();
/**
* Translated statuses
*
*/
protected $statusTranslations = array();
/**
* Translated flags
*
* @var array
*
*/
protected $notifyFlagsTranslations = array();
/**
* Number of comments to show per page
*
*/
protected $limit = 10;
/**
* Headline for masthead
*
*/
protected $headline = '';
/**
* Translated 'All' label
*
* @var string
*
*/
protected $labelAll = 'All';
/**
* Initialize the comments manager and define the statuses
*
*/
public function init() {
2023-03-10 19:41:40 +01:00
$this->wire()->modules->get('FieldtypeComments');
2022-03-08 15:55:41 +01:00
parent::init();
$this->statuses = array(
Comment::statusFeatured => 'featured',
Comment::statusApproved => 'approved',
Comment::statusPending => 'pending',
Comment::statusSpam => 'spam',
Comment::statusDelete => 'delete'
);
$this->statusTranslations = array(
Comment::statusFeatured => $this->_('Featured'),
Comment::statusApproved => $this->_('Approved'),
Comment::statusPending => $this->_('Pending'),
Comment::statusSpam => $this->_('Spam'),
Comment::statusDelete => $this->_('Delete')
);
2023-03-10 19:41:40 +01:00
$this->labelAll = $this->_('All');
2022-03-08 15:55:41 +01:00
$this->notifyFlagsTranslations = array(
0 => $this->_('No'),
2023-03-10 19:41:40 +01:00
Comment::flagNotifyAll => $this->labelAll,
2022-03-08 15:55:41 +01:00
Comment::flagNotifyReply => $this->_('Replies'),
);
2023-03-10 19:41:40 +01:00
if(wireClassExists("CommentStars")) {
$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'));
}
2022-03-08 15:55:41 +01:00
}
/**
* Ask the user to select which comments field they want to manage
*
* Or, redirect to the comments field if there is only 1.
*
* @return string
*
*/
public function ___execute() {
$this->checkInstall();
// locate all the FieldtypeComments fields
$fields = array();
foreach($this->fields as $field) {
2023-03-10 19:41:40 +01:00
/** @var Field $field */
2022-03-08 15:55:41 +01:00
if($field->type instanceof FieldtypeComments) $fields[] = $field;
}
$count = count($fields);
if(!$count) {
$error = $this->_('There are no comments fields installed');
$this->error($error);
return "<p>$error</p>";
}
2023-03-10 19:41:40 +01:00
$go = $this->wire()->sanitizer->pageName($this->wire('input')->get('go'));
2022-03-08 15:55:41 +01:00
if($count == 1 || $go) {
$field = reset($fields);
$to = 'all';
if($go && in_array($go, $this->statuses)) $to = $go;
2023-03-10 19:41:40 +01:00
$this->wire()->session->redirect("./list/$field->name/$to/");
2022-03-08 15:55:41 +01:00
return '';
}
$out = "<h2>" . $this->_('Please select a comments field') . "</h2><ul>";
foreach($fields as $field) {
2023-03-10 19:41:40 +01:00
$out .= "<li><a href='./list/$field->name/pending/'>$field->name</a></li>";
2022-03-08 15:55:41 +01:00
}
$out .= "</ul>";
return $out;
}
/**
* Execute the comments list
*
* @return string
*
*/
public function ___executeList() {
$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) $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 = $input->urlSegment3;
if(empty($status) || ($status != 'all' && !in_array($status, $this->statuses))) {
$redirectUrl = $page->url() . "list/$field->name/all/";
if($commentID) $redirectUrl .= "?id=$commentID";
$session->redirect($redirectUrl);
}
$statusNum = array_search($status, $this->statuses);
$headline = $statusNum !== false ? $this->statusTranslations[$statusNum] : $status;
if($headline === 'all') $headline = $this->labelAll;
$this->breadcrumb('../', $field->getLabel());
$limit = (int) $input->get('limit');
if($limit) {
$session->setFor($this, 'limit', $limit);
$session->redirect('./');
} else {
$limit = (int) $session->getFor($this, 'limit');
if(!$limit) $limit = (int) $this->limit;
}
$sort = $sanitizer->name($input->get('sort'));
if($sort) {
$session->setFor($this, 'sort', $sort);
$session->redirect('./');
} else {
$sort = $session->getFor($this, 'sort');
if(!$sort) $sort = '-created';
}
$start = ($input->pageNum() - 1) * $limit;
$selector = "start=$start, limit=$limit, sort=$sort";
if($status != 'all') $selector .= ", status=$statusNum";
$filterOut = '';
$filterLabels = array(
'id' => $this->_('ID'),
'parent_id' => $this->_('Replies to'),
'pages_id' => $this->_('Page'),
);
$properties = array(
'cite',
'email',
'text',
'ip',
'id',
'pages_id',
'parent_id',
'stars'
);
$q = $input->get('q');
if($q !== null) {
// query $q that contain a selector
$q = trim($sanitizer->text($q, array('stripTags' => false)));
$op = Selectors::stringHasOperator($q, true);
if($op) {
list($property, $value) = explode($op, $q, 2);
$property = $sanitizer->fieldName($property);
if(!in_array($property, $properties)) $property = '';
} else {
$property = 'text';
$op = strpos($q, ' ') ? '~=' : '%=';
$value = $q;
}
if($property && $value) {
$selector .= ", $property$op" . $sanitizer->selectorValue($value);
$input->whitelist('q', "$property$op$value");
}
$filterOut .= $sanitizer->entities(", $property$op$value");
}
foreach($properties as $key) {
$value = $input->get($key);
if(is_null($value)) continue;
if($key == 'id' || $key == 'pages_id' || $key == 'parent_id' || $key == 'stars') {
$value = (int) $value;
} else {
$value = trim($sanitizer->text($value));
}
$input->whitelist($key, $value);
$value = $sanitizer->selectorValue($value);
$selector .= ", $key=$value";
$filterLabel = isset($filterLabels[$key]) ? $filterLabels[$key] : ucfirst($key);
$filterOut .= $sanitizer->entities(", $filterLabel: $value");
}
/** @var FieldtypeComments $fieldtype */
$fieldtype = $field->type;
$comments = $fieldtype->find($field, $selector);
if($input->post('processComments')) {
$this->processComments($comments, $field);
}
if($filterOut) {
$this->breadcrumb('./', $headline);
$headline = trim($filterOut, ", ");
}
$this->headline = $headline;
return $this->renderComments($comments);
}
2023-03-10 19:41:40 +01:00
/**
* Execute the comment meta viewer/editor
*
* @return string
* @since 3.0.203
*
*/
public function ___executeMeta() {
$input = $this->wire()->input;
$commentId = (int) $input->get('comment_id');
$fieldId = (int) $input->get('field_id');
$pageId = (int) $input->get('page_id');
$modal = (int) $input->get('modal');
if($commentId < 1 || $fieldId < 1 || $pageId < 1) {
throw new WireException("Missing one of: comment_id, field_id, page_id");
}
$field = $this->wire()->fields->get($fieldId);
$fieldtype = $field->type; /** @var FieldtypeComments $fieldtype */
if(!$field || (!$fieldtype instanceof FieldtypeComments)) {
throw new WireException("Invalid field specified");
}
$page = $this->wire()->pages->get($pageId);
if(!$page->id || !$page->hasField($field) || !$page->editable($field)) {
throw new WireException("Invalid or non-editable page specified");
}
$comment = $fieldtype->getCommentByID($page, $field, $commentId);
if(!$comment) throw new WireException("Cannot find comment $commentId");
$meta = $comment->getMeta();
$flags = JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
if(defined('JSON_UNESCAPED_LINE_TERMINATORS')) $flags = $flags | JSON_UNESCAPED_LINE_TERMINATORS;
$metaJSON = empty($meta) ? '{ }' : json_encode($meta, $flags);
$numRows = substr_count($metaJSON, "\n")+1;
if($numRows < 10) $numRows = 10;
$this->headline(sprintf($this->_('Meta data for comment #%d'), $commentId));
if(empty($meta)) $this->message($this->_('Comment currently has no meta data'));
// $out = "<pre id='meta-preview'>$metaEncodedJSON</pre>";
$modules = $this->wire()->modules;
/** @var InputfieldForm $form */
$form = $modules->get('InputfieldForm');
$form->attr('method', 'post');
$form->attr('action', "./?comment_id=$commentId&page_id=$pageId&field_id=$fieldId&modal=$modal");
/** @var InputfieldTextarea $f */
$f = $modules->get('InputfieldTextarea');
$f->attr('name', 'meta');
$f->label = $this->_('Meta JSON editor');
$f->val($metaJSON);
$f->attr('rows', $numRows);
$f->attr('style', "font-family:monospace;");
$form->add($f);
/** @var InputfieldSubmit $f */
$f = $modules->get('InputfieldSubmit');
$f->attr('name', 'submit_save');
$f->val($this->_('Save changes'));
$form->add($f);
/** @var InputfieldSubmit $f */
$f = $modules->get('InputfieldButton');
$f->attr('name', 'submit_cancel');
$f->setSecondary();
$f->val($this->_('Cancel'));
$form->add($f);
if($input->post('submit_save')) {
$session = $this->wire()->session;
$form->processInput($input->post);
$metaInput = $form->getChildByName('meta');
$metaSaveJSON = $metaInput->val();
if($metaSaveJSON === $metaJSON) {
$this->message($this->_('No changes detected'));
$session->redirect($form->attr('action'));
} else {
$meta = json_decode($metaSaveJSON, true);
if(is_array($meta)) {
$result = $fieldtype->updateComment($page, $field, $comment, array('meta' => $meta));
if($result) {
$this->message(sprintf($this->_('Updated meta for comment #%d'), $commentId));
$session->redirect($form->attr('action'));
} else {
$this->error(sprintf($this->_('Error updating meta for comment #%d'), $commentId));
2023-03-10 19:41:40 +01:00
}
} else {
$this->error($this->_('Cannot save because invalid JSON'));
}
}
}
$out = $form->render();
return $out;
}
/**
* Edit or add comment
*
* @return string
* @throws WireException
* @throws WirePermissionException
*
*/
protected function ___executeEdit() {
$sanitizer = $this->wire()->sanitizer;
$input = $this->wire()->input;
$user = $this->wire()->user;
$submit = false;
$out = '';
if($input->post('submit_save_comment')) {
$submit = true;
$pageId = (int) $input->post('page_id');
$fieldId = (int) $input->post('field_id');
$parentId = (int) $input->post('parent_id');
$commentId = (int) $input->post('comment_id');
} else {
$pageId = (int) $input->get('page_id');
$fieldId = (int) $input->get('field_id');
$parentId = (int) $input->get('parent_id');
$commentId = (int) $input->get('comment_id');
}
$page = $this->wire()->pages->get($pageId);
$field = $this->wire()->fields->get($fieldId); /** @var CommentField $field */
if(!$page->id) throw new WireException("Cannot find page $pageId");
if(!$field) throw new WireException("Cannot find field $fieldId");
if(!$page->hasField($field)) throw new WireException("Page $pageId does not have field $field->name");
if($commentId) {
// editing existing comment
$comment = $field->getCommentByID($page, $commentId);
if(!$comment) throw new WireException('Comment not found');
if($comment->getField()->id != $field->id) throw new WireException('Invalid field');
if($comment->getPage()->id != $page->id) throw new WireException('Invalid page');
$this->headline(sprintf($this->_('Edit comment #%d'), $comment->id));
} else {
// adding new comment
$comment = new Comment();
$this->wire($comment);
$comment->setPage($page);
$comment->setField($field);
$comment->cite = $user->get('title|name');
$comment->email = $user->get('email');
$comment->created = time();
$comment->status = Comment::statusApproved;
if($parentId) {
// new comment that is reply to existing comment
$parentComment = $field->getCommentByID($page, $parentId);
if(!$parentComment) throw new WireException("Cannot find parent comment $parentId");
$comment->parent_id = $parentId;
$this->headline(sprintf($this->_('Comment #%d by %s'), $parentId, $parentComment->getFormatted('cite')));
// show comment being replied to
$text = $sanitizer->entities1($parentComment->getFormattedCommentText());
$when = date('Y/m/d H:i', $parentComment->created) . ' • ' . wireRelativeTimeStr($parentComment->created);
$out .= "<p class='detail'>$when</p>";
$out .= "<blockquote><p>$text</p></blockquote>";
$out .= "<h2>" . $this->_('Your reply') . "</h2>";
}
}
$form = $this->buildEditForm($page, $field, $comment);
if($submit) {
$this->processEditForm($form, $page, $field, $comment);
}
return $out . $form->render();
}
/**
* Process comment edit form
*
* @param InputfieldForm $form
* @param Page $page
* @param CommentField $field
* @param Comment $comment
* @return bool
*
*/
protected function processEditForm(InputfieldForm $form, Page $page, CommentField $field, Comment $comment) {
$input = $this->wire()->input;
$form->processInput($input->post);
if(count($form->getErrors())) return false;
$values = array();
$properties = array(
'cite',
'email',
'website',
'text',
'created',
'status'
);
if($field->useVotes) {
$properties[] = 'upvotes';
$properties[] = 'downvotes';
}
foreach($properties as $name) {
$f = $form->getChildByName($name);
if(!$f) continue;
$value = $f->val();
if($value != $comment->get($name)) {
$comment->set($name, $value);
$values[$name] = $value;
}
}
if($field->useStars) {
$stars = (int) $input->post('stars');
if($stars != $comment->stars && $stars > 0 && $stars <= 5) {
$comment->stars = $stars;
$values['stars'] = $stars;
}
}
$f = $field->useNotify ? $form->getChildByName('notify_author') : null;
if($f) {
$notify = (int) $f->val();
if($this->applyCommentNotifyFlag($comment, $notify)) {
$values['flags'] = $comment->flags;
}
}
/** @var Inputfield $f */
$f = $form->getChildByName('page_id');
$pageId = $f->val();
if($pageId != $page->id) {
$f->error('Changing page is not currently supported with this form');
return false;
/*
$newPage = $this->wire()->pages->get($pageId);
if(!$newPage->editable()) {
$f->error($this->_('Selected page is not editable'));
return false;
}
if(!$newPage->hasField($field)) {
$f->error(sprintf($this->_('Selected page does not have field: %s'), $field->name));
return false;
}
*/
}
if($comment->id) {
// existing comment
if($comment->status === Comment::statusDelete) {
$success = $field->deleteComment($page, $comment);
if($success) {
$this->message(sprintf($this->_('Deleted comment #%d'), $comment->id));
$this->wire()->session->location("../list/$field->name/all/");
}
} else {
$success = $field->updateComment($page, $comment, $values);
if($success) $this->message(sprintf($this->_('Updated comment #%d'), $comment->id));
}
} else {
// new comment
/** @var Inputfield $f */
$f = $form->getChildByName('notify');
$notify = $f && $f->val();
$comment->status = Comment::statusApproved;
$success = $field->addComment($page, $comment, $notify);
if($success) $this->message(sprintf($this->_('Added comment #%d'), $comment->id));
}
if($success) {
$this->wire()->session->location("../list/$field->name/all/?id=$comment->id");
} else {
$this->error($this->_('Error saving comment'));
}
return $success;
}
/**
* Build comment edit/add form
*
* @param Page $page
* @param CommentField $field
* @param Comment $comment
* @return InputfieldForm
*
*/
protected function buildEditForm(Page $page, CommentField $field, Comment $comment) {
$modules = $this->wire()->modules;
/** @var InputfieldForm $form */
$form = $modules->get('InputfieldForm');
/** @var InputfieldText $f */
$f = $modules->get('InputfieldText');
$f->attr('name', 'cite');
$f->label = $this->_('Cite / author');
$f->val($comment->cite);
$f->required = true;
$f->columnWidth = 50;
$form->add($f);
/** @var InputfieldEmail $f */
$f = $modules->get('InputfieldEmail');
$f->attr('name', 'email');
$f->label = $this->_('Email');
$f->val($comment->email);
$f->columnWidth = 50;
$f->required = true;
$form->add($f);
/** @var InputfieldSelect $f */
$f = $modules->get('InputfieldSelect');
$f->attr('name', 'status');
$f->label = $this->_('Status');
$f->required = true;
foreach($this->statusTranslations as $status => $statusText) {
$f->addOption($status, $statusText);
}
$f->val($comment->status);
$f->columnWidth = 50;
$form->add($f);
/** @var InputfieldDatetime $f */
$f = $modules->get('InputfieldDatetime');
$f->attr('name', 'created');
$f->label = $this->_('Created');
$f->inputType = 'html';
$f->htmlType = 'datetime';
$f->val($comment->created);
$f->required = true;
$f->columnWidth = 50;
$form->add($f);
if($field->useWebsite) {
/** @var InputfieldText $f */
$f = $modules->get('InputfieldUrl');
$f->attr('name', 'website');
$f->label = $this->_('Website/URL');
$f->val($comment->website);
$form->add($f);
}
/** @var InputfieldTextarea $f */
$f = $modules->get('InputfieldTextarea');
$f->attr('name', 'text');
$f->label = $this->_('Text');
$f->val($comment->text);
$f->attr('rows', 10);
$f->required = true;
$form->add($f);
if($field->useVotes) {
/** @var InputfieldInteger $f */
$f = $modules->get('InputfieldInteger');
$f->attr('name', 'upvotes');
$f->label = $this->_('Upvotes');
$f->val($comment->upvotes);
$f->columnWidth = $field->useStars ? 25 : 50;
$f->inputType = 'number';
$form->add($f);
/** @var InputfieldInteger $f */
$f = $modules->get('InputfieldInteger');
$f->attr('name', 'downvotes');
$f->label = $this->_('Downvotes');
$f->val($comment->upvotes);
$f->columnWidth = $field->useStars ? 25 : 50;
$f->inputType = 'number';
$form->add($f);
}
if($field->useStars) {
/** @var InputfieldMarkup $f */
$f = $modules->get('InputfieldMarkup');
$f->attr('name', 'stars');
$f->label = $this->_('Stars');
$f->value =
"<input type='hidden' name='stars' value='$comment->stars' />" .
$comment->renderStars(array('input' => true));
if($field->useVotes) $f->columnWidth = 50;
$form->add($f);
}
/** @var InputfieldRadios $f */
$f = $modules->get('InputfieldRadios');
$f->attr('name', 'notify_author');
$f->label = $this->_('Notify the email listed above when');
$f->addOption(0, $this->_('Never'));
if($field->depth) $f->addOption(Comment::flagNotifyReply, $this->_('There is a reply to this comment'));
$f->addOption(Comment::flagNotifyAll, $this->_('Anytime a new comment is posted on this page'));
if($comment->flags & Comment::flagNotifyAll) {
$f->val(Comment::flagNotifyAll);
} else if($comment->flags & Comment::flagNotifyReply) {
$f->val(Comment::flagNotifyReply);
} else {
$f->val(0);
}
$form->add($f);
/** @var InputfieldToggle $f */
if(!$comment->id) {
$f->columnWidth = 50;
$f = $modules->get('InputfieldToggle');
$f->attr('name', 'notify');
$f->label = $this->_('Allow notifications to others?');
$f->detail = $this->_('When “Yes”, emails about this new comment will be sent to users that have opted in to receive notifications.');
$f->val(true);
$f->columnWidth = 50;
$form->add($f);
}
/** @var InputfieldPageListSelect $f */
/*
$f = $modules->get('InputfieldPageListSelect');
$f->attr('name', 'page_id');
$f->label = $this->_('Page');
$f->val($page->id);
$form->add($f);
*/
/** @var InputfieldHidden $f */
$f = $modules->get('InputfieldHidden');
$f->attr('name', 'page_id');
$f->val($page->id);
$form->add($f);
/** @var InputfieldHidden $f */
$f = $modules->get('InputfieldHidden');
$f->attr('name', 'field_id');
$f->val($field->id);
$form->add($f);
/** @var InputfieldHidden $f */
$f = $modules->get('InputfieldHidden');
$f->attr('name', 'comment_id');
$f->val($comment->id);
$form->add($f);
/** @var InputfieldHidden $f */
$f = $modules->get('InputfieldHidden');
$f->attr('name', 'parent_id');
$f->val($comment->parent_id);
$form->add($f);
/** @var InputfieldSubmit $f */
$f = $modules->get('InputfieldSubmit');
$f->attr('name', 'submit_save_comment');
$f->showInHeader();
$form->add($f);
return $form;
}
2022-03-08 15:55:41 +01:00
/**
* Process changes to posted comments
*
* @param CommentArray $comments
2023-03-10 19:41:40 +01:00
* @param CommentField $field
2022-03-08 15:55:41 +01:00
*
*/
protected function processComments(CommentArray $comments, Field $field) {
2023-03-10 19:41:40 +01:00
$input = $this->wire()->input;
2022-03-08 15:55:41 +01:00
$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;
foreach($comments as $comment) {
/** @var Comment $comment */
$properties = array();
2023-03-10 19:41:40 +01:00
$text = $input->post("CommentText$comment->id");
2022-03-08 15:55:41 +01:00
if(!is_null($text) && $text != $comment->text) {
$comment->text = $text; // cleans it
$properties['text'] = $comment->text;
$numChanged++;
}
if($field->get('useVotes')) {
foreach(array("upvotes", "downvotes") as $name) {
2023-03-10 19:41:40 +01:00
$votes = $input->post("Comment" . ucfirst($name) . $comment->id);
if($votes !== null) {
$votes = (int) $votes;
if($votes != $comment->$name) {
$comment->set($name, $votes);
$properties[$name] = $comment->$name;
$numChanged++;
}
2022-03-08 15:55:41 +01:00
}
}
}
if($field->get('useStars')) {
2023-03-10 19:41:40 +01:00
$stars = $input->post("CommentStars$comment->id");
if($stars !== null) {
$stars = (int) $stars;
if($stars != $comment->stars) {
$comment->set('stars', $stars);
$properties['stars'] = $comment->stars;
$numChanged++;
}
2022-03-08 15:55:41 +01:00
}
}
2023-03-10 19:41:40 +01:00
$_status = $input->post("CommentStatus$comment->id");
2022-03-08 15:55:41 +01:00
$status = (int) $_status;
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;
}
2023-03-10 19:41:40 +01:00
$notify = $input->post("CommentNotify$comment->id");
if($notify !== null && $field->useNotify && ctype_digit($notify)) {
2022-03-08 15:55:41 +01:00
$notify = (int) $notify;
2023-03-10 19:41:40 +01:00
if($this->applyCommentNotifyFlag($comment, $notify)) {
2022-03-08 15:55:41 +01:00
$properties['flags'] = $comment->flags;
}
}
$changePage = null;
if($allowChangePage) {
// check for change of Page ID
2023-03-10 19:41:40 +01:00
$pageID = (int) $input->post("CommentPage$comment->id");
2022-03-08 15:55:41 +01:00
if($pageID > 0 && "$pageID" !== "$comment->page") {
2023-03-10 19:41:40 +01:00
$page = $this->wire()->pages->get($pageID);
2022-03-08 15:55:41 +01:00
$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 = $input->pageNum() > 1 ? 'page' . $input->pageNum() : '';
$this->session->redirect('./' . $pageNum . $this->getQueryString());
}
}
/**
* Render the markup for a single comment
*
2023-03-10 19:41:40 +01:00
* Hookable since 3.0.204
*
2022-03-08 15:55:41 +01:00
* @param Comment $comment
2023-03-10 19:41:40 +01:00
* @param array $options Options (since 3.0.204)
2022-03-08 15:55:41 +01:00
* @return string
*
*/
2023-03-10 19:41:40 +01:00
protected function ___renderComment(Comment $comment, array $options = array()) {
$defaults = array(
'prependMarkup' => '',
'appendMarkup' => '',
'prependContentMarkup' => '',
'appendContentMarkup' => '',
);
$options = array_merge($defaults, $options);
2022-03-08 15:55:41 +01:00
$sanitizer = $this->sanitizer;
$page = $comment->getPage();
$pageTitle = $sanitizer->entities1($page->get('title|name'));
$field = $comment->getField();
2023-03-10 19:41:40 +01:00
$adminTheme = $this->wire()->adminTheme;
2022-03-08 15:55:41 +01:00
$isSuperuser = $this->user->isSuperuser();
$allowDepth = $field->depth > 0;
$allowDepthChange = $isSuperuser && $allowDepth;
$allowPageChange = $isSuperuser;
$parent = $comment->parent();
$numChildren = 0;
$text = $this->renderCommentText($comment);
$id = $comment->id;
2023-03-10 19:41:40 +01:00
/** @var JqueryUI $jQueryUI */
$jQueryUI = $this->wire()->modules->get('JqueryUI');
$jQueryUI->use('modal');
2022-03-08 15:55:41 +01:00
$icons = array(
'edit' => 'edit',
'upvote' => 'arrow-up',
'downvote' => 'arrow-down',
'changed' => 'dot-circle-o',
'reply' => 'angle-double-down',
'replies' => 'angle-double-right',
2023-03-10 19:41:40 +01:00
'pageEdit' => 'pencil',
'pageView' => 'external-link-square',
'commentEdit' => 'pencil',
'commentReply' => 'reply',
'meta' => 'sliders',
2022-03-08 15:55:41 +01:00
);
$outs = array(
'status' => '',
'notify' => '',
'website' => '',
'stars' => '',
'votes' => '',
'page' => '',
'parent' => '',
'reply' => '',
'where' => '',
'children' => '',
);
$classes = array(
'input' => 'CommentInput',
'textarea' => '',
'radio' => '',
'checkbox' => '',
'table' => ''
);
$labels = array(
'edit' => $this->_('edit'),
'view' => $this->_('view'),
2023-03-10 19:41:40 +01:00
'meta' => $this->_('meta'),
2022-03-08 15:55:41 +01:00
'page' => $this->_('Page'),
'date' => $this->_('When'),
'status' => $this->_('Status'),
2023-03-10 19:41:40 +01:00
'action' => $this->_('Action'),
2022-03-08 15:55:41 +01:00
'cite' => $this->_('Cite'),
'website' => $this->_('Web'),
'email' => $this->_('Mail'),
'none' => $this->_('None'),
'parent' => $this->_('Parent'),
'where' => $this->_('Where'),
2023-03-10 19:41:40 +01:00
'reply' => $this->_('reply'),
2022-03-08 15:55:41 +01:00
'replyTo' => $this->_('Reply to %s'),
'stars' => $this->_('Stars'),
'votes' => $this->_('Votes'),
'notify' => $this->_('Notify'),
'commentId' => $this->_('Comment ID'),
2023-03-10 19:41:40 +01:00
'commentEdit' => $this->_('editor'),
2022-03-08 15:55:41 +01:00
);
$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(),
2023-03-10 19:41:40 +01:00
'metaEdit' => "../../../meta/?comment_id=$comment->id&amp;page_id=$page->id&amp;field_id=$field->id&amp;modal=1",
'commentReply' => "../../../edit/?parent_id=$comment->id&amp;page_id=$page->id&amp;field_id=$field->id",
'commentEdit' => "../../../edit/?comment_id=$comment->id&amp;page_id=$page->id&amp;field_id=$field->id",
2022-03-08 15:55:41 +01:00
'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'),
2023-03-10 19:41:40 +01:00
'viewPage' => $this->_('View page'),
2022-03-08 15:55:41 +01:00
'edit' => $this->_('Edit value'),
2023-03-10 19:41:40 +01:00
'editPage' => $this->_('Edit page'),
2022-03-08 15:55:41 +01:00
'pageFilter' => $this->_('Show only comments from page'),
2023-03-10 19:41:40 +01:00
'pageEdit' => $this->_('Edit page'),
'pageView' => $this->_('View page'),
'commentEdit' => $this->_('Comment editor'),
'commentReply' => $this->_('Reply to this comment'),
'meta' => $this->_('View/edit metadata'),
2022-03-08 15:55:41 +01:00
);
foreach($values as $key => $value) {
$values[$key] = $sanitizer->entities($value);
}
foreach($icons as $key => $value) {
2023-03-10 19:41:40 +01:00
$icons[$key] = wireIconMarkup($value, 'fw');
2022-03-08 15:55:41 +01:00
}
if($allowDepth) {
$children = $comment->children();
$numChildren = count($children);
}
2023-03-10 19:41:40 +01:00
if($adminTheme instanceof AdminThemeFramework) {
2022-03-08 15:55:41 +01:00
$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');
2023-03-10 19:41:40 +01:00
$classes['select'] = $adminTheme->getClass('select-small');
2022-03-08 15:55:41 +01:00
// if(strpos($classes['input'], 'uk-input') !== false) $classes['input'] .= " uk-form-blank";
}
2023-03-10 19:41:40 +01:00
$outs['status'] = "<select name='CommentStatus$id' class='CommentStatus $classes[select]'>";
2022-03-08 15:55:41 +01:00
foreach($this->statusTranslations as $status => $label) {
if($status == Comment::statusDelete && $numChildren) continue;
2023-03-10 19:41:40 +01:00
$selected = $comment->status == $status ? " selected='selected' " : '';
$outs['status'] .= "<option value='$status'$selected>$label</option>";
/*
// $checked = $comment->status == $status ? "checked='checked' " : '';
2022-03-08 15:55:41 +01:00
$outs['status'] .=
"<label class='CommentStatus'>" .
"<input class='$classes[radio]' type='radio' name='CommentStatus$id' value='$status' $checked/>&nbsp;" .
"<small>$label</small>" .
"</label>&nbsp; ";
2023-03-10 19:41:40 +01:00
*/
2022-03-08 15:55:41 +01:00
}
2023-03-10 19:41:40 +01:00
$outs['status'] .= "</select>";
2022-03-08 15:55:41 +01:00
if($page->editable()) {
$text =
"<div class='CommentTextEditable' id='CommentText$id' data-textarea-class='$classes[textarea]'>" .
"<p>$text <a class='CommentTextEdit' href='#'>$icons[edit]&nbsp;$labels[edit]</a></p>" .
"</div>";
} else {
$text = "<p>$text</p>";
}
if($allowPageChange) $outs['page'] =
"<span class='detail ui-priority-secondary'>Page #</span>&nbsp;" .
"<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>&nbsp;" .
"<input class='$classes[input] pw-tooltip' title='$tooltips[parent]' placeholder='$values[parentPlaceholder]' name='CommentParent$id' value='$values[parent]' />";
if($outs['parent'] || $outs['page']) $outs['where'] =
"<tr>" .
"<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>";
if($field->useNotify) {
foreach($this->notifyFlagsTranslations as $flag => $label) {
$checked = false;
if($flag && $comment->flags & $flag) {
$checked = true; // All or Replies
} else if(!$flag && !($comment->flags & 2) && !($comment->flags & 4) && !($comment->flags & 8)) {
$checked = true; // None
}
$checked = $checked ? "checked='checked' " : '';
$outs['notify'] .=
"<label class='CommentNotify'>" .
"<input class='$classes[radio]' type='radio' name='CommentNotify$id' value='$flag' $checked/>&nbsp;" .
"<small>$label</small>" .
"</label>&nbsp; ";
}
$outs['notify'] = "<tr><th>$labels[notify]</th><td>$outs[notify]</td></tr>";
}
if($parent) {
$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>";
}
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>";
/** @var FieldtypeComments $fieldtype */
// $fieldtype = $field->type;
// $who = $fieldtype->getNotifyEmails($page, $field, $comment);
// $text .= "<pre>" . htmlentities(print_r($who, true)) . "</pre>";
$numRows = 0;
foreach($outs as $out) if(!empty($out)) $numRows++;
$contentClass = 'CommentContent';
if($numRows >= 7) $contentClass .= ' CommentContentLarge';
2023-03-10 19:41:40 +01:00
$meta = $comment->getMeta();
$metaCnt = count($meta);
$metaTip = $tooltips['meta'] . ' (' . sprintf($this->_n('%d value', '%d values', $metaCnt), $metaCnt) . ')';
$statusLinks = array(
"<a class='pw-tooltip' title='$tooltips[commentEdit]' href='$urls[commentEdit]'>$icons[commentEdit] $labels[commentEdit]</a>",
"<a class='pw-tooltip pw-modal' title='$metaTip' href='$urls[metaEdit]' data-buttons='button' data-close='button[name=submit_cancel]'>$icons[meta] $labels[meta]</a>",
);
if($comment->allowChildren()) {
$statusLinks[] = "<a class='pw-tooltip' title='$tooltips[commentReply]' href='$urls[commentReply]'>$icons[commentReply] $labels[reply]</a>";
}
2022-03-08 15:55:41 +01:00
$out =
"<div class='CommentItem ui-helper-clearfix CommentItemStatus$comment->status'>" .
2023-03-10 19:41:40 +01:00
$options['prependMarkup'] .
2022-03-08 15:55:41 +01:00
"<table class='CommentItemInfo $classes[table]' cellspacing='0'>" .
"<tr class='CommentTitle'>" .
"<th>" .
"<label>" .
"<input class='CommentCheckbox $classes[checkbox]' type='checkbox' name='comment[]' value='$id' />&nbsp;" .
"<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'>&nbsp;" .
2023-03-10 19:41:40 +01:00
"<a class='detail pw-tooltip' title='$tooltips[pageView]' href='$urls[pageView]'>$labels[view]</a>&nbsp;/&nbsp;" .
"<a class='detail pw-tooltip' title='$tooltips[pageEdit]' href='$urls[pageEdit]'>$labels[edit]</a>" .
2022-03-08 15:55:41 +01:00
"</span>" .
"<span class='CommentChangedIcon'>$icons[changed]</span>" .
"</td>" .
"</tr>" .
"<tr class='CommentItemStatus'>" .
2023-03-10 19:41:40 +01:00
"<th>$labels[action]</th>" .
"<td class='CommentStatus'>" .
$outs['status'] . " &nbsp;&nbsp;" .
implode('&nbsp;&nbsp;', $statusLinks) .
"</td>" .
2022-03-08 15:55:41 +01:00
"</tr>" .
$outs['notify'] .
"<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='$contentClass'>" .
2023-03-10 19:41:40 +01:00
$options['prependContentMarkup'] .
2022-03-08 15:55:41 +01:00
"$outs[reply]" .
"<div class='CommentText'>$text</div>" .
"$outs[children]" .
2023-03-10 19:41:40 +01:00
$options['appendContentMarkup'] .
2022-03-08 15:55:41 +01:00
"</div>" .
2023-03-10 19:41:40 +01:00
$options['appendMarkup'] .
2022-03-08 15:55:41 +01:00
"</div>";
/*
$out =
"<div class='CommentItem ui-helper-clearfix CommentItemStatus{$comment->status}'>" .
"$out " .
"<div class='CommentContent'>" .
"$outs[parent]" .
"<div class='CommentText'>$text</div>" .
"$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
*
* @param int $limit
* @param bool $useVotes
* @param bool $useStars
* @return string
*
*/
protected function renderCommentsHeader($limit, $useVotes, $useStars) {
$setStatusLabel = $this->_('Set status:');
$perPageLabel = $this->_('per page');
$adminTheme = $this->wire('adminTheme');
2023-03-10 19:41:40 +01:00
$selectClass = $adminTheme instanceof AdminThemeFramework ? $adminTheme->getClass('select') : '';
$checkboxClass = $adminTheme instanceof AdminThemeFramework ? $adminTheme->getClass('input-checkbox') : '';
2022-03-08 15:55:41 +01:00
$pagerLimitOut = "
<select class='$selectClass' id='CommentLimitSelect'>
<option>10 $perPageLabel</option>
<option>25 $perPageLabel</option>
<option>50 $perPageLabel</option>
<option>100 $perPageLabel</option>
</select>
";
$pagerLimitOut = str_replace("<option>$limit ", "<option selected>$limit ", $pagerLimitOut);
$checkAllLabel = $this->_('Check/uncheck all');
$checkAll =
2023-03-10 19:41:40 +01:00
"<label class='pw-tooltip' title='$checkAllLabel'><input class='$checkboxClass' type='checkbox' id='CommentCheckAll' /> " .
2022-03-08 15:55:41 +01:00
"<span class='detail'></span></label>";
$noCheckedLabel = $this->_('There are no checked items');
$actionsOut =
"<select class='$selectClass' id='CommentActions' data-nochecked='$noCheckedLabel'>" .
"<option value=''>" . $this->_('Actions (checked items)') . "</option>";
foreach($this->statusTranslations as $status => $label) {
$actionsOut .= "<option value='$status'>$setStatusLabel $label</option>";
}
if($useVotes) {
$actionsOut .= "<option value='reset-upvotes'>" . $this->_('Reset: Upvotes') . "</option>";
$actionsOut .= "<option value='reset-downvotes'>" . $this->_('Reset: Downvotes') . "</option>";
}
$actionsOut .= "</select>";
$sorts = array(
'-created' => $this->_('Date (newold)'),
'created' => $this->_('Date (oldnew)'),
);
if($useStars) {
$sorts['-stars'] = $this->_('Stars (highlow)');
$sorts['stars'] = $this->_('Stars (lowhigh)');
}
if($useVotes) {
$sorts['upvotes'] = $this->_('Upvotes');
$sorts['downvotes'] = $this->_('Downvotes');
}
$sortByOut = "<select class='$selectClass' id='CommentListSort'>";
$sortLabelPrefix = $this->_('Sort:');
foreach($sorts as $sortKey => $sortLabel) {
$sortByOut .= "<option value='$sortKey'>$sortLabelPrefix $sortLabel</option>";
}
$sortByOut .= "</select>";
$sort = $this->wire('session')->getFor($this, 'sort');
if(empty($sort)) $sort = "-created";
$sortByOut = str_replace("'$sort'", "'$sort' selected", $sortByOut);
return
"<p class='CommentCheckAll'>$checkAll</p>" .
"<p class='CommentActions'>$actionsOut</p>" .
"<p class='CommentSorts'>$sortByOut</p>" .
"<p class='CommentLimitSelect'>$pagerLimitOut</p>";
}
/**
* Render the markup for a list of comments
*
* @param CommentArray $comments
* @return string
*
*/
protected function renderComments(CommentArray $comments) {
2023-03-10 19:41:40 +01:00
$input = $this->wire()->input;
2022-03-08 15:55:41 +01:00
$commentsBody = '';
$cnt = 0;
2023-03-10 19:41:40 +01:00
$status = $input->urlSegment3;
2022-03-08 15:55:41 +01:00
$start = $comments->getStart();
$limit = $comments->getLimit();
$total = $comments->getTotal();
$pageNumPrefix = $this->config->pageNumUrlPrefix;
2023-03-10 19:41:40 +01:00
$pageNum = $input->pageNum();
2022-03-08 15:55:41 +01:00
$queryString = $this->getQueryString();
$unsavedChangesLabel = $this->_('You have unsaved changes!');
$field = $comments->getField();
2023-03-10 19:41:40 +01:00
$pagesId = (int) $input->get('pages_id');
if($pagesId) $this->breadcrumb("./$queryString", "Page $pagesId");
2022-03-08 15:55:41 +01:00
foreach($comments as $comment) {
/** @var Comment $comment */
if($status && $status != 'all' && $this->statuses[$comment->status] != $status) continue;
2023-03-10 19:41:40 +01:00
$commentsBody .= $this->renderComment($comment, array());
2022-03-08 15:55:41 +01:00
$cnt++;
if($cnt >= $limit) break;
}
/** @var MarkupPagerNav $pager */
2023-03-10 19:41:40 +01:00
$pager = $this->wire()->modules->get('MarkupPagerNav');
2022-03-08 15:55:41 +01:00
$pagerOut = $pager->render($comments, array(
'queryString' => $queryString,
'baseUrl' => "./"
));
/** @var JqueryWireTabs $wireTabs */
$wireTabs = $this->modules->get('JqueryWireTabs');
$tabs = array();
2023-03-10 19:41:40 +01:00
$class = $input->urlSegment3 === 'all' ? 'on' : '';
$tabs["tabStatusAll"] = "<a class='$class' href='../all/$queryString'>" . $this->labelAll . "</a>";
2022-03-08 15:55:41 +01:00
foreach($this->statuses as $status => $name) {
if($status == Comment::statusDelete) continue;
2023-03-10 19:41:40 +01:00
$class = $input->urlSegment3 === $name ? 'on' : '';
2022-03-08 15:55:41 +01:00
$label = $this->statusTranslations[$status];
if($label === $name) $label = ucfirst($label);
2023-03-10 19:41:40 +01:00
$tabs["tabStatus$status"] = "<a class='$class' href='../$name/$queryString'>$label</a>";
2022-03-08 15:55:41 +01:00
}
$tabsOut = $wireTabs->renderTabList($tabs);
2023-03-10 19:41:40 +01:00
if($total) {
$this->headline .= ' (' . ($start + 1) . "" . ($start + $cnt) . " " . sprintf($this->_('of %d'), $total) . ')';
} else {
$this->headline .= ' (0)';
}
2022-03-08 15:55:41 +01:00
$this->headline($this->headline);
if($cnt) {
/** @var InputfieldSubmit $button */
$button = $this->modules->get('InputfieldSubmit');
$button->attr('name', 'processComments');
$button->showInHeader();
$button = $button->render();
} else $button = '';
2023-03-10 19:41:40 +01:00
if($input->pageNum > 1) {
2022-03-08 15:55:41 +01:00
$queryString = "./$pageNumPrefix$pageNum$queryString";
}
if(!count($comments)) {
return
"<form>" .
$tabsOut .
"<h2>" . $this->_('None to display') . "</h2>" .
"</form>";
}
$commentsHeader = $this->renderCommentsHeader($limit, $field->get('useVotes'), $field->get('useStars'));
return
"<form id='CommentListForm' action='$queryString' method='post' data-unsaved='$unsavedChangesLabel'>" .
$tabsOut .
"<div id='CommentListHeader' class='ui-helper-clearfix'>" .
$pagerOut .
$commentsHeader .
"</div>" .
"<div class='CommentItems ui-helper-clearfix'>" .
$commentsBody .
"</div>" .
$pagerOut .
$button .
"</form>";
}
protected function getQueryString() {
$queryString = '';
foreach($this->input->whitelist as $key => $value) {
$queryString .= $this->wire('sanitizer')->entities($key) . "=" . $this->wire('sanitizer')->entities($value) . "&";
}
$queryString = trim($queryString, '&');
if($queryString) $queryString = "?$queryString";
return $queryString;
}
2023-03-10 19:41:40 +01:00
/**
* Apply the "notify" flag value to comment
*
* @param Comment $comment
* @param int $notify
* @return bool Returns true if flags were changed, false if not
*
*/
protected function applyCommentNotifyFlag(Comment $comment, $notify) {
$notify = (int) $notify;
$flags = $comment->flags;
$flagsPrev = $flags;
if($notify === 0 && $comment->flags) {
// remove flag
if($comment->flags & Comment::flagNotifyAll) $flags = $flags & ~Comment::flagNotifyAll; // remove
if($comment->flags & Comment::flagNotifyReply) $flags = $flags & ~Comment::flagNotifyReply; // remove
if($comment->flags & Comment::flagNotifyConfirmed) $flags = $flags & ~Comment::flagNotifyConfirmed; // remove
} else if($notify === Comment::flagNotifyAll && !($flags & Comment::flagNotifyAll)) {
if($flags & Comment::flagNotifyReply) $flags = $flags & ~Comment::flagNotifyReply; // remove
$flags = $flags | Comment::flagNotifyAll; // add
} else if($notify === Comment::flagNotifyReply) {
if($flags & Comment::flagNotifyAll) $flags = $flags & ~Comment::flagNotifyAll; // remove
$flags = $flags | Comment::flagNotifyReply; // add
}
if($notify && !$comment->id) {
// comments added in admin do not need email confirmation
$flags = $flags | Comment::flagNotifyConfirmed;
}
if($flags != $flagsPrev) {
$comment->flags = $flags;
return true;
}
return false;
}
2022-03-08 15:55:41 +01:00
protected function checkInstall() {
if($this->wire('modules')->isInstalled('ProcessLatestComments')) {
$this->warning('Please uninstall the ProcessLatestComments module (this module replaces it).');
}
}
public function ___install() {
$this->checkInstall();
2023-03-10 19:41:40 +01:00
parent::___install();
2022-03-08 15:55:41 +01:00
}
/**
* Search for items containing $text and return an array representation of them
*
* Implementation for SearchableModule interface
*
* @param string $text Text to search for
* @param array $options Options to modify behavior:
* - `edit` (bool): True if any 'url' returned should be to edit items rather than view them
* - `multilang` (bool): If true, search all languages rather than just current (default=true).
* - `start` (int): Start index (0-based), if pagination active (default=0).
* - `limit` (int): Limit to this many items, if pagination active (default=0, disabled).
* @return array
*
*/
public function search($text, array $options = array()) {
2023-03-10 19:41:40 +01:00
$sanitizer = $this->wire()->sanitizer;
2022-03-08 15:55:41 +01:00
$page = $this->getProcessPage();
2023-03-10 19:41:40 +01:00
$commentFields = array();
foreach($this->wire()->fields as $field) {
/** @var Field $field */
if($field->type instanceof FieldtypeComments) $commentFields[$field->name] = $field;
2022-03-08 15:55:41 +01:00
}
$result = array(
'title' => $page->id ? (string) $page->title : $this->className(),
'total' => 0,
'url' => '',
'items' => array(),
'properties' => array(
'text',
'cite',
'email',
'status',
'created',
'website',
'ip',
'user_agent',
'upvotes',
'downvotes',
'stars'
)
);
if($options['help']) return $result;
$operator = empty($options['operator']) ? '%=' : $options['operator'];
2023-03-10 19:41:40 +01:00
$value = $sanitizer->selectorValue($text);
2022-03-08 15:55:41 +01:00
$summaryLength = $options['verbose'] ? 1024 : 200;
if(!empty($options['property'])) {
$selector = "$options[property]$operator$value";
$q = "$options[property]$operator$text";
} else {
$selector = "text$operator$value";
2023-03-10 19:41:40 +01:00
$q = "text$operator$text";
2022-03-08 15:55:41 +01:00
}
if(!empty($options['limit'])) $selector .= ", limit=$options[limit]";
if(!empty($options['start'])) $selector .= ", start=$options[start]";
2023-03-10 19:41:40 +01:00
foreach($commentFields as $field) {
/** @var CommentField $field */
2022-03-08 15:55:41 +01:00
/** @var CommentArray $comments */
$comments = $field->type->find($selector);
if(!count($comments)) continue;
$result['total'] += $comments->getTotal();
$url = $page->url() . "list/$field->name/all/";
2023-03-10 19:41:40 +01:00
if(count($commentFields) === 1) {
$result['url'] = $url . "?q=" . urlencode($q);
}
2022-03-08 15:55:41 +01:00
foreach($comments as $comment) {
/** @var Comment $comment */
$commentPage = $comment->getPage();
$editUrl = $url . "?id=$comment->id";
$item = array(
'id' => $comment->id,
'name' => $comment->cite,
'title' => $comment->cite,
'subtitle' => $commentPage && $commentPage->id ? (string) $commentPage->get('title|name') : '',
2023-03-10 19:41:40 +01:00
'summary' => $sanitizer->truncate($comment->text, $summaryLength),
2022-03-08 15:55:41 +01:00
'url' => empty($options['edit']) ? $comment->url() : $editUrl
);
$result['items'][] = $item;
}
}
/* // for debugging:
$result['items'][] = array(
'id' => 0,
'name' => '',
'title' => $selector,
'subtitle' => '',
'summary' => '',
'url' => '#'
);
*/
return $result;
}
}