PageRenderTime 28ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/Comment/src/Controller/CommentUIControllerTrait.php

http://github.com/QuickAppsCMS/QuickApps-CMS
PHP | 346 lines | 173 code | 28 blank | 145 comment | 19 complexity | 843bbb27303506380bd776fedcba953e MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception, GPL-3.0
  1. <?php
  2. /**
  3. * Licensed under The GPL-3.0 License
  4. * For full copyright and license information, please see the LICENSE.txt
  5. * Redistributions of files must retain the above copyright notice.
  6. *
  7. * @since 2.0.0
  8. * @author Christopher Castro <chris@quickapps.es>
  9. * @link http://www.quickappscms.org
  10. * @license http://opensource.org/licenses/gpl-3.0.html GPL-3.0 License
  11. */
  12. namespace Comment\Controller;
  13. use Cake\Event\Event;
  14. use Cake\Network\Exception\ForbiddenException;
  15. use Cake\ORM\Exception\RecordNotFoundException;
  16. use Cake\Utility\Inflector;
  17. use CMS\Core\Plugin;
  18. use Field\Utility\TextToolbox;
  19. /**
  20. * Comment UI Trait.
  21. *
  22. * Other plugins may `extends` Comment plugin by using this trait in their
  23. * controllers.
  24. *
  25. * With this trait, Comment plugin provides an user friendly UI for manage
  26. * entity's comments. It provides a comment-manager user interface (UI) by
  27. * attaching a series of actions over a `clean` controller.
  28. *
  29. * # Usage:
  30. *
  31. * Beside adding `use CommentUIControllerTrait;` to your controller you MUST
  32. * also indicate the name of the Table being managed. Example:
  33. *
  34. * ```php
  35. * uses Comment\Controller\CommentUIControllerTrait;
  36. *
  37. * class MyCleanController extends AppController {
  38. * use CommentUIControllerTrait;
  39. * // underscored table alias. e.g.: "user_photos"
  40. * protected $_manageTable = 'contents';
  41. * }
  42. * ```
  43. *
  44. * In order to avoid trait collision you should always `extend` Comment UI using
  45. * this trait over a `clean` controller. This is, an empty controller class with
  46. * no methods defined. For instance, create a new controller class
  47. * `MyPlugin\Controller\MyTableCommentManagerController` and use this trait to
  48. * handle comments for "MyTable" database table.
  49. *
  50. * ## _inResponseTo() method
  51. *
  52. * Also, your controller must implement the `_inResponseTo()` method. This method
  53. * must return a string value describing the entity that the given comment is
  54. * attached to. For example:
  55. *
  56. * ```php
  57. * protected function _inResponseTo(\Comment\Model\Entity\Comment $comment) {
  58. * $this->loadModel('MyPlugin.Persons');
  59. * $person = $this->Persons->get($comment->entity_id);
  60. * return "{$person->name}<br />{$person->email}";
  61. * }
  62. * ```php
  63. *
  64. * # Requirements
  65. *
  66. * - This trait should only be used over a clean controller.
  67. * - You must define `$_manageTable` property in your controller.
  68. * - Your Controller must be a backend-controller (under `Controller\Admin` namespace).
  69. * - Your Controller must implement the `_inResponseTo()` method described above.
  70. */
  71. trait CommentUIControllerTrait
  72. {
  73. /**
  74. * Validation rules.
  75. *
  76. * @param \Cake\Event\Event $event The event instance.
  77. * @return void
  78. * @throws \Cake\Network\Exception\ForbiddenException When
  79. * - $_manageTable is not defined.
  80. * - trait is used in non-controller classes.
  81. * - the controller is not a backend controller.
  82. * - the "_inResponseTo()" is not implemented.
  83. */
  84. public function beforeFilter(Event $event)
  85. {
  86. $requestParams = $event->subject()->request->params;
  87. if (!isset($this->_manageTable) || empty($this->_manageTable)) {
  88. throw new ForbiddenException(__d('comment', 'CommentUIControllerTrait: The property $_manageTable was not found or is empty.'));
  89. } elseif (!($this instanceof \Cake\Controller\Controller)) {
  90. throw new ForbiddenException(__d('comment', 'CommentUIControllerTrait: This trait must be used on instances of Cake\Controller\Controller.'));
  91. } elseif (!isset($requestParams['prefix']) || strtolower($requestParams['prefix']) !== 'admin') {
  92. throw new ForbiddenException(__d('comment', 'CommentUIControllerTrait: This trait must be used on backend-controllers only.'));
  93. } elseif (!method_exists($this, '_inResponseTo')) {
  94. throw new ForbiddenException(__d('comment', 'CommentUIControllerTrait: This trait needs you to implement the "_inResponseTo()" method.'));
  95. }
  96. $this->_manageTable = Inflector::underscore($this->_manageTable);
  97. $this->helpers[] = 'Time';
  98. $this->helpers[] = 'Paginator';
  99. $this->paginate['limit'] = 10;
  100. $this->loadComponent('Paginator');
  101. $this->loadComponent('Comment.Comment');
  102. $this->Comment->initialize([]);
  103. }
  104. /**
  105. * Fallback for template location when extending Comment UI API.
  106. *
  107. * If controller tries to render an unexisting template under its Template
  108. * directory, then we try to find that view under `Comment/Template/CommentUI`
  109. * directory.
  110. *
  111. * ### Example:
  112. *
  113. * Suppose you are using this trait to manage comments attached to `Persons`
  114. * entities. You would probably have a `Person` plugin and a `clean` controller
  115. * as follow:
  116. *
  117. * // http://example.com/admin/person/comments_manager
  118. * Person\Controller\CommentsManagerController::index()
  119. *
  120. * The above controller action will try to render
  121. * `/plugins/Person/Template/CommentsManager/index.ctp`. But if does not exists
  122. * then `<QuickAppsCorePath>/plugins/Comment/Template/CommentUI/index.ctp` will
  123. * be used instead.
  124. *
  125. * Of course you may create your own template and skip this fallback functionality.
  126. *
  127. * @param \Cake\Event\Event $event the event instance.
  128. * @return void
  129. */
  130. public function beforeRender(Event $event)
  131. {
  132. $plugin = (string)Inflector::camelize($event->subject()->request->params['plugin']);
  133. $controller = Inflector::camelize($event->subject()->request->params['controller']);
  134. $action = Inflector::underscore($event->subject()->request->params['action']);
  135. $prefix = '';
  136. if (!empty($event->subject()->request->params['prefix'])) {
  137. $prefix = Inflector::camelize($event->subject()->request->params['prefix']) . '/';
  138. }
  139. $templatePath = Plugin::classPath($plugin) . "Template/{$prefix}{$controller}/{$action}.ctp";
  140. if (!is_readable($templatePath)) {
  141. $alternativeTemplatePath = Plugin::classPath('Comment') . 'Template/CommentUI';
  142. if (is_readable("{$alternativeTemplatePath}/{$action}.ctp")) {
  143. $this->plugin = 'Comment';
  144. $this->viewBuilder()->templatePath('CommentUI');
  145. }
  146. }
  147. parent::beforeRender($event);
  148. }
  149. /**
  150. * Field UI main action.
  151. *
  152. * Shows all the comments attached to the Table being managed. Possibles values
  153. * for status are:
  154. *
  155. * - `all`: Comments marked as `pending` or `approved`. (by default)
  156. * - `pending`: Comments awaiting for moderation.
  157. * - `approved`: Comments approved and published.
  158. * - `spam`: Comments marked as SPAM by Akismet.
  159. * - `trash`: Comments that were sent to trash bin.
  160. *
  161. * @param string $status Filter comments by `status`, see list above
  162. * @return void
  163. */
  164. public function index($status = 'all')
  165. {
  166. $this->loadModel('Comment.Comments');
  167. $this->_setCounters();
  168. $search = ''; // fills form's input
  169. $conditions = ['table_alias' => $this->_manageTable];
  170. if (in_array($status, ['pending', 'approved', 'spam', 'trash'])) {
  171. $conditions['Comments.status'] = $status;
  172. } else {
  173. $status = 'all';
  174. $conditions['Comments.status IN'] = ['pending', 'approved'];
  175. }
  176. if (!empty($this->request->query['search'])) {
  177. $search = $this->request->query['search'];
  178. $conditions['OR'] = [
  179. 'Comments.subject LIKE' => "%{$this->request->query['search']}%",
  180. 'Comments.body LIKE' => "%{$this->request->query['search']}%",
  181. ];
  182. }
  183. $comments = $this->Comments
  184. ->find()
  185. ->contain(['Users'])
  186. ->where($conditions)
  187. ->order(['Comments.created' => 'DESC'])
  188. ->formatResults(function ($results) {
  189. return $results->map(function ($comment) {
  190. $comment->set('entity', $this->_inResponseTo($comment));
  191. $comment->set(
  192. 'body',
  193. TextToolbox::trimmer(
  194. TextToolbox::plainProcessor(
  195. TextToolbox::stripHtmlTags($comment->body)
  196. ),
  197. 180
  198. )
  199. );
  200. return $comment;
  201. });
  202. });
  203. $this->title(__d('comment', 'Comments List'));
  204. $this->set('search', $search);
  205. $this->set('filterBy', $status);
  206. $this->set('comments', $this->paginate($comments));
  207. }
  208. /**
  209. * Edit form for given comment.
  210. *
  211. * @param int $id Comment id
  212. * @return void Redirects to previous page
  213. * @throws \Cake\ORM\Exception\RecordNotFoundException When comment was not found
  214. */
  215. public function edit($id)
  216. {
  217. $this->loadModel('Comment.Comments');
  218. $comment = $this->Comments
  219. ->find()
  220. ->contain(['Users'])
  221. ->where(['Comments.id' => $id, 'Comments.table_alias' => $this->_manageTable])
  222. ->first();
  223. if (!$comment) {
  224. throw new RecordNotFoundException(__d('comment', 'Comment could not be found.'));
  225. }
  226. if ($this->request->data()) {
  227. $comment->accessible('*', false);
  228. $comment->accessible(['subject', 'body', 'author_name', 'author_email', 'author_web', 'status'], true);
  229. $validator = $comment->user_id ? 'default' : 'anonymous';
  230. $this->Comments->patchEntity($comment, $this->request->data(), ['validate' => $validator]);
  231. $errors = $comment->errors();
  232. if (empty($errors)) {
  233. $this->Comments->save($comment, ['associated' => false]);
  234. $this->Flash->success(__d('comment', 'Comment saved!.'));
  235. $this->redirect($this->referer());
  236. } else {
  237. $this->Flash->danger(__d('comment', 'Comment could not be saved, please check your information.'));
  238. }
  239. }
  240. $this->title(__d('comment', 'Editing Comment'));
  241. $this->set('comment', $comment);
  242. }
  243. /**
  244. * Changes the status of the given comment.
  245. *
  246. * @param int $id Comment id
  247. * @param string $status New status for the comment
  248. * @return void Redirects to previous page
  249. */
  250. public function status($id, $status)
  251. {
  252. if (in_array($status, ['pending', 'approved', 'spam', 'trash'])) {
  253. $this->loadModel('Comment.Comments');
  254. if ($comment = $this->Comments->get($id)) {
  255. $comment->set('status', $status);
  256. $this->Comments->save($comment);
  257. }
  258. }
  259. $this->title(__d('comment', 'Change Comment Status'));
  260. $this->redirect($this->referer());
  261. }
  262. /**
  263. * Permanently deletes the given comment.
  264. *
  265. * @param int $id Comment id
  266. * @return void Redirects to previous page
  267. */
  268. public function delete($id)
  269. {
  270. $this->loadModel('Comment.Comments');
  271. $comment = $this->Comments
  272. ->find()
  273. ->where(['Comments.id' => $id, 'Comments.table_alias' => $this->_manageTable])
  274. ->first();
  275. if ($comment) {
  276. if ($this->Comments->delete($comment)) {
  277. $this->Flash->success(__d('comment', 'Comment was successfully deleted!'));
  278. } else {
  279. $this->Flash->danger(__d('comment', 'Comment could not be deleted, please try again.'));
  280. }
  281. } else {
  282. $this->Flash->danger(__d('comment', 'Invalid comment, comment was not found.'));
  283. }
  284. $this->title(__d('comment', 'Delete Comment'));
  285. $this->redirect($this->referer());
  286. }
  287. /**
  288. * Permanently deletes all comments marked as "trash".
  289. *
  290. * @return void Redirects to previous page
  291. */
  292. public function emptyTrash()
  293. {
  294. $this->loadModel('Comment.Comments');
  295. $this->Comments->deleteAll(['Comments.status' => 'trash', 'Comments.table_alias' => $this->_manageTable]);
  296. $this->Flash->success(__d('comment', 'All comments in trash were successfully removed!'));
  297. $this->title(__d('comment', 'Empty Trash'));
  298. $this->redirect($this->referer());
  299. }
  300. /**
  301. * Sets a few view-variables holding counters for
  302. * each status ("pending", "approved", "spam" or "trash").
  303. *
  304. * @return void
  305. */
  306. protected function _setCounters()
  307. {
  308. $this->loadModel('Comment.Comments');
  309. $pending = $this->Comments->find()->where(['Comments.status' => 'pending', 'Comments.table_alias' => $this->_manageTable])->count();
  310. $approved = $this->Comments->find()->where(['Comments.status' => 'approved', 'Comments.table_alias' => $this->_manageTable])->count();
  311. $spam = $this->Comments->find()->where(['Comments.status' => 'spam', 'Comments.table_alias' => $this->_manageTable])->count();
  312. $trash = $this->Comments->find()->where(['Comments.status' => 'trash', 'Comments.table_alias' => $this->_manageTable])->count();
  313. $this->set('pendingCounter', $pending);
  314. $this->set('approvedCounter', $approved);
  315. $this->set('spamCounter', $spam);
  316. $this->set('trashCounter', $trash);
  317. }
  318. }