SproutCMS

This is the code documentation for the SproutCMS project

source of /sprout/Controllers/Admin/ActionLogAdminController.php

  1. <?php
  2. /*
  3.  * Copyright (C) 2017 Karmabunny Pty Ltd.
  4.  *
  5.  * This file is a part of SproutCMS.
  6.  *
  7.  * SproutCMS is free software: you can redistribute it and/or modify it under the terms
  8.  * of the GNU General Public License as published by the Free Software Foundation, either
  9.  * version 2 of the License, or (at your option) any later version.
  10.  *
  11.  * For more information, visit <http://getsproutcms.com>.
  12.  */
  13.  
  14. namespace Sprout\Controllers\Admin;
  15.  
  16. use DateInterval;
  17. use DateTime;
  18.  
  19. use karmabunny\pdb\Exceptions\RowMissingException;
  20. use Sprout\Helpers\ColModifierActionLogData;
  21. use Sprout\Helpers\ColModifierBinary;
  22. use Sprout\Helpers\ColModifierDate;
  23. use Sprout\Helpers\ColModifierHexIP;
  24. use Sprout\Helpers\Cron;
  25. use Sprout\Helpers\Enc;
  26. use Sprout\Helpers\File;
  27. use Sprout\Helpers\Itemlist;
  28. use Sprout\Helpers\Pdb;
  29. use Sprout\Helpers\RefineBar;
  30. use Sprout\Helpers\RefineWidgetSelect;
  31. use Sprout\Helpers\RefineWidgetTextbox;
  32.  
  33.  
  34. /**
  35.  * Handles admin processing for the action log, which is a record of changes to database content
  36.  */
  37. class ActionLogAdminController extends ManagedAdminController
  38. {
  39. protected $controller_name = 'action_log';
  40. protected $friendly_name = 'Activity log';
  41. protected $table_name = 'history_items';
  42. protected $action_log = false;
  43. protected $main_add = false;
  44. protected $main_delete = false;
  45. protected $main_where = ['item.parent_id = 0'];
  46.  
  47.  
  48. /**
  49.   * Constructor
  50.   **/
  51. public function __construct()
  52. {
  53. parent::__construct();
  54.  
  55. $this->main_order = 'item.id DESC';
  56.  
  57. $this->main_columns = array(
  58. 'Type' => 'type',
  59. 'Date' => array(new ColModifierDate('d/m/Y H:i:s'), 'date_added'),
  60. 'Editor' => 'modified_editor',
  61. 'Table' => 'record_table',
  62. 'Record' => [new ColModifierActionLogData(), 'id'],
  63. );
  64.  
  65. $this->refine_bar = new RefineBar();
  66. $types = Pdb::extractEnumArr($this->table_name, 'type');
  67. $this->refine_bar->addWidget(new RefineWidgetSelect('type', 'Type', $types));
  68. $this->refine_bar->addWidget(new RefineWidgetTextbox('record_table', 'Table'));
  69. $this->refine_bar->addWidget(new RefineWidgetTextbox('record_id', 'Record ID'));
  70. $this->refine_bar->addWidget(new RefineWidgetTextbox('modified_editor', 'Editor'));
  71. }
  72.  
  73.  
  74. public function _addSave(&$item_id) { return false; }
  75. public function _isEditSaved($item_id) { return false; }
  76. public function _editSave($item_id) { return false; }
  77. public function _getAddForm() { return false; }
  78.  
  79.  
  80. /**
  81.   * List of tools
  82.   */
  83. public function _getTools()
  84. {
  85. $tools = parent::_getTools();
  86. unset($tools['import']);
  87.  
  88. $tools[] = '<li><a href="SITE/admin/extra/action_log/login_attempts">View login attempts</a></li>';
  89.  
  90. return $tools;
  91. }
  92.  
  93.  
  94. /**
  95.   * Return the fields to show in the sidebar when adding or editing a record.
  96.   * These fields are shown under a heading of "Visibility"
  97.   *
  98.   * Key is the field name, value is the field label
  99.   *
  100.   * @return array
  101.   */
  102. public function _getVisibilityFields()
  103. {
  104. return [];
  105. }
  106.  
  107.  
  108. /**
  109.   * Mods
  110.   **/
  111. protected function _editPreRender($view, $item_id)
  112. {
  113. // Previous
  114. $q = "SELECT id
  115. FROM ~history_items
  116. WHERE record_table = ? AND id < ?
  117. ORDER BY id DESC
  118. LIMIT 1";
  119. try {
  120. $row = Pdb::q($q, [$view->data['record_table'], $item_id], 'row');
  121. $view->prev_id = $row['id'];
  122. } catch (RowMissingException $ex) {
  123. // No problem
  124. }
  125.  
  126. // Next
  127. $q = "SELECT id
  128. FROM ~history_items
  129. WHERE record_table = ? AND id > ?
  130. ORDER BY id ASC
  131. LIMIT 1";
  132. try {
  133. $row = Pdb::q($q, [$view->data['record_table'], $item_id], 'row');
  134. $view->next_id = $row['id'];
  135. } catch (RowMissingException $ex) {
  136. // No problem
  137. }
  138.  
  139. $ctlr_class = $view->data['controller'];
  140. if (class_exists($ctlr_class)) {
  141. $ctlr = new $ctlr_class();
  142. $view->controller = $ctlr->getControllerName();
  143. }
  144. }
  145.  
  146.  
  147. /**
  148.   * Shows a list of tables
  149.   **/
  150. public function _getNavigation()
  151. {
  152.  
  153.  
  154. $q = "SELECT record_table, COUNT(id) AS num FROM ~history_items GROUP BY record_table ORDER BY record_table";
  155. $res = Pdb::q($q, [], 'pdo');
  156.  
  157. $ret = '<ul class="list-style-1">';
  158. foreach ($res as $row) {
  159. $nice = ucfirst(str_replace('_', ' ', $row['record_table']));
  160. $ret .= "<li class=\"action-log\"><a href=\"admin/contents/action_log?record_table=";
  161. $ret .= Enc::html($row['record_table']) . "\">" . Enc::html($nice) . " ({$row['num']})</a></li>";
  162. }
  163. $ret .= '</ul>';
  164.  
  165. $res->closeCursor();
  166.  
  167. return $ret;
  168. }
  169.  
  170.  
  171. /**
  172.   * List if recent logins
  173.   */
  174. public function _extraLoginAttempts()
  175. {
  176. $q = "SELECT * FROM ~login_attempts ORDER BY id DESC LIMIT 25";
  177. $res = Pdb::query($q, [], 'arr');
  178.  
  179. $itemlist = new Itemlist();
  180. $itemlist->main_columns = array(
  181. 'Username' => 'username',
  182. 'Success' => array(new ColModifierBinary(), 'success'),
  183. 'Date' => array(new ColModifierDate(), 'date_added'),
  184. 'IP Address' => array(new ColModifierHexIP(), 'ip'),
  185. );
  186. $itemlist->items = $res;
  187.  
  188. return array(
  189. 'title' => 'Login Attempts',
  190. 'content' => $itemlist->render()
  191. );
  192. }
  193.  
  194.  
  195. public function cronCleanup()
  196. {
  197. Cron::start('Clean up action log');
  198.  
  199. $date = new DateTime();
  200. $date->sub(new DateInterval('P3M'));
  201. $date = $date->format('Y-m-d H:i:s');
  202.  
  203. $q = "DELETE FROM ~history_items
  204. WHERE date_modified <= ? AND
  205. (record_table != 'files' OR type != 'Delete' OR restored_date > '1')";
  206. $affected = Pdb::q($q, [$date], 'count');
  207. Cron::message("{$affected} ordinary record(s) deleted");
  208.  
  209. $q = "DELETE FROM ~history_items WHERE id = ?";
  210. $del_st = Pdb::q($q, [], 'prep');
  211.  
  212. $num_deleted = 0;
  213. $q = "SELECT id, type, restored_date, data
  214. FROM ~history_items
  215. WHERE date_modified <= ? AND record_table = 'files'";
  216. $res = Pdb::q($q, [$date], 'pdo');
  217. foreach ($res as $row) {
  218. $data = json_decode($row['data'], true);
  219. if (File::delete($data['filename'])) {
  220. Cron::message('Deleted file ' . $data['filename']);
  221. } else {
  222. Cron::message('Failed to delete file ' . $data['filename']);
  223. }
  224. Pdb::execute($del_st, [$row['id']], 'null');
  225. ++$num_deleted;
  226. }
  227. $res->closeCursor();
  228. Cron::message("{$num_deleted} stale file reference(s) deleted");
  229.  
  230. Cron::success();
  231. }
  232. }
  233.