__('Comments', __FILE__), 'summary' => __('Manage comments in your site outside of the page editor.', __FILE__), 'version' => 12, '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->labelAll = $this->_('All'); $this->notifyFlagsTranslations = array( 0 => $this->_('No'), Comment::flagNotifyAll => $this->labelAll, Comment::flagNotifyReply => $this->_('Replies'), ); 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')); } } /** * 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) { /** @var Field $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 "

$error

"; } $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 = "

" . $this->_('Please select a comments field') . "

"; 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); } /** * 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 = "
$metaEncodedJSON
"; $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)); } } 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 .= "

$when

"; $out .= "

$text

"; $out .= "

" . $this->_('Your reply') . "

"; } } $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 = "" . $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; } /** * Process changes to posted comments * * @param CommentArray $comments * @param CommentField $field * */ protected function processComments(CommentArray $comments, Field $field) { $input = $this->wire()->input; $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(); $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 = $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++; } } } } if($field->get('useStars')) { $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++; } } } $_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($notify !== null && $field->useNotify && ctype_digit($notify)) { $notify = (int) $notify; if($this->applyCommentNotifyFlag($comment, $notify)) { $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 * * Hookable since 3.0.204 * * @param Comment $comment * @param array $options Options (since 3.0.204) * @return string * */ protected function ___renderComment(Comment $comment, array $options = array()) { $defaults = array( 'prependMarkup' => '', 'appendMarkup' => '', 'prependContentMarkup' => '', 'appendContentMarkup' => '', ); $options = array_merge($defaults, $options); $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; /** @var JqueryUI $jQueryUI */ $jQueryUI = $this->wire()->modules->get('JqueryUI'); $jQueryUI->use('modal'); $icons = array( 'edit' => 'edit', 'upvote' => 'arrow-up', 'downvote' => 'arrow-down', 'changed' => 'dot-circle-o', 'reply' => 'angle-double-down', 'replies' => 'angle-double-right', 'pageEdit' => 'pencil', 'pageView' => 'external-link-square', 'commentEdit' => 'pencil', 'commentReply' => 'reply', 'meta' => 'sliders', ); $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'), 'meta' => $this->_('meta'), 'page' => $this->_('Page'), 'date' => $this->_('When'), 'status' => $this->_('Status'), 'action' => $this->_('Action'), 'cite' => $this->_('Cite'), 'website' => $this->_('Web'), 'email' => $this->_('Mail'), 'none' => $this->_('None'), 'parent' => $this->_('Parent'), 'where' => $this->_('Where'), 'reply' => $this->_('reply'), 'replyTo' => $this->_('Reply to %s'), 'stars' => $this->_('Stars'), 'votes' => $this->_('Votes'), 'notify' => $this->_('Notify'), 'commentId' => $this->_('Comment ID'), 'commentEdit' => $this->_('editor'), ); $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(), 'metaEdit' => "../../../meta/?comment_id=$comment->id&page_id=$page->id&field_id=$field->id&modal=1", 'commentReply' => "../../../edit/?parent_id=$comment->id&page_id=$page->id&field_id=$field->id", 'commentEdit' => "../../../edit/?comment_id=$comment->id&page_id=$page->id&field_id=$field->id", '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'), 'viewPage' => $this->_('View page'), 'edit' => $this->_('Edit value'), 'editPage' => $this->_('Edit page'), 'pageFilter' => $this->_('Show only comments from page'), 'pageEdit' => $this->_('Edit page'), 'pageView' => $this->_('View page'), 'commentEdit' => $this->_('Comment editor'), 'commentReply' => $this->_('Reply to this comment'), 'meta' => $this->_('View/edit metadata'), ); foreach($values as $key => $value) { $values[$key] = $sanitizer->entities($value); } foreach($icons as $key => $value) { $icons[$key] = wireIconMarkup($value, 'fw'); } if($allowDepth) { $children = $comment->children(); $numChildren = count($children); } if($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'); $classes['select'] = $adminTheme->getClass('select-small'); // if(strpos($classes['input'], 'uk-input') !== false) $classes['input'] .= " uk-form-blank"; } $outs['status'] = " " . "$label" . "  "; */ } $outs['status'] .= ""; if($page->editable()) { $text = "
" . "

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

" . "
"; } else { $text = "

$text

"; } if($allowPageChange) $outs['page'] = "Page # " . " "; if($allowDepthChange) $outs['parent'] = "" . $this->_('Reply to #') . " " . ""; if($outs['parent'] || $outs['page']) $outs['where'] = "" . "$labels[where]" . "$outs[page]$outs[parent]" . ""; if($values['website']) $outs['website'] = "" . "$labels[website]" . "$values[website]" . ""; if($field->useStars) $outs['stars'] = "" . "$labels[stars]" . "" . "" . $comment->renderStars(array('input' => true)) . "" . ""; if($field->useVotes) $outs['votes'] = "" . "$labels[votes]" . "" . " " . " " . "" . ""; 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'] .= "  "; } $outs['notify'] = "$labels[notify]$outs[notify]"; } if($parent) { $a = $sanitizer->entities($parent->cite) . " $parent->id"; $outs['reply'] = // displayed after table "

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

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

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

"; /** @var FieldtypeComments $fieldtype */ // $fieldtype = $field->type; // $who = $fieldtype->getNotifyEmails($page, $field, $comment); // $text .= "
" . htmlentities(print_r($who, true)) . "
"; $numRows = 0; foreach($outs as $out) if(!empty($out)) $numRows++; $contentClass = 'CommentContent'; if($numRows >= 7) $contentClass .= ' CommentContentLarge'; $meta = $comment->getMeta(); $metaCnt = count($meta); $metaTip = $tooltips['meta'] . ' (' . sprintf($this->_n('%d value', '%d values', $metaCnt), $metaCnt) . ')'; $statusLinks = array( "$icons[commentEdit] $labels[commentEdit]", "$icons[meta] $labels[meta]", ); if($comment->allowChildren()) { $statusLinks[] = "$icons[commentReply] $labels[reply]"; } $out = "
" . $options['prependMarkup'] . "" . "" . "" . "" . "" . "" . "" . "" . "" . $outs['notify'] . "" . "" . "" . "" . "" . "" . "" . "" . "" . "" . "" . "" . $outs['website'] . $outs['stars'] . $outs['votes'] . $outs['where'] . "
" . "" . "" . "$pageTitle " . " " . "$labels[view] / " . "$labels[edit]" . "" . "$icons[changed]" . "
$labels[action]" . $outs['status'] . "   " . implode('  ', $statusLinks) . "
$labels[date]$values[date] $values[dateRelative]
$labels[cite]" . "$values[cite] " . //" " . // @todo //"$icons[edit] " . "$values[ip] " . "
$labels[email]" . "$values[email] " . //" " . // @todo //"$icons[edit]" . "
" . "
" . $options['prependContentMarkup'] . "$outs[reply]" . "
$text
" . "$outs[children]" . $options['appendContentMarkup'] . "
" . $options['appendMarkup'] . "
"; /* $out = "
" . "$out " . "
" . "$outs[parent]" . "
$text
" . "$outs[children]" . "
" . "
"; */ $page->of(false); return $out; } /** * Prep comment text for output in editor * * @param Comment $comment * @return string * */ protected function renderCommentText(Comment $comment) { $text = $this->sanitizer->entities($comment->get('text')); $text = str_replace('\r', ' ', $text); $text = preg_replace('/\r?(\n)/', '\r', $text); $text = str_replace('\r\r', "
\n
\n", $text); $text = str_replace('\r', "
\n", $text); return $text; } /** * Render the comments list header * * @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 instanceof AdminThemeFramework ? $adminTheme->getClass('select') : ''; $checkboxClass = $adminTheme instanceof AdminThemeFramework ? $adminTheme->getClass('input-checkbox') : ''; $pagerLimitOut = " "; $pagerLimitOut = str_replace("