UserIndex.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @package Grav\Common\Flex
  5. *
  6. * @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
  7. * @license MIT License; see LICENSE file for details.
  8. */
  9. namespace Grav\Common\Flex\Types\Users;
  10. use Grav\Common\Debugger;
  11. use Grav\Common\File\CompiledYamlFile;
  12. use Grav\Common\Flex\FlexIndex;
  13. use Grav\Common\Grav;
  14. use Grav\Common\User\Interfaces\UserCollectionInterface;
  15. use Grav\Common\User\Interfaces\UserInterface;
  16. use Grav\Framework\Flex\Interfaces\FlexStorageInterface;
  17. use Monolog\Logger;
  18. use function count;
  19. use function is_string;
  20. /**
  21. * Class UserIndex
  22. * @package Grav\Common\Flex\Types\Users
  23. *
  24. * @extends FlexIndex<UserObject,UserCollection>
  25. */
  26. class UserIndex extends FlexIndex implements UserCollectionInterface
  27. {
  28. public const VERSION = parent::VERSION . '.2';
  29. /**
  30. * @param FlexStorageInterface $storage
  31. * @return array
  32. */
  33. public static function loadEntriesFromStorage(FlexStorageInterface $storage): array
  34. {
  35. // Load saved index.
  36. $index = static::loadIndex($storage);
  37. $version = $index['version'] ?? 0;
  38. $force = static::VERSION !== $version;
  39. // TODO: Following check flex index to be out of sync after some saves, disabled until better solution is found.
  40. //$timestamp = $index['timestamp'] ?? 0;
  41. //if (!$force && $timestamp && $timestamp > time() - 1) {
  42. // return $index['index'];
  43. //}
  44. // Load up-to-date index.
  45. $entries = parent::loadEntriesFromStorage($storage);
  46. return static::updateIndexFile($storage, $index['index'], $entries, ['force_update' => $force]);
  47. }
  48. /**
  49. * @param array $meta
  50. * @param array $data
  51. * @param FlexStorageInterface $storage
  52. * @return void
  53. */
  54. public static function updateObjectMeta(array &$meta, array $data, FlexStorageInterface $storage): void
  55. {
  56. // Username can also be number and stored as such.
  57. $key = (string)($data['username'] ?? $meta['key'] ?? $meta['storage_key']);
  58. $meta['key'] = static::filterUsername($key, $storage);
  59. $meta['email'] = isset($data['email']) ? mb_strtolower($data['email']) : null;
  60. }
  61. /**
  62. * Load user account.
  63. *
  64. * Always creates user object. To check if user exists, use $this->exists().
  65. *
  66. * @param string $username
  67. * @return UserObject
  68. */
  69. public function load($username): UserInterface
  70. {
  71. $username = (string)$username;
  72. if ($username !== '') {
  73. $key = static::filterUsername($username, $this->getFlexDirectory()->getStorage());
  74. $user = $this->get($key);
  75. if ($user) {
  76. return $user;
  77. }
  78. } else {
  79. $key = '';
  80. }
  81. $directory = $this->getFlexDirectory();
  82. /** @var UserObject $object */
  83. $object = $directory->createObject(
  84. [
  85. 'username' => $username,
  86. 'state' => 'enabled'
  87. ],
  88. $key
  89. );
  90. return $object;
  91. }
  92. /**
  93. * Delete user account.
  94. *
  95. * @param string $username
  96. * @return bool True if user account was found and was deleted.
  97. */
  98. public function delete($username): bool
  99. {
  100. $user = $this->load($username);
  101. $exists = $user->exists();
  102. if ($exists) {
  103. $user->delete();
  104. }
  105. return $exists;
  106. }
  107. /**
  108. * Find a user by username, email, etc
  109. *
  110. * @param string $query the query to search for
  111. * @param array $fields the fields to search
  112. * @return UserObject
  113. */
  114. public function find($query, $fields = ['username', 'email']): UserInterface
  115. {
  116. if (is_string($query) && $query !== '') {
  117. foreach ((array)$fields as $field) {
  118. if ($field === 'key') {
  119. $user = $this->get($query);
  120. } elseif ($field === 'storage_key') {
  121. $user = $this->withKeyField('storage_key')->get($query);
  122. } elseif ($field === 'flex_key') {
  123. $user = $this->withKeyField('flex_key')->get($query);
  124. } elseif ($field === 'email') {
  125. $email = mb_strtolower($query);
  126. $user = $this->withKeyField('email')->get($email);
  127. } elseif ($field === 'username') {
  128. $username = static::filterUsername($query, $this->getFlexDirectory()->getStorage());
  129. $user = $this->get($username);
  130. } else {
  131. $user = $this->__call('find', [$query, $field]);
  132. }
  133. if ($user) {
  134. return $user;
  135. }
  136. }
  137. }
  138. return $this->load('');
  139. }
  140. /**
  141. * @param string $key
  142. * @param FlexStorageInterface $storage
  143. * @return string
  144. */
  145. protected static function filterUsername(string $key, FlexStorageInterface $storage): string
  146. {
  147. return method_exists($storage, 'normalizeKey') ? $storage->normalizeKey($key) : $key;
  148. }
  149. /**
  150. * @param FlexStorageInterface $storage
  151. * @return CompiledYamlFile|null
  152. */
  153. protected static function getIndexFile(FlexStorageInterface $storage)
  154. {
  155. // Load saved index file.
  156. $grav = Grav::instance();
  157. $locator = $grav['locator'];
  158. $filename = $locator->findResource('user-data://flex/indexes/accounts.yaml', true, true);
  159. return CompiledYamlFile::instance($filename);
  160. }
  161. /**
  162. * @param array $entries
  163. * @param array $added
  164. * @param array $updated
  165. * @param array $removed
  166. */
  167. protected static function onChanges(array $entries, array $added, array $updated, array $removed): void
  168. {
  169. $message = sprintf('Flex: User index updated, %d objects (%d added, %d updated, %d removed).', count($entries), count($added), count($updated), count($removed));
  170. $grav = Grav::instance();
  171. /** @var Logger $logger */
  172. $logger = $grav['log'];
  173. $logger->addDebug($message);
  174. /** @var Debugger $debugger */
  175. $debugger = $grav['debugger'];
  176. $debugger->addMessage($message, 'debug');
  177. }
  178. }