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

1089 lines
34 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
* Copyright (C) 2019 by Ryan Cramer
* This file licensed under Mozilla Public License v2.0 http://mozilla.org/MPL/2.0/
*
* https://processwire.com
*
*/
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__),
'version' => 10,
'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() {
$this->wire('modules')->get('FieldtypeComments');
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')
);
$this->notifyFlagsTranslations = array(
0 => $this->_('No'),
Comment::flagNotifyAll => $this->_('All'),
Comment::flagNotifyReply => $this->_('Replies'),
);
$this->labelAll = $this->_('All');
}
/**
* 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) {
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>";
}
$go = $this->wire('sanitizer')->pageName($this->wire('input')->get('go'));
if($count == 1 || $go) {
$field = reset($fields);
$to = 'all';
if($go && in_array($go, $this->statuses)) $to = $go;
$this->wire('session')->redirect("./list/$field->name/$to/");
return '';
}
$out = "<h2>" . $this->_('Please select a comments field') . "</h2><ul>";
foreach($fields as $field) {
$out .= "<li><a href='./list/{$field->name}/pending/'>{$field->name}</a></li>";
}
$out .= "</ul>";
return $out;
}
/**
* Execute the comments list
*
* @return string
*
*/
public function ___executeList() {
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'));
}
$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);
}
/**
* Process changes to posted comments
*
* @param CommentArray $comments
* @param Field $field
*
*/
protected function processComments(CommentArray $comments, Field $field) {
$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 = $input->post("CommentText{$comment->id}");
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) {
$votes = (int) $input->post("Comment" . ucfirst($name) . $comment->id);
if($votes != $comment->$name) {
$comment->set($name, $votes);
$properties[$name] = $comment->$name;
$numChanged++;
}
}
}
if($field->get('useStars')) {
$stars = (int) $input->post("CommentStars$comment->id");
if($stars != $comment->stars) {
$comment->set('stars', $stars);
$properties['stars'] = $comment->stars;
$numChanged++;
}
}
$_status = $input->post("CommentStatus{$comment->id}");
$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;
}
$notify = $input->post("CommentNotify{$comment->id}");
if($field->useNotify && ctype_digit($notify)) {
$notify = (int) $notify;
if($notify === 0 && $comment->flags) {
if($comment->flags & Comment::flagNotifyAll) $comment->flags = $comment->flags & ~Comment::flagNotifyAll;
if($comment->flags & Comment::flagNotifyReply) $comment->flags = $comment->flags & ~Comment::flagNotifyReply;
if($comment->flags & Comment::flagNotifyConfirmed) $comment->flags = $comment->flags & ~Comment::flagNotifyConfirmed;
$properties['flags'] = $comment->flags;
} else if($notify === Comment::flagNotifyAll && !($comment->flags & Comment::flagNotifyAll)) {
$comment->flags = $comment->flags | Comment::flagNotifyAll;
$properties['flags'] = $comment['flags'];
} else if($notify === Comment::flagNotifyReply) {
$comment->flags = $comment->flags | Comment::flagNotifyReply;
$properties['flags'] = $comment['flags'];
}
}
$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 = $input->pageNum() > 1 ? 'page' . $input->pageNum() : '';
$this->session->redirect('./' . $pageNum . $this->getQueryString());
}
}
/**
* Render the markup for a single comment
*
* @param Comment $comment
* @return string
*
*/
protected function renderComment(Comment $comment) {
$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;
$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' => '',
'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'),
'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'),
'notify' => $this->_('Notify'),
'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);
}
if($adminTheme && $adminTheme instanceof AdminThemeFramework) {
$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) {
if($status == Comment::statusDelete && $numChildren) continue;
$checked = $comment->status == $status ? "checked='checked' " : '';
$outs['status'] .=
"<label class='CommentStatus'>" .
"<input class='$classes[radio]' type='radio' name='CommentStatus$id' value='$status' $checked/>&nbsp;" .
"<small>$label</small>" .
"</label>&nbsp; ";
}
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';
$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' />&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;" .
"<a class='detail' href='$urls[pageView]'>$labels[view]</a>&nbsp;/&nbsp;" .
"<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>" .
$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'>" .
"$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'>" .
"$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');
$selectClass = $adminTheme && $adminTheme instanceof AdminThemeFramework ? $adminTheme->getClass('select') : '';
$checkboxClass = $adminTheme && $adminTheme instanceof AdminThemeFramework ? $adminTheme->getClass('input-checkbox') : '';
$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 =
"<label title='$checkAllLabel'><input class='$checkboxClass' type='checkbox' id='CommentCheckAll' /> " .
"<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) {
$commentsBody = '';
$cnt = 0;
$status = $this->input->urlSegment3;
$start = $comments->getStart();
$limit = $comments->getLimit();
$total = $comments->getTotal();
$pageNumPrefix = $this->config->pageNumUrlPrefix;
$pageNum = $this->wire('input')->pageNum;
$queryString = $this->getQueryString();
$unsavedChangesLabel = $this->_('You have unsaved changes!');
$field = $comments->getField();
foreach($comments as $comment) {
/** @var Comment $comment */
if($status && $status != 'all' && $this->statuses[$comment->status] != $status) continue;
$commentsBody .= $this->renderComment($comment);
$cnt++;
if($cnt >= $limit) break;
}
/** @var MarkupPagerNav $pager */
$pager = $this->wire('modules')->get('MarkupPagerNav');
$pagerOut = $pager->render($comments, array(
'queryString' => $queryString,
'baseUrl' => "./"
));
/** @var JqueryWireTabs $wireTabs */
$wireTabs = $this->modules->get('JqueryWireTabs');
$tabs = array();
$class = $this->input->urlSegment3 === 'all' ? 'on' : '';
$tabs["tabStatusAll"] = "<a class='$class' href='../all/'>" . $this->labelAll . "</a>";
foreach($this->statuses as $status => $name) {
if($status == Comment::statusDelete) continue;
$class = $this->input->urlSegment3 === $name ? 'on' : '';
$label = $this->statusTranslations[$status];
if($label === $name) $label = ucfirst($label);
$tabs["tabStatus$status"] = "<a class='$class' href='../$name/'>$label</a>";
}
$tabsOut = $wireTabs->renderTabList($tabs);
$this->headline .= ' (' . ($start+1) . "" . ($start + $cnt) . " " . sprintf($this->_('of %d'), $total) . ')';
$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 = '';
if($this->input->pageNum > 1) {
$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;
}
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();
return parent::___install();
}
/**
* 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()) {
/** @var Languages $languages */
$page = $this->getProcessPage();
$fields = array();
foreach($this->wire('fields') as $field) {
if($field->type instanceof FieldtypeComments) $fields[$field->name] = $field;
}
$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'];
$value = $this->wire('sanitizer')->selectorValue($text);
$summaryLength = $options['verbose'] ? 1024 : 200;
if(!empty($options['property'])) {
$selector = "$options[property]$operator$value";
$q = "$options[property]$operator$text";
} else {
$selector = "text$operator$value";
$q = "text{$operator}$text";
}
if(!empty($options['limit'])) $selector .= ", limit=$options[limit]";
if(!empty($options['start'])) $selector .= ", start=$options[start]";
foreach($fields as $fieldName => $field) {
/** @var CommentArray $comments */
$comments = $field->type->find($selector);
if(!count($comments)) continue;
$result['total'] += $comments->getTotal();
$url = $page->url() . "list/$field->name/all/";
if(count($fields) == 1) $result['url'] = $url . "?q=" . urlencode($q);
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') : '',
'summary' => $this->wire('sanitizer')->truncate($comment->text, $summaryLength),
'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;
}
}