PageRenderTime 58ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/modules/Collections/Controller/Admin.php

https://github.com/agentejo/cockpit
PHP | 609 lines | 413 code | 183 blank | 13 comment | 92 complexity | ad31fd609d231b90f055cb64bb235835 MD5 | raw file
Possible License(s): MIT, BSD-3-Clause, Apache-2.0, LGPL-2.1
  1. <?php
  2. /**
  3. * This file is part of the Cockpit project.
  4. *
  5. * (c) Artur Heinze - πŸ…°πŸ…ΆπŸ…΄πŸ…½πŸ†ƒπŸ…΄πŸ…ΉπŸ…Ύ, http://agentejo.com
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Collections\Controller;
  11. class Admin extends \Cockpit\AuthController {
  12. public function index() {
  13. $_collections = $this->module('collections')->getCollectionsInGroup(null, false);
  14. $collections = [];
  15. foreach ($_collections as $collection => $meta) {
  16. $meta['allowed'] = [
  17. 'delete' => $this->module('cockpit')->hasaccess('collections', 'delete'),
  18. 'create' => $this->module('cockpit')->hasaccess('collections', 'create'),
  19. 'edit' => $this->module('collections')->hasaccess($collection, 'collection_edit'),
  20. 'entries_create' => $this->module('collections')->hasaccess($collection, 'collection_create'),
  21. 'entries_delete' => $this->module('collections')->hasaccess($collection, 'entries_delete'),
  22. ];
  23. $meta['itemsCount'] = null;
  24. $collections[] = [
  25. 'name' => $collection,
  26. 'label' => isset($meta['label']) && $meta['label'] ? $meta['label'] : $collection,
  27. 'meta' => $meta
  28. ];
  29. }
  30. // sort collections
  31. usort($collections, function($a, $b) {
  32. return mb_strtolower($a['label']) <=> mb_strtolower($b['label']);
  33. });
  34. return $this->render('collections:views/index.php', compact('collections'));
  35. }
  36. public function _collections() {
  37. return $this->module('collections')->collections();
  38. }
  39. public function _find() {
  40. if ($this->param('collection') && $this->param('options')) {
  41. return $this->module('collections')->find($this->param('collection'), $this->param('options'));
  42. }
  43. return false;
  44. }
  45. public function collection($name = null) {
  46. if ($name && !$this->module('collections')->hasaccess($name, 'collection_edit')) {
  47. return $this->helper('admin')->denyRequest();
  48. }
  49. if (!$name && !$this->module('cockpit')->hasaccess('collections', 'create')) {
  50. return $this->helper('admin')->denyRequest();
  51. }
  52. $default = [
  53. 'name' => '',
  54. 'label' => '',
  55. 'color' => '',
  56. 'fields'=>[],
  57. 'acl' => new \ArrayObject,
  58. 'sortable' => false,
  59. 'sort' => [
  60. 'column' => '_created',
  61. 'dir' => -1,
  62. ],
  63. 'in_menu' => false
  64. ];
  65. $collection = $default;
  66. if ($name) {
  67. $collection = $this->module('collections')->collection($name);
  68. if (!$collection) {
  69. return false;
  70. }
  71. if (!$this->app->helper('admin')->isResourceEditableByCurrentUser($collection['_id'], $meta)) {
  72. return $this->render('cockpit:views/base/locked.php', compact('meta'));
  73. }
  74. $this->app->helper('admin')->lockResourceId($collection['_id']);
  75. $collection = array_merge($default, $collection);
  76. }
  77. // get field templates
  78. $templates = [];
  79. foreach ($this->app->helper('fs')->ls('*.php', 'collections:fields-templates') as $file) {
  80. $templates[] = include($file->getRealPath());
  81. }
  82. foreach ($this->app->module('collections')->collections() as $col) {
  83. $templates[] = $col;
  84. }
  85. // acl groups
  86. $aclgroups = [];
  87. foreach ($this->app->helper('acl')->getGroups() as $group => $superAdmin) {
  88. if (!$superAdmin) $aclgroups[] = $group;
  89. }
  90. // rules
  91. $rules = [
  92. 'create' => !$name ? "<?php\n\n" : $this->app->helper('fs')->read("#storage:collections/rules/{$name}.create.php"),
  93. 'read' => !$name ? "<?php\n\n" : $this->app->helper('fs')->read("#storage:collections/rules/{$name}.read.php"),
  94. 'update' => !$name ? "<?php\n\n" : $this->app->helper('fs')->read("#storage:collections/rules/{$name}.update.php"),
  95. 'delete' => !$name ? "<?php\n\n" : $this->app->helper('fs')->read("#storage:collections/rules/{$name}.delete.php"),
  96. ];
  97. return $this->render('collections:views/collection.php', compact('collection', 'templates', 'aclgroups', 'rules'));
  98. }
  99. public function save_collection() {
  100. $collection = $this->param('collection');
  101. $rules = $this->param('rules', null);
  102. if (!$collection) {
  103. return false;
  104. }
  105. $isUpdate = isset($collection['_id']);
  106. if (!$isUpdate && !$this->module('cockpit')->hasaccess('collections', 'create')) {
  107. return $this->helper('admin')->denyRequest();
  108. }
  109. if ($isUpdate && !$this->module('collections')->hasaccess($collection['name'], 'collection_edit')) {
  110. return $this->helper('admin')->denyRequest();
  111. }
  112. if ($isUpdate && !$this->app->helper('admin')->isResourceEditableByCurrentUser($collection['_id'])) {
  113. $this->stop(['error' => "Saving failed! Collection is locked!"], 412);
  114. }
  115. $collection = $this->module('collections')->saveCollection($collection['name'], $collection, $rules);
  116. if (!$isUpdate) {
  117. $this->app->helper('admin')->lockResourceId($collection['_id']);
  118. }
  119. return $collection;
  120. }
  121. public function entries($collection) {
  122. if (!$this->module('collections')->hasaccess($collection, 'entries_view')) {
  123. return $this->helper('admin')->denyRequest();
  124. }
  125. $collection = $this->module('collections')->collection($collection);
  126. if (!$collection) {
  127. return false;
  128. }
  129. $collection = array_merge([
  130. 'sortable' => false,
  131. 'sort' => [
  132. 'column' => '_created',
  133. 'dir' => -1,
  134. ],
  135. 'color' => '',
  136. 'icon' => '',
  137. 'description' => ''
  138. ], $collection);
  139. $context = _check_collection_rule($collection, 'read', ['options' => ['filter'=>[]]]);
  140. $this->app->helper('admin')->favicon = [
  141. 'path' => 'collections:icon.svg',
  142. 'color' => $collection['color']
  143. ];
  144. if ($context && isset($context->options['fields'])) {
  145. foreach ($collection['fields'] as &$field) {
  146. if (isset($context->options['fields'][$field['name']]) && !$context->options['fields'][$field['name']]) {
  147. $field['lst'] = false;
  148. }
  149. }
  150. }
  151. $view = 'collections:views/entries.php';
  152. if ($override = $this->app->path('#config:collections/'.$collection['name'].'/views/entries.php')) {
  153. $view = $override;
  154. }
  155. return $this->render($view, compact('collection'));
  156. }
  157. public function entry($collection, $id = null) {
  158. if ($id && !$this->module('collections')->hasaccess($collection, 'entries_view')) {
  159. return $this->helper('admin')->denyRequest();
  160. }
  161. if (!$id && !$this->module('collections')->hasaccess($collection, 'entries_create')) {
  162. return $this->helper('admin')->denyRequest();
  163. }
  164. $collection = $this->module('collections')->collection($collection);
  165. $entry = new \ArrayObject([]);
  166. $excludeFields = [];
  167. if (!$collection) {
  168. return false;
  169. }
  170. $collection = array_merge([
  171. 'sortable' => false,
  172. 'sort' => [
  173. 'column' => '_created',
  174. 'dir' => -1,
  175. ],
  176. 'color' => '',
  177. 'icon' => '',
  178. 'description' => ''
  179. ], $collection);
  180. $this->app->helper('admin')->favicon = [
  181. 'path' => 'collections:icon.svg',
  182. 'color' => $collection['color']
  183. ];
  184. if ($id) {
  185. $entry = $this->module('collections')->findOne($collection['name'], ['_id' => $id]);
  186. //$entry = $this->app->storage->findOne("collections/{$collection['_id']}", ['_id' => $id]);
  187. if (!$entry) {
  188. return cockpit()->helper('admin')->denyRequest();
  189. }
  190. if (!$this->app->helper('admin')->isResourceEditableByCurrentUser($id, $meta)) {
  191. return $this->render('collections:views/locked.php', compact('meta', 'collection', 'entry'));
  192. }
  193. $this->app->helper('admin')->lockResourceId($id);
  194. }
  195. $context = _check_collection_rule($collection, 'read', ['options' => ['filter'=>[]]]);
  196. if ($context && isset($context->options['fields'])) {
  197. foreach ($context->options['fields'] as $field => $include) {
  198. if(!$include) $excludeFields[] = $field;
  199. }
  200. }
  201. $view = 'collections:views/entry.php';
  202. if ($override = $this->app->path('#config:collections/'.$collection['name'].'/views/entry.php')) {
  203. $view = $override;
  204. }
  205. return $this->render($view, compact('collection', 'entry', 'excludeFields'));
  206. }
  207. public function save_entry($collection) {
  208. $collection = $this->module('collections')->collection($collection);
  209. if (!$collection) {
  210. return false;
  211. }
  212. $entry = $this->param('entry', false);
  213. if (!$entry) {
  214. return false;
  215. }
  216. if (!isset($entry['_id']) && !$this->module('collections')->hasaccess($collection['name'], 'entries_create')) {
  217. return $this->helper('admin')->denyRequest();
  218. }
  219. if (isset($entry['_id']) && !$this->module('collections')->hasaccess($collection['name'], 'entries_edit')) {
  220. return $this->helper('admin')->denyRequest();
  221. }
  222. $entry['_mby'] = $this->module('cockpit')->getUser('_id');
  223. if (isset($entry['_id'])) {
  224. if (!$this->app->helper('admin')->isResourceEditableByCurrentUser($entry['_id'])) {
  225. $this->stop(['error' => "Saving failed! Entry is locked!"], 412);
  226. }
  227. $_entry = $this->module('collections')->findOne($collection['name'], ['_id' => $entry['_id']]);
  228. $revision = !(json_encode($_entry) == json_encode($entry));
  229. } else {
  230. $entry['_by'] = $entry['_mby'];
  231. $revision = true;
  232. if ($collection['sortable']) {
  233. $entry['_o'] = $this->app->storage->count("collections/{$collection['_id']}", ['_pid' => ['$exists' => false]]);
  234. }
  235. }
  236. $entry = $this->module('collections')->save($collection['name'], $entry, ['revision' => $revision]);
  237. $this->app->helper('admin')->lockResourceId($entry['_id']);
  238. return $entry;
  239. }
  240. public function delete_entries($collection) {
  241. \session_write_close();
  242. $collection = $this->module('collections')->collection($collection);
  243. if (!$collection) {
  244. return false;
  245. }
  246. if (!$this->module('collections')->hasaccess($collection['name'], 'entries_delete')) {
  247. return $this->helper('admin')->denyRequest();
  248. }
  249. $filter = $this->param('filter', false);
  250. if (!$filter) {
  251. return false;
  252. }
  253. $items = $this->module('collections')->find($collection['name'], ['filter' => $filter]);
  254. if (count($items)) {
  255. $trashItems = [];
  256. $time = time();
  257. $by = $this->module('cockpit')->getUser('_id');
  258. foreach ($items as $item) {
  259. $trashItems[] = [
  260. 'collection' => $collection['name'],
  261. 'data' => $item,
  262. '_by' => $by,
  263. '_created' => $time
  264. ];
  265. }
  266. $this->app->storage->getCollection('collections/_trash')->insertMany($trashItems);
  267. }
  268. $this->module('collections')->remove($collection['name'], $filter);
  269. return true;
  270. }
  271. public function update_order($collection) {
  272. \session_write_close();
  273. $collection = $this->module('collections')->collection($collection);
  274. $entries = $this->param('entries');
  275. if (!$collection) return false;
  276. if (!$entries) return false;
  277. $_collectionId = $collection['_id'];
  278. if (is_array($entries) && count($entries)) {
  279. foreach($entries as $entry) {
  280. $this->app->storage->save("collections/{$_collectionId}", $entry);
  281. }
  282. }
  283. return $entries;
  284. }
  285. public function export($collection) {
  286. \session_write_close();
  287. if (!$this->app->module("cockpit")->hasaccess('collections', 'manage')) {
  288. return false;
  289. }
  290. $collection = $this->module('collections')->collection($collection);
  291. if (!$collection) return false;
  292. if (!$this->module('collections')->hasaccess($collection['name'], 'entries_view')) {
  293. return $this->helper('admin')->denyRequest();
  294. }
  295. $entries = $this->module('collections')->find($collection['name']);
  296. return json_encode($entries, JSON_PRETTY_PRINT);
  297. }
  298. public function tree() {
  299. \session_write_close();
  300. $collection = $this->app->param('collection');
  301. if (!$collection) return false;
  302. $items = $this->app->module('collections')->find($collection);
  303. if (count($items)) {
  304. $items = $this->helper('utils')->buildTree($items, [
  305. 'parent_id_column_name' => '_pid',
  306. 'children_key_name' => 'children',
  307. 'id_column_name' => '_id',
  308. 'sort_column_name' => '_o'
  309. ]);
  310. }
  311. return $items;
  312. }
  313. public function find() {
  314. \session_write_close();
  315. $collection = $this->app->param('collection');
  316. $options = $this->app->param('options');
  317. if (!$collection) return false;
  318. $collection = $this->app->module('collections')->collection($collection);
  319. if (isset($options['filter']) && is_string($options['filter'])) {
  320. $filter = null;
  321. if (\preg_match('/^\{(.*)\}$/', $options['filter'])) {
  322. try {
  323. $filter = json5_decode($options['filter'], true);
  324. } catch (\Exception $e) {}
  325. }
  326. if (!$filter) {
  327. $filter = $this->_filter($options['filter'], $collection, $options['lang'] ?? null);
  328. }
  329. $options['filter'] = $filter;
  330. }
  331. $this->app->trigger("collections.admin.find.before.{$collection['name']}", [&$options]);
  332. $entries = $this->app->module('collections')->find($collection['name'], $options);
  333. $this->app->trigger("collections.admin.find.after.{$collection['name']}", [&$entries, $options]);
  334. $count = $this->app->module('collections')->count($collection['name'], isset($options['filter']) ? $options['filter'] : []);
  335. $pages = isset($options['limit']) ? ceil($count / $options['limit']) : 1;
  336. $page = 1;
  337. if ($pages > 1 && isset($options['skip'])) {
  338. $page = ceil($options['skip'] / $options['limit']) + 1;
  339. }
  340. return compact('entries', 'count', 'pages', 'page');
  341. }
  342. public function revisions($collection, $id) {
  343. if (!$this->module('collections')->hasaccess($collection, 'entries_edit')) {
  344. return $this->helper('admin')->denyRequest();
  345. }
  346. $collection = $this->module('collections')->collection($collection);
  347. if (!$collection) {
  348. return false;
  349. }
  350. $entry = $this->module('collections')->findOne($collection['name'], ['_id' => $id]);
  351. if (!$entry) {
  352. return false;
  353. }
  354. $revisions = $this->app->helper('revisions')->getList($id);
  355. return $this->render('collections:views/revisions.php', compact('collection', 'entry', 'revisions'));
  356. }
  357. protected function _filter($filter, $collection, $lang = null) {
  358. if ($this->app->storage->type == 'mongolite') {
  359. return $this->_filterLight($filter, $collection, $lang);
  360. }
  361. if ($this->app->storage->type == 'mongodb') {
  362. return $this->_filterMongo($filter, $collection, $lang);
  363. }
  364. return null;
  365. }
  366. protected function _filterLight($filter, $collection, $lang) {
  367. $allowedtypes = ['text','longtext','boolean','select','html','wysiwyg','markdown','code'];
  368. $criterias = [];
  369. $_filter = null;
  370. foreach ($collection['fields'] as $field) {
  371. $name = $field['name'];
  372. if ($lang && $field['localize']) {
  373. $name = "{$name}_{$lang}";
  374. }
  375. if ($field['type'] != 'boolean' && in_array($field['type'], $allowedtypes)) {
  376. $criteria = [];
  377. $criteria[$name] = ['$regex' => $filter];
  378. $criterias[] = $criteria;
  379. }
  380. if ($field['type']=='collectionlink') {
  381. $criteria = [];
  382. $criteria[$name.'.display'] = ['$regex' => $filter];
  383. $criterias[] = $criteria;
  384. }
  385. if ($field['type']=='location') {
  386. $criteria = [];
  387. $criteria[$name.'.address'] = ['$regex' => $filter];
  388. $criterias[] = $criteria;
  389. }
  390. }
  391. if (count($criterias)) {
  392. $_filter = ['$or' => $criterias];
  393. }
  394. return $_filter;
  395. }
  396. protected function _filterMongo($filter, $collection, $lang) {
  397. $allowedtypes = ['text','longtext','boolean','select','html','wysiwyg','markdown','code'];
  398. $criterias = [];
  399. $_filter = null;
  400. foreach ($collection['fields'] as $field) {
  401. $name = $field['name'];
  402. if ($lang && $field['localize']) {
  403. $name = "{$name}_{$lang}";
  404. }
  405. if ($field['type'] != 'boolean' && in_array($field['type'], $allowedtypes)) {
  406. $criteria = [];
  407. $criteria[$name] = ['$regex' => $filter, '$options' => 'i'];
  408. $criterias[] = $criteria;
  409. }
  410. if ($field['type']=='collectionlink') {
  411. $criteria = [];
  412. $criteria[$name.'.display'] = ['$regex' => $filter, '$options' => 'i'];
  413. $criterias[] = $criteria;
  414. }
  415. if ($field['type']=='location') {
  416. $criteria = [];
  417. $criteria[$name.'.address'] = ['$regex' => $filter, '$options' => 'i'];
  418. $criterias[] = $criteria;
  419. }
  420. }
  421. if (count($criterias)) {
  422. $_filter = ['$or' => $criterias];
  423. }
  424. return $_filter;
  425. }
  426. }