Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 
 
 

445 lines
13 KiB

  1. <?php
  2. declare(strict_types=1);
  3. namespace Grav\Plugin\FlexObjects;
  4. use Grav\Common\Config\Config;
  5. use Grav\Common\Filesystem\Folder;
  6. use Grav\Common\Grav;
  7. use Grav\Common\Page\Interfaces\PageInterface;
  8. use Grav\Common\Utils;
  9. use Grav\Framework\Flex\FlexDirectory;
  10. use Grav\Framework\Flex\FlexObject;
  11. use Grav\Framework\Flex\Interfaces\FlexCollectionInterface;
  12. use Grav\Framework\Flex\Interfaces\FlexCommonInterface;
  13. use Grav\Framework\Flex\Interfaces\FlexDirectoryInterface;
  14. use Grav\Framework\Flex\Interfaces\FlexInterface;
  15. use Grav\Framework\Flex\Interfaces\FlexObjectInterface;
  16. use Grav\Plugin\FlexObjects\Admin\AdminController;
  17. use Grav\Plugin\FlexObjects\Table\DataTable;
  18. /**
  19. * Class Flex
  20. * @package Grav\Plugin\FlexObjects
  21. */
  22. class Flex implements FlexInterface
  23. {
  24. /** @var FlexInterface */
  25. protected $flex;
  26. /** @var array */
  27. protected $adminRoutes;
  28. /** @var array */
  29. protected $adminMenu;
  30. /** @var array */
  31. protected $managed;
  32. /**
  33. * @param bool $newToOld
  34. * @return array
  35. * @internal
  36. */
  37. public static function getLegacyBlueprintMap(bool $newToOld = true): array
  38. {
  39. $map = [
  40. 'blueprints://flex-objects/pages.yaml' => 'blueprints://flex-objects/grav-pages.yaml',
  41. 'blueprints://flex-objects/user-accounts.yaml' => 'blueprints://flex-objects/grav-accounts.yaml',
  42. 'blueprints://flex-objects/user-groups.yaml' => 'blueprints://flex-objects/grav-user-groups.yaml'
  43. ];
  44. return $newToOld ? $map : array_flip($map);
  45. }
  46. /**
  47. * Flex constructor.
  48. * @param FlexInterface $flex
  49. * @param array $types
  50. */
  51. public function __construct(FlexInterface $flex, array $types)
  52. {
  53. $this->flex = $flex;
  54. $this->managed = [];
  55. $legacy = static::getLegacyBlueprintMap(false);
  56. foreach ($types as $blueprint) {
  57. // Backwards compatibility to v1.0.0-rc.3
  58. $blueprint = $legacy[$blueprint] ?? $blueprint;
  59. $type = Utils::basename((string)$blueprint, '.yaml');
  60. if ($type) {
  61. $this->managed[] = $type;
  62. }
  63. }
  64. }
  65. /**
  66. * @param string $type
  67. * @param string $blueprint
  68. * @param array $config
  69. * @return $this
  70. */
  71. public function addDirectoryType(string $type, string $blueprint, array $config = [])
  72. {
  73. $this->flex->addDirectoryType($type, $blueprint, $config);
  74. return $this;
  75. }
  76. /**
  77. * @param FlexDirectory $directory
  78. * @return $this
  79. */
  80. public function addDirectory(FlexDirectory $directory)
  81. {
  82. $this->flex->addDirectory($directory);
  83. return $this;
  84. }
  85. /**
  86. * @param string $type
  87. * @return bool
  88. */
  89. public function hasDirectory(string $type): bool
  90. {
  91. return $this->flex->hasDirectory($type);
  92. }
  93. /**
  94. * @param string[]|null $types
  95. * @param bool $keepMissing
  96. * @return array<FlexDirectoryInterface|null>
  97. */
  98. public function getDirectories(array $types = null, bool $keepMissing = false): array
  99. {
  100. return $this->flex->getDirectories($types, $keepMissing);
  101. }
  102. /**
  103. * Get directories which are not hidden in the site.
  104. *
  105. * @return array
  106. */
  107. public function getDefaultDirectories(): array
  108. {
  109. $list = $this->getDirectories();
  110. foreach ($list as $type => $directory) {
  111. if ($directory->getConfig('site.hidden', false)) {
  112. unset($list[$type]);
  113. }
  114. }
  115. return $list;
  116. }
  117. /**
  118. * @param string $type
  119. * @return FlexDirectory|null
  120. */
  121. public function getDirectory(string $type): ?FlexDirectory
  122. {
  123. return $this->flex->getDirectory($type);
  124. }
  125. /**
  126. * @param string $type
  127. * @param array|null $keys
  128. * @param string|null $keyField
  129. * @return FlexCollectionInterface|null
  130. */
  131. public function getCollection(string $type, array $keys = null, string $keyField = null): ?FlexCollectionInterface
  132. {
  133. return $this->flex->getCollection($type, $keys, $keyField);
  134. }
  135. /**
  136. * @param array $keys
  137. * @param array $options In addition to the options in getObjects(), following options can be passed:
  138. * collection_class: Class to be used to create the collection. Defaults to ObjectCollection.
  139. * @return FlexCollectionInterface
  140. * @throws \RuntimeException
  141. */
  142. public function getMixedCollection(array $keys, array $options = []): FlexCollectionInterface
  143. {
  144. return $this->flex->getMixedCollection($keys, $options);
  145. }
  146. /**
  147. * @param array $keys
  148. * @param array $options Following optional options can be passed:
  149. * types: List of allowed types.
  150. * type: Allowed type if types isn't defined, otherwise acts as default_type.
  151. * default_type: Set default type for objects given without type (only used if key_field isn't set).
  152. * keep_missing: Set to true if you want to return missing objects as null.
  153. * key_field: Key field which is used to match the objects.
  154. * @return array
  155. */
  156. public function getObjects(array $keys, array $options = []): array
  157. {
  158. return $this->flex->getObjects($keys, $options);
  159. }
  160. /**
  161. * @param string $key
  162. * @param string|null $type
  163. * @param string|null $keyField
  164. * @return FlexObjectInterface|null
  165. */
  166. public function getObject(string $key, string $type = null, string $keyField = null): ?FlexObjectInterface
  167. {
  168. return $this->flex->getObject($key, $type, $keyField);
  169. }
  170. /**
  171. * @return int
  172. */
  173. public function count(): int
  174. {
  175. return $this->flex->count();
  176. }
  177. public function isManaged(string $type): bool
  178. {
  179. return \in_array($type, $this->managed, true);
  180. }
  181. /**
  182. * @return array
  183. */
  184. public function getAll(): array
  185. {
  186. $directories = $this->getDirectories($this->managed);
  187. $all = $this->getBlueprints();
  188. /** @var FlexDirectory $directory */
  189. foreach ($all as $type => $directory) {
  190. if (!isset($directories[$type])) {
  191. $directories[$type] = $directory;
  192. }
  193. }
  194. ksort($directories);
  195. return $directories;
  196. }
  197. /**
  198. * @return array
  199. */
  200. public function getBlueprints(): array
  201. {
  202. $params = [
  203. 'pattern' => '|\.yaml|',
  204. 'value' => 'Url',
  205. 'recursive' => false,
  206. 'folders' => false
  207. ];
  208. $directories = [];
  209. $all = Folder::all('blueprints://flex-objects', $params);
  210. foreach ($all as $url) {
  211. $type = Utils::basename($url, '.yaml');
  212. $directory = new FlexDirectory($type, $url);
  213. if ($directory->getConfig('hidden') !== true) {
  214. $directories[$type] = $directory;
  215. }
  216. }
  217. // Order blueprints by title.
  218. usort($directories, static function (FlexDirectory $a, FlexDirectory $b) {
  219. return $a->getTitle() <=> $b->getTitle();
  220. });
  221. return $directories;
  222. }
  223. /**
  224. * @param string|FlexDirectory $type
  225. * @param array $options
  226. * @return DataTable
  227. */
  228. public function getDataTable($type, array $options = []): DataTable
  229. {
  230. $directory = $type instanceof FlexDirectory ? $type : $this->getDirectory($type);
  231. if (!$directory) {
  232. throw new \RuntimeException('Not Found', 404);
  233. }
  234. $collection = $options['collection'] ?? $directory->getCollection();
  235. if (isset($options['filters']) && is_array($options['filters'])) {
  236. $collection = $collection->filterBy($options['filters']);
  237. }
  238. $table = new DataTable($options);
  239. $table->setCollection($collection);
  240. return $table;
  241. }
  242. /**
  243. * @param string|object|null $type
  244. * @param array $params
  245. * @param string $extension
  246. * @return string
  247. */
  248. public function adminRoute($type = null, array $params = [], string $extension = ''): string
  249. {
  250. if (\is_object($type)) {
  251. $object = $type;
  252. if ($object instanceof FlexCommonInterface || $object instanceof FlexDirectory) {
  253. $type = $type->getFlexType();
  254. } else {
  255. return '';
  256. }
  257. } else {
  258. $object = null;
  259. }
  260. $routes = $this->getAdminRoutes();
  261. $grav = Grav::instance();
  262. /** @var Config $config */
  263. $config = $grav['config'];
  264. if (!Utils::isAdminPlugin()) {
  265. $parts = [
  266. trim($grav['base_url'], '/'),
  267. trim($config->get('plugins.admin.route'), '/')
  268. ];
  269. }
  270. if ($type && isset($routes[$type])) {
  271. if (!$routes[$type]) {
  272. // Directory has empty route.
  273. return '';
  274. }
  275. // Directory has it's own menu item.
  276. $parts[] = trim($routes[$type], '/');
  277. } else {
  278. if (empty($routes[''])) {
  279. // Default route has been disabled.
  280. return '';
  281. }
  282. // Use default route.
  283. $parts[] = trim($routes[''], '/');
  284. if ($type) {
  285. $parts[] = $type;
  286. }
  287. }
  288. // Append object key if available.
  289. if ($object instanceof FlexObject) {
  290. if ($object->exists()) {
  291. $parts[] = trim($object->getKey(), '/');
  292. } else {
  293. if ($object->hasKey()) {
  294. $parts[] = trim($object->getKey(), '/');
  295. }
  296. $params = ['' => 'add'] + $params;
  297. }
  298. }
  299. $p = [];
  300. $separator = $config->get('system.param_sep');
  301. foreach ($params as $key => $val) {
  302. $p[] = $key . $separator . $val;
  303. }
  304. $parts = array_filter($parts, static function ($val) { return $val !== ''; });
  305. $route = '/' . implode('/', $parts);
  306. $extension = $extension ? '.' . $extension : '';
  307. return $route . $extension . ($p ? '/' . implode('/', $p) : '');
  308. }
  309. public function getAdminController(): ?AdminController
  310. {
  311. $grav = Grav::instance();
  312. if (!isset($grav['admin'])) {
  313. return null;
  314. }
  315. /** @var PageInterface $page */
  316. $page = $grav['page'];
  317. $header = $page->header();
  318. $callable = $header->controller['controller']['instance'] ?? null;
  319. if (null !== $callable && \is_callable($callable)) {
  320. return $callable();
  321. }
  322. return null;
  323. }
  324. /**
  325. * @return array
  326. */
  327. public function getAdminRoutes(): array
  328. {
  329. if (null === $this->adminRoutes) {
  330. $routes = [];
  331. /** @var FlexDirectory $directory */
  332. foreach ($this->getDirectories() as $directory) {
  333. $config = $directory->getConfig('admin');
  334. if (!$directory->isEnabled() || !empty($config['disabled'])) {
  335. continue;
  336. }
  337. // Resolve route.
  338. $route = $config['router']['path']
  339. ?? $config['menu']['list']['route']
  340. ?? "/flex-objects/{$directory->getFlexType()}";
  341. $routes[$directory->getFlexType()] = $route;
  342. }
  343. $this->adminRoutes = $routes;
  344. }
  345. return $this->adminRoutes;
  346. }
  347. /**
  348. * @return array
  349. */
  350. public function getAdminMenuItems(): array
  351. {
  352. if (null === $this->adminMenu) {
  353. $routes = [];
  354. $count = 0;
  355. $directories = $this->getDirectories();
  356. /** @var FlexDirectory $directory */
  357. foreach ($directories as $directory) {
  358. $config = $directory->getConfig('admin');
  359. if (!$directory->isEnabled() || !empty($config['disabled'])) {
  360. continue;
  361. }
  362. $type = $directory->getFlexType();
  363. $items = $directory->getConfig('admin.menu') ?? [];
  364. if ($items) {
  365. foreach ($items as $view => $item) {
  366. $item += [
  367. 'route' => '/' . $type,
  368. 'title' => $directory->getTitle(),
  369. 'icon' => 'fa fa-file',
  370. 'directory' => $type
  371. ];
  372. $routes[$type] = $item;
  373. }
  374. } else {
  375. $count++;
  376. }
  377. }
  378. if ($count && !isset($routes[''])) {
  379. $routes[''] = ['route' => '/flex-objects'];
  380. }
  381. $this->adminMenu = $routes;
  382. }
  383. return $this->adminMenu;
  384. }
  385. }