4
0

Share.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * Manage the sharing options in FreshRSS.
  5. */
  6. class FreshRSS_Share {
  7. /**
  8. * The list of available sharing options.
  9. * @var array<string,FreshRSS_Share>
  10. */
  11. private static array $list_sharing = [];
  12. /**
  13. * Register a new sharing option.
  14. * @param array{type:string,url:string,transform?:array<callable>|array<string,array<callable>>,field?:string,help?:string,form?:'simple'|'advanced',
  15. * method?:'GET'|'POST',HTMLtag?:'button',deprecated?:bool} $share_options is an array defining the share option.
  16. */
  17. public static function register(array $share_options): void {
  18. $type = $share_options['type'];
  19. if (isset(self::$list_sharing[$type])) {
  20. return;
  21. }
  22. self::$list_sharing[$type] = new FreshRSS_Share(
  23. $type,
  24. $share_options['url'],
  25. $share_options['transform'] ?? [],
  26. $share_options['form'] ?? 'simple',
  27. $share_options['help'] ?? '',
  28. $share_options['method'] ?? 'GET',
  29. $share_options['field'] ?? null,
  30. $share_options['HTMLtag'] ?? null,
  31. $share_options['deprecated'] ?? false
  32. );
  33. }
  34. /**
  35. * Register sharing options in a file.
  36. * @param string $filename the name of the file to load.
  37. */
  38. public static function load(string $filename): void {
  39. $shares_from_file = @include($filename);
  40. if (!is_array($shares_from_file)) {
  41. $shares_from_file = [];
  42. }
  43. foreach ($shares_from_file as $share_type => $share_options) {
  44. if (!is_array($share_options)) {
  45. continue;
  46. }
  47. $share_options['type'] = $share_type;
  48. /** @var array{type:string,url:string,transform?:array<callable>|array<string,array<callable>>,field?:string,help?:string,form?:'simple'|'advanced',
  49. * method?:'GET'|'POST',HTMLtag?:'button',deprecated?:bool} $share_options */
  50. self::register($share_options);
  51. }
  52. uasort(self::$list_sharing, static fn(FreshRSS_Share $a, FreshRSS_Share $b) => strcasecmp($a->name() ?? '', $b->name() ?? ''));
  53. }
  54. /**
  55. * Return the list of sharing options.
  56. * @return array<string,FreshRSS_Share>
  57. */
  58. public static function enum(): array {
  59. return self::$list_sharing;
  60. }
  61. /**
  62. * @param string $type the share type, null if $type is not registered.
  63. * @return FreshRSS_Share|null object related to the given type.
  64. */
  65. public static function get(string $type): ?FreshRSS_Share {
  66. return self::$list_sharing[$type] ?? null;
  67. }
  68. private readonly string $name;
  69. /**
  70. * @phpstan-var 'simple'|'advanced'
  71. */
  72. private readonly string $form_type;
  73. private ?string $custom_name = null;
  74. private ?string $base_url = null;
  75. private ?string $id = null;
  76. private ?string $title = null;
  77. private ?string $link = null;
  78. /**
  79. * @phpstan-var 'GET'|'POST'
  80. */
  81. private string $method;
  82. /**
  83. * Create a FreshRSS_Share object.
  84. * @param string $type is a unique string defining the kind of share option.
  85. * @param string $url_transform defines the url format to use in order to share.
  86. * @param array<callable>|array<string,array<callable>> $transforms is an array of transformations to apply on link and title.
  87. * @param 'simple'|'advanced' $form_type defines which form we have to use to complete. "simple"
  88. * is typically for a centralized service while "advanced" is for
  89. * decentralized ones.
  90. * @param string $help_url is an optional url to give help on this option.
  91. * @param 'GET'|'POST' $method defines the sharing method (GET or POST)
  92. * @param 'button'|null $HTMLtag
  93. */
  94. private function __construct(
  95. private readonly string $type,
  96. private readonly string $url_transform,
  97. private array $transforms,
  98. string $form_type,
  99. private readonly string $help_url,
  100. string $method,
  101. private ?string $field,
  102. private readonly ?string $HTMLtag,
  103. private readonly bool $isDeprecated = false
  104. ) {
  105. $this->name = _t('gen.share.' . $this->type);
  106. if (!in_array($form_type, ['simple', 'advanced'], true)) {
  107. $form_type = 'simple';
  108. }
  109. $this->form_type = $form_type;
  110. if (!in_array($method, ['GET', 'POST'], true)) {
  111. $method = 'GET';
  112. }
  113. $this->method = $method;
  114. }
  115. /**
  116. * Update a FreshRSS_Share object with information from an array.
  117. * @param array<string,string> $options is a list of information to update where keys should be
  118. * in this list: name, url, id, title, link.
  119. */
  120. public function update(array $options): void {
  121. foreach ($options as $key => $value) {
  122. switch ($key) {
  123. case 'name':
  124. $this->custom_name = $value;
  125. break;
  126. case 'url':
  127. $this->base_url = $value;
  128. break;
  129. case 'id':
  130. $this->id = $value;
  131. break;
  132. case 'title':
  133. $this->title = $value;
  134. break;
  135. case 'link':
  136. $this->link = $value;
  137. break;
  138. case 'method':
  139. $this->method = strcasecmp($value, 'POST') === 0 ? 'POST' : 'GET';
  140. break;
  141. case 'field':
  142. $this->field = $value;
  143. break;
  144. }
  145. }
  146. }
  147. /**
  148. * Return the current type of the share option.
  149. */
  150. public function type(): string {
  151. return $this->type;
  152. }
  153. /**
  154. * Return the current method of the share option.
  155. * @return 'GET'|'POST'
  156. */
  157. public function method(): string {
  158. return $this->method;
  159. }
  160. /**
  161. * Return the current field of the share option. It’s null for shares
  162. * using the GET method.
  163. */
  164. public function field(): ?string {
  165. return $this->field;
  166. }
  167. /**
  168. * Return the current form type of the share option.
  169. * @return 'simple'|'advanced'
  170. */
  171. public function formType(): string {
  172. return $this->form_type;
  173. }
  174. /**
  175. * Return the current help url of the share option.
  176. */
  177. public function help(): string {
  178. return $this->help_url;
  179. }
  180. /**
  181. * Return the custom type of HTML tag of the share option, null for default.
  182. * @return 'button'|null
  183. */
  184. public function HTMLtag(): ?string {
  185. return $this->HTMLtag;
  186. }
  187. /**
  188. * Return the current name of the share option.
  189. */
  190. public function name(bool $real = false): ?string {
  191. if ($real || empty($this->custom_name)) {
  192. return $this->name;
  193. } else {
  194. return $this->custom_name;
  195. }
  196. }
  197. /**
  198. * Return the current base url of the share option.
  199. */
  200. public function baseUrl(): string {
  201. return $this->base_url ?? '';
  202. }
  203. /**
  204. * Return the deprecated status of the share option.
  205. */
  206. public function isDeprecated(): bool {
  207. return $this->isDeprecated;
  208. }
  209. /**
  210. * Return the current url by merging url_transform and base_url.
  211. */
  212. public function url(): string {
  213. $matches = [
  214. '~ID~',
  215. '~URL~',
  216. '~TITLE~',
  217. '~LINK~',
  218. ];
  219. $replaces = [
  220. $this->id() ?? '',
  221. $this->base_url ?? '',
  222. $this->title(),
  223. $this->link(),
  224. ];
  225. return str_replace($matches, $replaces, $this->url_transform);
  226. }
  227. /**
  228. * Return the id.
  229. * @param bool $raw true if we should get the id without transformations.
  230. */
  231. public function id(bool $raw = false): ?string {
  232. if ($raw) {
  233. return $this->id;
  234. }
  235. if ($this->id === null) {
  236. return null;
  237. }
  238. return self::transform($this->id, $this->getTransform('id'));
  239. }
  240. /**
  241. * Return the title.
  242. * @param bool $raw true if we should get the title without transformations.
  243. */
  244. public function title(bool $raw = false): string {
  245. if ($raw) {
  246. return $this->title ?? '';
  247. }
  248. if ($this->title === null) {
  249. return '';
  250. }
  251. return self::transform($this->title, $this->getTransform('title'));
  252. }
  253. /**
  254. * Return the link.
  255. * @param bool $raw true if we should get the link without transformations.
  256. */
  257. public function link(bool $raw = false): string {
  258. if ($raw) {
  259. return $this->link ?? '';
  260. }
  261. if ($this->link === null) {
  262. return '';
  263. }
  264. return self::transform($this->link, $this->getTransform('link'));
  265. }
  266. /**
  267. * Transform a data with the given functions.
  268. * @param string $data the data to transform.
  269. * @param array<callable> $transform an array containing a list of functions to apply.
  270. * @return string the transformed data.
  271. */
  272. private static function transform(string $data, array $transform): string {
  273. if (empty($transform)) {
  274. return $data;
  275. }
  276. foreach ($transform as $action) {
  277. $return = call_user_func($action, $data);
  278. if (is_string($return)) {
  279. $data = $return;
  280. }
  281. }
  282. return $data;
  283. }
  284. /**
  285. * Get the list of transformations for the given attribute.
  286. * @param string $attr the attribute of which we want the transformations.
  287. * @return list<callable> containing a list of transformations to apply.
  288. */
  289. private function getTransform(string $attr): array {
  290. if (array_key_exists($attr, $this->transforms)) {
  291. $candidates = is_array($this->transforms[$attr]) ? $this->transforms[$attr] : [];
  292. } else {
  293. $candidates = $this->transforms;
  294. }
  295. $transforms = [];
  296. foreach ($candidates as $transform) {
  297. if (is_callable($transform)) {
  298. $transforms[] = $transform;
  299. }
  300. }
  301. return $transforms;
  302. }
  303. }