SproutCMS

This is the code documentation for the SproutCMS project

source of /sprout/Controllers/Admin/PerRecordPermissionAdminController.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 Exception;
  17.  
  18. use Sprout\Helpers\Admin;
  19. use Sprout\Helpers\AdminAuth;
  20. use Sprout\Helpers\AdminError;
  21. use Sprout\Helpers\AdminPerms;
  22. use Sprout\Helpers\Csrf;
  23. use Sprout\Helpers\Enc;
  24. use Sprout\Helpers\Form;
  25. use Sprout\Helpers\Inflector;
  26. use Sprout\Helpers\Notification;
  27. use Sprout\Helpers\Pdb;
  28. use Sprout\Helpers\PerRecordPerms;
  29. use Sprout\Helpers\Register;
  30. use Sprout\Helpers\Url;
  31. use Sprout\Helpers\View;
  32.  
  33.  
  34. /**
  35.  * Manages which controllers have per-record permissions enabled
  36.  */
  37. class PerRecordPermissionAdminController extends NoRecordsAdminController
  38. {
  39. protected $controller_name = 'per_record_permission';
  40. protected $friendly_name = 'Per-record permissions';
  41. protected $table_name = 'per_record_controllers';
  42.  
  43.  
  44. /**
  45.   * Gets the list of controllers which can have per-category permissions set
  46.   *
  47.   * @return array [shorthand name => human-readable label]
  48.   */
  49. protected function getControllerList()
  50. {
  51. $controllers = Register::getAdminControllers();
  52.  
  53. unset($controllers['page']); // already has tree-based permissions system
  54. unset($controllers['file']); // quite complex to implement with file selectors
  55.  
  56. // These are tied to forms and are saved in a separate table for each form.
  57. // In any case, the permissions really apply to the forms themselves; there's no obvious
  58. // case for restricting access to individual form submissions
  59. unset($controllers['form_submission']);
  60.  
  61. foreach ($controllers as $shorthand => $ctlr_class) {
  62. $reflect = new \ReflectionClass($ctlr_class);
  63. $props = $reflect->getDefaultProperties();
  64.  
  65. // Ignore category controllers
  66. if ($reflect->isSubclassOf('Sprout\\Controllers\\Admin\\CategoryAdminController')) {
  67. unset($controllers[$shorthand]);
  68. continue;
  69. }
  70.  
  71. // Ignore controllers without records
  72. if ($reflect->isSubclassOf('Sprout\\Controllers\\Admin\\NoRecordsAdminController')) {
  73. unset($controllers[$shorthand]);
  74. continue;
  75. }
  76.  
  77. $controllers[$shorthand] = $props['friendly_name'];
  78. }
  79. asort($controllers);
  80.  
  81. return $controllers;
  82. }
  83.  
  84.  
  85. /**
  86.   * Return the navigation for this controller
  87.   *
  88.   * @return string HTML
  89.   */
  90. public function _getNavigation()
  91. {
  92. $out = "<ul class=\"list-style-1\">";
  93. $out .= "<li><a href=\"admin/contents/{$this->controller_name}\">Configure tabs</a></li>";
  94. $out .= "<li><a href=\"admin/extra/per_record_permission/reset\">Reset a single tab</a></li>";
  95. $out .= "</ul>";
  96.  
  97. return $out;
  98. }
  99.  
  100.  
  101. /**
  102.   * Generate a form where operators can specify which controllers should have per-record permissions enabled
  103.   *
  104.   * This is instead of the normal behaviour: HTML which represents a list of records for a controller
  105.   *
  106.   * @return array Contains elements 'title' and 'content' as per {@see ManagedAdminController::_getContents}
  107.   */
  108. public function _getContents()
  109. {
  110. if (!AdminPerms::canAccess('access_operators')) return new AdminError('Access denied');
  111.  
  112. $controllers = $this->getControllerList();
  113.  
  114. $view = new View('sprout/admin/per_record_perms');
  115. $view->enabled = Pdb::q('SELECT name FROM ~per_record_controllers WHERE active = 1', [], 'col');
  116. $view->controllers = $controllers;
  117.  
  118. return [
  119. 'title' => Enc::html($this->friendly_name),
  120. 'content' => $view->render(),
  121. ];
  122. }
  123.  
  124.  
  125. /**
  126.   * Save which controllers should have per-record permissions enabled
  127.   */
  128. public function save()
  129. {
  130. Csrf::checkOrDie();
  131. if (!AdminPerms::canAccess('access_operators')) return new AdminError('Access denied');
  132.  
  133. $controllers = @$_POST['controllers'];
  134. if (!is_array($controllers)) $controllers = [];
  135.  
  136. if (count($controllers) == 0) {
  137. Pdb::update('per_record_controllers', ['active' => 0], [1]);
  138. } else {
  139. Pdb::transact();
  140.  
  141. $q = "UPDATE ~per_record_controllers SET active = 0 WHERE id = ?";
  142. $deactivate = Pdb::prepare($q);
  143.  
  144. $q = "UPDATE ~per_record_controllers SET active = 1 WHERE id = ?";
  145. $activate = Pdb::prepare($q);
  146.  
  147. $q = "INSERT INTO ~per_record_controllers (name, active) VALUES (?, 1)";
  148. $insert = Pdb::prepare($q);
  149.  
  150. $q = "SELECT id, name, active
  151. FROM ~per_record_controllers";
  152. $res = Pdb::q($q, [], 'pdo');
  153. $extant = [];
  154. foreach ($res as $row) {
  155. if (in_array($row['name'], $controllers)) {
  156. if (!$row['active']) {
  157. Pdb::execute($activate, [$row['id']], 'null');
  158. $this->logEdit('per_record_controllers', $row['id'], $row);
  159. }
  160. } else {
  161. if ($row['active']) {
  162. Pdb::execute($deactivate, [$row['id']], 'null');
  163. $this->logEdit('per_record_controllers', $row['id'], $row);
  164. }
  165. }
  166. $extant[$row['name']] = $row['id'];
  167. }
  168. $res->closeCursor();
  169.  
  170. foreach ($controllers as $controller) {
  171. if (isset($extant[$controller])) continue;
  172.  
  173. Pdb::execute($insert, [$controller], 'null');
  174. $id = Pdb::getLastInsertId();
  175. $this->logAdd('per_record_controllers', $id);
  176. }
  177.  
  178. Pdb::commit();
  179. }
  180.  
  181. Notification::confirm('Configuration updated');
  182.  
  183. Url::redirect('admin/contents/' . $this->controller_name);
  184. }
  185.  
  186.  
  187. public function _extraReset()
  188. {
  189. $controllers = $this->getControllerList();
  190.  
  191. $q = "SELECT name FROM ~per_record_controllers WHERE active = 1 ORDER BY name";
  192. $active_controllers = Pdb::q($q, [], 'col');
  193.  
  194. foreach ($controllers as $controller => $label) {
  195. if (!in_array($controller, $active_controllers)) {
  196. unset($controllers[$controller]);
  197. }
  198. }
  199.  
  200. $out = '<form method="post" action="admin/call/' . $this->controller_name . '/resetSave">';
  201. $out .= Csrf::token();
  202.  
  203. Form::nextFieldDetails('Tab to reset all per-record permissions on', true);
  204. $out .= Form::dropdown('controller', [], $controllers);
  205.  
  206. $checked_cats = Form::getData('_prm_categories');
  207.  
  208. $all_cats = AdminAuth::getAllCategories();
  209. unset($all_cats[AdminAuth::getPrimaryCategoryId()]);
  210.  
  211. Form::nextFieldDetails('Allow changes by', false);
  212. $allow_cats = Form::checkboxSet('_prm_categories', [], $all_cats);
  213.  
  214. // Hack in 'all operators' option
  215. $all = '<div class="field-element__input-set">';
  216. $all .= '<div class="fieldset-input"><input type="checkbox" value="1" name="_prm_all_cats" id="_prm_all"';
  217. if ($checked_cats == ['*']) $all .= ' checked';
  218. $all .= '><label for="_prm_all">All operators</label></div>';
  219. $allow_cats = str_replace('<div class="field-element__input-set">', $all, $allow_cats);
  220.  
  221. $out .= $allow_cats;
  222.  
  223. $out .= '<p><button type="submit" class="button">Reset permissions</button></p>';
  224.  
  225. $out .= '</form>';
  226.  
  227. return [
  228. 'title' => Enc::html($this->friendly_name),
  229. 'content' => $out,
  230. ];
  231. }
  232.  
  233.  
  234. public function resetSave()
  235. {
  236. Csrf::checkOrDie();
  237. $url = 'admin/extra/' . $this->controller_name . '/reset';
  238.  
  239. $errs = false;
  240. if (empty($_POST['controller'])) {
  241. $errs = true;
  242. Notification::error('No tab specified');
  243. Url::redirect($url);
  244. }
  245.  
  246. // Determine actual class name from Register
  247. try {
  248. $ctlr = Admin::getController($_POST['controller']);
  249. } catch (Exception $ex) {
  250. Notification::error('Invalid tab specified');
  251. Url::redirect($url);
  252. }
  253.  
  254. $table = $ctlr->getTableName();
  255. $q = "SELECT id FROM ~{$table}";
  256. $res = Pdb::q($q, [], 'col');
  257.  
  258. Pdb::transact();
  259.  
  260. foreach ($res as $id) {
  261. PerRecordPerms::save($ctlr, $id);
  262. }
  263.  
  264. Pdb::commit();
  265.  
  266. $msg = 'Permissions updated for ' . Inflector::numPlural(count($res), 'record');
  267. $msg .= ' for ' . $ctlr->getFriendlyName();
  268. Notification::confirm($msg);
  269.  
  270. Url::redirect($url);
  271. }
  272.  
  273. }
  274.