PageRenderTime 59ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/public_html/wire/modules/Fieldtype/FieldtypeComments/CommentList.php

https://bitbucket.org/thomas1151/mats
PHP | 308 lines | 182 code | 44 blank | 82 comment | 54 complexity | 2b84d1b4a1d3bed989dc578d61649044 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause, LGPL-2.1, MPL-2.0-no-copyleft-exception
  1. <?php namespace ProcessWire;
  2. /**
  3. * ProcessWire CommentListInterface and CommentList
  4. *
  5. * CommentListInterface defines an interface for CommentLists.
  6. * CommentList provides the default implementation of this interface.
  7. *
  8. * Use of these is not required. These are just here to provide output for a FieldtypeComments field.
  9. * Typically you would iterate through the field and generate your own output. But if you just need
  10. * something simple, or are testing, then this may fit your needs.
  11. *
  12. * ProcessWire 3.x, Copyright 2016 by Ryan Cramer
  13. * https://processwire.com
  14. *
  15. *
  16. */
  17. /*
  18. * CommentListInterface defines an interface for CommentLists.
  19. *
  20. */
  21. interface CommentListInterface {
  22. public function __construct(CommentArray $comments, $options = array());
  23. public function render();
  24. public function renderItem(Comment $comment);
  25. }
  26. /**
  27. * CommentList provides the default implementation of the CommentListInterface interface.
  28. *
  29. */
  30. class CommentList extends Wire implements CommentListInterface {
  31. /**
  32. * Reference to CommentsArray provided in constructor
  33. *
  34. */
  35. protected $comments = null;
  36. protected $page;
  37. protected $field;
  38. /**
  39. * Default options that may be overridden from constructor
  40. *
  41. */
  42. protected $options = array(
  43. 'headline' => '', // '<h3>Comments</h3>',
  44. 'commentHeader' => '', // 'Posted by {cite} on {created} {stars}',
  45. 'dateFormat' => '', // 'm/d/y g:ia',
  46. 'encoding' => 'UTF-8',
  47. 'admin' => false, // shows unapproved comments if true
  48. 'useGravatar' => '', // enable gravatar? if so, specify maximum rating: [ g | pg | r | x ] or blank = disable gravatar
  49. 'useGravatarImageset' => 'mm', // default gravatar imageset, specify: [ 404 | mm | identicon | monsterid | wavatar ]
  50. 'usePermalink' => false, // @todo
  51. 'useVotes' => 0,
  52. 'useStars' => 0,
  53. 'upvoteFormat' => '&uarr;{cnt}',
  54. 'downvoteFormat' => '&darr;{cnt}',
  55. 'depth' => 0,
  56. 'replyLabel' => 'Reply',
  57. );
  58. /**
  59. * Construct the CommentList
  60. *
  61. * @param CommentArray $comments
  62. * @param array $options Options that may override those provided with the class (see CommentList::$options)
  63. *
  64. */
  65. public function __construct(CommentArray $comments, $options = array()) {
  66. $h3 = $this->_('h3'); // Headline tag
  67. $this->options['headline'] = "<$h3>" . $this->_('Comments') . "</$h3>"; // Header text
  68. $this->options['replyLabel'] = $this->_('Reply');
  69. if(empty($options['commentHeader'])) {
  70. if(empty($options['dateFormat'])) {
  71. $this->options['dateFormat'] = 'relative';
  72. }
  73. } else {
  74. //$this->options['commentHeader'] = $this->('Posted by {cite} on {created}'); // Comment header // Include the tags {cite} and {created}, but leave them untranslated
  75. if(empty($options['dateFormat'])) {
  76. $this->options['dateFormat'] = $this->_('%b %e, %Y %l:%M %p'); // Date format in either PHP strftime() or PHP date() format // Example 1 (strftime): %b %e, %Y %l:%M %p = Feb 27, 2012 1:21 PM. Example 2 (date): m/d/y g:ia = 02/27/12 1:21pm.
  77. }
  78. }
  79. $this->comments = $comments;
  80. $this->page = $comments->getPage();
  81. $this->field = $comments->getField();
  82. $this->options['useStars'] = $this->field->get('useStars');
  83. $this->options = array_merge($this->options, $options);
  84. }
  85. /**
  86. * Get replies to the given comment ID, or 0 for root level comments
  87. *
  88. * @param int|Comment $commentID
  89. * @return array
  90. *
  91. */
  92. public function getReplies($commentID) {
  93. if(is_object($commentID)) $commentID = $commentID->id;
  94. $commentID = (int) $commentID;
  95. $admin = $this->options['admin'];
  96. $replies = array();
  97. foreach($this->comments as $c) {
  98. if($c->parent_id != $commentID) continue;
  99. if(!$admin && $c->status != Comment::statusApproved) continue;
  100. $replies[] = $c;
  101. }
  102. return $replies;
  103. }
  104. /**
  105. * Rendering of comments for API demonstration and testing purposes (or feel free to use for production if suitable)
  106. *
  107. * @see Comment::render()
  108. * @return string or blank if no comments
  109. *
  110. */
  111. public function render() {
  112. $out = $this->renderList(0);
  113. if($out) $out = "\n" . $this->options['headline'] . $out;
  114. return $out;
  115. }
  116. protected function renderList($parent_id = 0, $depth = 0) {
  117. $out = $parent_id ? '' : $this->renderCheckActions();
  118. $comments = $this->options['depth'] > 0 ? $this->getReplies($parent_id) : $this->comments;
  119. if(!count($comments)) return $out;
  120. foreach($comments as $comment) $out .= $this->renderItem($comment, $depth);
  121. if(!$out) return '';
  122. $class = "CommentList";
  123. if($this->options['depth'] > 0) $class .= " CommentListThread";
  124. else $class .= " CommentListNormal";
  125. if($this->options['useGravatar']) $class .= " CommentListHasGravatar";
  126. if($parent_id) $class .= " CommentListReplies";
  127. $out = "<ul class='$class'>$out\n</ul><!--/CommentList-->";
  128. return $out;
  129. }
  130. /**
  131. * Render the comment
  132. *
  133. * This is the default rendering for development/testing/demonstration purposes
  134. *
  135. * It may be used for production, but only if it meets your needs already. Typically you'll want to render the comments
  136. * using your own code in your templates.
  137. *
  138. * @param Comment $comment
  139. * @param int $depth Default=0
  140. * @return string
  141. * @see CommentArray::render()
  142. *
  143. */
  144. public function renderItem(Comment $comment, $depth = 0) {
  145. $text = $comment->getFormatted('text');
  146. $cite = $comment->getFormatted('cite');
  147. $gravatar = '';
  148. if($this->options['useGravatar']) {
  149. $imgUrl = $comment->gravatar($this->options['useGravatar'], $this->options['useGravatarImageset']);
  150. if($imgUrl) $gravatar = "\n\t\t<img class='CommentGravatar' src='$imgUrl' alt='$cite' />";
  151. }
  152. $website = '';
  153. if($comment->website) $website = $comment->getFormatted('website');
  154. if($website) $cite = "<a href='$website' rel='nofollow' target='_blank'>$cite</a>";
  155. $created = wireDate($this->options['dateFormat'], $comment->created);
  156. if(empty($this->options['commentHeader'])) {
  157. $header = "<span class='CommentCite'>$cite</span> <small class='CommentCreated'>$created</small> ";
  158. if($this->options['useStars']) $header .= $this->renderStars($comment);
  159. if($this->options['useVotes']) $header .= $this->renderVotes($comment);
  160. } else {
  161. $header = str_replace(array('{cite}', '{created}'), array($cite, $created), $this->options['commentHeader']);
  162. if(strpos($header, '{votes}') !== false) $header = str_replace('{votes}', $this->renderVotes($comment), $header);
  163. if(strpos($header, '{stars}') !== false) $header = str_replace('{stars}', $this->renderStars($comment), $header);
  164. }
  165. $liClass = '';
  166. $replies = $this->options['depth'] > 0 ? $this->renderList($comment->id, $depth+1) : '';
  167. if($replies) $liClass .= ' CommentHasReplies';
  168. if($comment->status == Comment::statusPending) $liClass .= ' CommentStatusPending';
  169. else if($comment->status == Comment::statusSpam) $liClass .= ' CommentStatusSpam';
  170. $out =
  171. "\n\t<li id='Comment{$comment->id}' class='CommentListItem$liClass' data-comment='$comment->id'>" . $gravatar .
  172. "\n\t\t<p class='CommentHeader'>$header</p>" .
  173. "\n\t\t<div class='CommentText'>" .
  174. "\n\t\t\t<p>$text</p>" .
  175. "\n\t\t</div>";
  176. if($this->options['usePermalink']) {
  177. $permalink = $comment->getPage()->httpUrl;
  178. $urlSegmentStr = $this->wire('input')->urlSegmentStr;
  179. if($urlSegmentStr) $permalink .= rtrim($permalink, '/') . $urlSegmentStr . '/';
  180. $permalink .= '#Comment' . $comment->id;
  181. $permalink = "<a class='CommentActionPermalink' href='$permalink'>" . $this->_('Permalink') . "</a>";
  182. } else {
  183. $permalink = '';
  184. }
  185. if($this->options['depth'] > 0 && $depth < $this->options['depth']) {
  186. $out .=
  187. "\n\t\t<div class='CommentFooter'>" .
  188. "\n\t\t\t<p class='CommentAction'>" .
  189. "\n\t\t\t\t<a class='CommentActionReply' data-comment-id='$comment->id' href='#Comment{$comment->id}'>" . $this->options['replyLabel'] . "</a> " .
  190. ($permalink ? "\n\t\t\t\t$permalink" : "") .
  191. "\n\t\t\t</p>" .
  192. "\n\t\t</div>";
  193. if($replies) $out .= $replies;
  194. } else {
  195. $out .= "\n\t\t<div class='CommentFooter'></div>";
  196. }
  197. $out .= "\n\t</li>";
  198. return $out;
  199. }
  200. public function renderVotes(Comment $comment) {
  201. if(!$this->options['useVotes']) return '';
  202. $upvoteFormat = str_replace('{cnt}', "<small class='CommentUpvoteCnt'>$comment->upvotes</small>", $this->options['upvoteFormat']);
  203. $upvoteURL = "{$this->page->url}?comment_success=upvote&amp;comment_id=$comment->id&amp;field_id={$this->field->id}#Comment$comment->id";
  204. $upvoteLabel = $this->_('Like this comment');
  205. $downvoteFormat = str_replace('{cnt}', "<small class='CommentDownvoteCnt'>$comment->downvotes</small>", $this->options['downvoteFormat']);
  206. $downvoteURL = "{$this->page->url}?comment_success=downvote&amp;comment_id=$comment->id&amp;field_id={$this->field->id}#Comment$comment->id";
  207. $downvoteLabel = $this->_('Dislike this comment');
  208. // note that data-url attribute stores the href (rather than href) so that we can keep crawlers out of auto-following these links
  209. $out = "<span class='CommentVotes'>";
  210. $out .= "<a class='CommentActionUpvote' title='$upvoteLabel' data-url='$upvoteURL' href='#Comment$comment->id'>$upvoteFormat</a>";
  211. if($this->options['useVotes'] == FieldtypeComments::useVotesAll) {
  212. $out .= "<a class='CommentActionDownvote' title='$downvoteLabel' data-url='$downvoteURL' href='#Comment$comment->id'>$downvoteFormat</a>";
  213. }
  214. $out .= "</span> ";
  215. return $out;
  216. }
  217. public function renderStars(Comment $comment) {
  218. if(!$this->options['useStars']) return '';
  219. if(!$comment->stars) return '';
  220. $commentStars = new CommentStars();
  221. return $commentStars->render($comment->stars, false);
  222. }
  223. /**
  224. * Check for URL-based comment approval actions
  225. *
  226. * Note that when it finds an actionable approval code, it performs a
  227. * redirect back to the same page after completing the action, with
  228. * ?comment_success=2 on successful action, or ?comment_success=3 on
  229. * error.
  230. *
  231. * It also populates a session variable 'CommentApprovalMessage' with
  232. * a text message of what occurred.
  233. *
  234. * @return string
  235. *
  236. */
  237. public function renderCheckActions() {
  238. $action = $this->wire('input')->get('comment_success');
  239. if(empty($action) || $action === "1") return '';
  240. if($action === '2' || $action === '3') {
  241. $message = $this->wire('session')->get('CommentApprovalMessage');
  242. if($message) {
  243. $this->wire('session')->remove('CommentApprovalMessage');
  244. $class = $action === '2' ? 'success' : 'error';
  245. $commentID = (int) $this->wire('input')->get('comment_id');
  246. $message = $this->wire('sanitizer')->entities($message);
  247. if($commentID) $message = str_replace($commentID, "<a href='#Comment$commentID'>$commentID</a>", $message);
  248. return "<p id='CommentApprovalMessage' class='$class'><strong>$message</strong></p>";
  249. }
  250. }
  251. if(!$this->field) return '';
  252. require_once(dirname(__FILE__) . '/CommentNotifications.php');
  253. $no = $this->wire(new CommentNotifications($this->page, $this->field));
  254. $info = $no->checkActions();
  255. if($info['valid']) {
  256. $url = $this->page->url . '?';
  257. if($info['commentID']) $url .= "comment_id=$info[commentID]&";
  258. $url .= "comment_success=" . ($info['success'] ? '2' : '3');
  259. $this->wire('session')->set('CommentApprovalMessage', $info['message']);
  260. $this->wire('session')->redirect($url . '#CommentApprovalMessage');
  261. }
  262. return '';
  263. }
  264. }