You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Notification.php 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright Copyright (c) 2016, ownCloud, Inc.
  5. *
  6. * @author Joas Schilling <coding@schilljs.com>
  7. * @author Morris Jobke <hey@morrisjobke.de>
  8. * @author Roeland Jago Douma <roeland@famdouma.nl>
  9. *
  10. * @license AGPL-3.0
  11. *
  12. * This code is free software: you can redistribute it and/or modify
  13. * it under the terms of the GNU Affero General Public License, version 3,
  14. * as published by the Free Software Foundation.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU Affero General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Affero General Public License, version 3,
  22. * along with this program. If not, see <http://www.gnu.org/licenses/>
  23. *
  24. */
  25. namespace OC\Notification;
  26. use OCP\Notification\IAction;
  27. use OCP\Notification\INotification;
  28. use OCP\RichObjectStrings\InvalidObjectExeption;
  29. use OCP\RichObjectStrings\IValidator;
  30. class Notification implements INotification {
  31. protected string $app = '';
  32. protected string $user = '';
  33. protected \DateTime $dateTime;
  34. protected string $objectType = '';
  35. protected string $objectId = '';
  36. protected string $subject = '';
  37. protected array $subjectParameters = [];
  38. protected string $subjectParsed = '';
  39. protected string $subjectRich = '';
  40. protected array $subjectRichParameters = [];
  41. protected string $message = '';
  42. protected array $messageParameters = [];
  43. protected string $messageParsed = '';
  44. protected string $messageRich = '';
  45. protected array $messageRichParameters = [];
  46. protected string $link = '';
  47. protected string $icon = '';
  48. protected array $actions = [];
  49. protected array $actionsParsed = [];
  50. protected bool $hasPrimaryAction = false;
  51. protected bool $hasPrimaryParsedAction = false;
  52. public function __construct(
  53. protected IValidator $richValidator,
  54. ) {
  55. $this->dateTime = new \DateTime();
  56. $this->dateTime->setTimestamp(0);
  57. }
  58. /**
  59. * @param string $app
  60. * @return $this
  61. * @throws \InvalidArgumentException if the app id is invalid
  62. * @since 8.2.0
  63. */
  64. public function setApp(string $app): INotification {
  65. if ($app === '' || isset($app[32])) {
  66. throw new \InvalidArgumentException('The given app name is invalid');
  67. }
  68. $this->app = $app;
  69. return $this;
  70. }
  71. /**
  72. * @return string
  73. * @since 8.2.0
  74. */
  75. public function getApp(): string {
  76. return $this->app;
  77. }
  78. /**
  79. * @param string $user
  80. * @return $this
  81. * @throws \InvalidArgumentException if the user id is invalid
  82. * @since 8.2.0
  83. */
  84. public function setUser(string $user): INotification {
  85. if ($user === '' || isset($user[64])) {
  86. throw new \InvalidArgumentException('The given user id is invalid');
  87. }
  88. $this->user = $user;
  89. return $this;
  90. }
  91. /**
  92. * @return string
  93. * @since 8.2.0
  94. */
  95. public function getUser(): string {
  96. return $this->user;
  97. }
  98. /**
  99. * @param \DateTime $dateTime
  100. * @return $this
  101. * @throws \InvalidArgumentException if the $dateTime is invalid
  102. * @since 9.0.0
  103. */
  104. public function setDateTime(\DateTime $dateTime): INotification {
  105. if ($dateTime->getTimestamp() === 0) {
  106. throw new \InvalidArgumentException('The given date time is invalid');
  107. }
  108. $this->dateTime = $dateTime;
  109. return $this;
  110. }
  111. /**
  112. * @return \DateTime
  113. * @since 9.0.0
  114. */
  115. public function getDateTime(): \DateTime {
  116. return $this->dateTime;
  117. }
  118. /**
  119. * @param string $type
  120. * @param string $id
  121. * @return $this
  122. * @throws \InvalidArgumentException if the object type or id is invalid
  123. * @since 8.2.0 - 9.0.0: Type of $id changed to string
  124. */
  125. public function setObject(string $type, string $id): INotification {
  126. if ($type === '' || isset($type[64])) {
  127. throw new \InvalidArgumentException('The given object type is invalid');
  128. }
  129. $this->objectType = $type;
  130. if ($id === '' || isset($id[64])) {
  131. throw new \InvalidArgumentException('The given object id is invalid');
  132. }
  133. $this->objectId = $id;
  134. return $this;
  135. }
  136. /**
  137. * @return string
  138. * @since 8.2.0
  139. */
  140. public function getObjectType(): string {
  141. return $this->objectType;
  142. }
  143. /**
  144. * @return string
  145. * @since 8.2.0 - 9.0.0: Return type changed to string
  146. */
  147. public function getObjectId(): string {
  148. return $this->objectId;
  149. }
  150. /**
  151. * @param string $subject
  152. * @param array $parameters
  153. * @return $this
  154. * @throws \InvalidArgumentException if the subject or parameters are invalid
  155. * @since 8.2.0
  156. */
  157. public function setSubject(string $subject, array $parameters = []): INotification {
  158. if ($subject === '' || isset($subject[64])) {
  159. throw new \InvalidArgumentException('The given subject is invalid');
  160. }
  161. $this->subject = $subject;
  162. $this->subjectParameters = $parameters;
  163. return $this;
  164. }
  165. /**
  166. * @return string
  167. * @since 8.2.0
  168. */
  169. public function getSubject(): string {
  170. return $this->subject;
  171. }
  172. /**
  173. * @return array
  174. * @since 8.2.0
  175. */
  176. public function getSubjectParameters(): array {
  177. return $this->subjectParameters;
  178. }
  179. /**
  180. * @param string $subject
  181. * @return $this
  182. * @throws \InvalidArgumentException if the subject is invalid
  183. * @since 8.2.0
  184. */
  185. public function setParsedSubject(string $subject): INotification {
  186. if ($subject === '') {
  187. throw new \InvalidArgumentException('The given parsed subject is invalid');
  188. }
  189. $this->subjectParsed = $subject;
  190. return $this;
  191. }
  192. /**
  193. * @return string
  194. * @since 8.2.0
  195. */
  196. public function getParsedSubject(): string {
  197. return $this->subjectParsed;
  198. }
  199. /**
  200. * @param string $subject
  201. * @param array $parameters
  202. * @return $this
  203. * @throws \InvalidArgumentException if the subject or parameters are invalid
  204. * @since 11.0.0
  205. */
  206. public function setRichSubject(string $subject, array $parameters = []): INotification {
  207. if ($subject === '') {
  208. throw new \InvalidArgumentException('The given parsed subject is invalid');
  209. }
  210. $this->subjectRich = $subject;
  211. $this->subjectRichParameters = $parameters;
  212. if ($this->subjectParsed === '') {
  213. $this->subjectParsed = $this->richToParsed($subject, $parameters);
  214. }
  215. return $this;
  216. }
  217. /**
  218. * @throws \InvalidArgumentException if a parameter has no name or no type
  219. */
  220. private function richToParsed(string $message, array $parameters): string {
  221. $placeholders = [];
  222. $replacements = [];
  223. foreach ($parameters as $placeholder => $parameter) {
  224. $placeholders[] = '{' . $placeholder . '}';
  225. foreach (['name','type'] as $requiredField) {
  226. if (!isset($parameter[$requiredField]) || !is_string($parameter[$requiredField])) {
  227. throw new \InvalidArgumentException("Invalid rich object, {$requiredField} field is missing");
  228. }
  229. }
  230. if ($parameter['type'] === 'user') {
  231. $replacements[] = '@' . $parameter['name'];
  232. } elseif ($parameter['type'] === 'file') {
  233. $replacements[] = $parameter['path'] ?? $parameter['name'];
  234. } else {
  235. $replacements[] = $parameter['name'];
  236. }
  237. }
  238. return str_replace($placeholders, $replacements, $message);
  239. }
  240. /**
  241. * @return string
  242. * @since 11.0.0
  243. */
  244. public function getRichSubject(): string {
  245. return $this->subjectRich;
  246. }
  247. /**
  248. * @return array[]
  249. * @since 11.0.0
  250. */
  251. public function getRichSubjectParameters(): array {
  252. return $this->subjectRichParameters;
  253. }
  254. /**
  255. * @param string $message
  256. * @param array $parameters
  257. * @return $this
  258. * @throws \InvalidArgumentException if the message or parameters are invalid
  259. * @since 8.2.0
  260. */
  261. public function setMessage(string $message, array $parameters = []): INotification {
  262. if ($message === '' || isset($message[64])) {
  263. throw new \InvalidArgumentException('The given message is invalid');
  264. }
  265. $this->message = $message;
  266. $this->messageParameters = $parameters;
  267. return $this;
  268. }
  269. /**
  270. * @return string
  271. * @since 8.2.0
  272. */
  273. public function getMessage(): string {
  274. return $this->message;
  275. }
  276. /**
  277. * @return array
  278. * @since 8.2.0
  279. */
  280. public function getMessageParameters(): array {
  281. return $this->messageParameters;
  282. }
  283. /**
  284. * @param string $message
  285. * @return $this
  286. * @throws \InvalidArgumentException if the message is invalid
  287. * @since 8.2.0
  288. */
  289. public function setParsedMessage(string $message): INotification {
  290. if ($message === '') {
  291. throw new \InvalidArgumentException('The given parsed message is invalid');
  292. }
  293. $this->messageParsed = $message;
  294. return $this;
  295. }
  296. /**
  297. * @return string
  298. * @since 8.2.0
  299. */
  300. public function getParsedMessage(): string {
  301. return $this->messageParsed;
  302. }
  303. /**
  304. * @param string $message
  305. * @param array $parameters
  306. * @return $this
  307. * @throws \InvalidArgumentException if the message or parameters are invalid
  308. * @since 11.0.0
  309. */
  310. public function setRichMessage(string $message, array $parameters = []): INotification {
  311. if ($message === '') {
  312. throw new \InvalidArgumentException('The given parsed message is invalid');
  313. }
  314. $this->messageRich = $message;
  315. $this->messageRichParameters = $parameters;
  316. if ($this->messageParsed === '') {
  317. $this->messageParsed = $this->richToParsed($message, $parameters);
  318. }
  319. return $this;
  320. }
  321. /**
  322. * @return string
  323. * @since 11.0.0
  324. */
  325. public function getRichMessage(): string {
  326. return $this->messageRich;
  327. }
  328. /**
  329. * @return array[]
  330. * @since 11.0.0
  331. */
  332. public function getRichMessageParameters(): array {
  333. return $this->messageRichParameters;
  334. }
  335. /**
  336. * @param string $link
  337. * @return $this
  338. * @throws \InvalidArgumentException if the link is invalid
  339. * @since 8.2.0
  340. */
  341. public function setLink(string $link): INotification {
  342. if ($link === '' || isset($link[4000])) {
  343. throw new \InvalidArgumentException('The given link is invalid');
  344. }
  345. $this->link = $link;
  346. return $this;
  347. }
  348. /**
  349. * @return string
  350. * @since 8.2.0
  351. */
  352. public function getLink(): string {
  353. return $this->link;
  354. }
  355. /**
  356. * @param string $icon
  357. * @return $this
  358. * @throws \InvalidArgumentException if the icon is invalid
  359. * @since 11.0.0
  360. */
  361. public function setIcon(string $icon): INotification {
  362. if ($icon === '' || isset($icon[4000])) {
  363. throw new \InvalidArgumentException('The given icon is invalid');
  364. }
  365. $this->icon = $icon;
  366. return $this;
  367. }
  368. /**
  369. * @return string
  370. * @since 11.0.0
  371. */
  372. public function getIcon(): string {
  373. return $this->icon;
  374. }
  375. /**
  376. * @return IAction
  377. * @since 8.2.0
  378. */
  379. public function createAction(): IAction {
  380. return new Action();
  381. }
  382. /**
  383. * @param IAction $action
  384. * @return $this
  385. * @throws \InvalidArgumentException if the action is invalid
  386. * @since 8.2.0
  387. */
  388. public function addAction(IAction $action): INotification {
  389. if (!$action->isValid()) {
  390. throw new \InvalidArgumentException('The given action is invalid');
  391. }
  392. if ($action->isPrimary()) {
  393. if ($this->hasPrimaryAction) {
  394. throw new \InvalidArgumentException('The notification already has a primary action');
  395. }
  396. $this->hasPrimaryAction = true;
  397. }
  398. $this->actions[] = $action;
  399. return $this;
  400. }
  401. /**
  402. * @return IAction[]
  403. * @since 8.2.0
  404. */
  405. public function getActions(): array {
  406. return $this->actions;
  407. }
  408. /**
  409. * @param IAction $action
  410. * @return $this
  411. * @throws \InvalidArgumentException if the action is invalid
  412. * @since 8.2.0
  413. */
  414. public function addParsedAction(IAction $action): INotification {
  415. if (!$action->isValidParsed()) {
  416. throw new \InvalidArgumentException('The given parsed action is invalid');
  417. }
  418. if ($action->isPrimary()) {
  419. if ($this->hasPrimaryParsedAction) {
  420. throw new \InvalidArgumentException('The notification already has a primary action');
  421. }
  422. $this->hasPrimaryParsedAction = true;
  423. // Make sure the primary action is always the first one
  424. array_unshift($this->actionsParsed, $action);
  425. } else {
  426. $this->actionsParsed[] = $action;
  427. }
  428. return $this;
  429. }
  430. /**
  431. * @return IAction[]
  432. * @since 8.2.0
  433. */
  434. public function getParsedActions(): array {
  435. return $this->actionsParsed;
  436. }
  437. /**
  438. * @return bool
  439. * @since 8.2.0
  440. */
  441. public function isValid(): bool {
  442. return
  443. $this->isValidCommon()
  444. &&
  445. $this->getSubject() !== ''
  446. ;
  447. }
  448. /**
  449. * @return bool
  450. * @since 8.2.0
  451. */
  452. public function isValidParsed(): bool {
  453. if ($this->getRichSubject() !== '' || !empty($this->getRichSubjectParameters())) {
  454. try {
  455. $this->richValidator->validate($this->getRichSubject(), $this->getRichSubjectParameters());
  456. } catch (InvalidObjectExeption $e) {
  457. return false;
  458. }
  459. }
  460. if ($this->getRichMessage() !== '' || !empty($this->getRichMessageParameters())) {
  461. try {
  462. $this->richValidator->validate($this->getRichMessage(), $this->getRichMessageParameters());
  463. } catch (InvalidObjectExeption $e) {
  464. return false;
  465. }
  466. }
  467. return
  468. $this->isValidCommon()
  469. &&
  470. $this->getParsedSubject() !== ''
  471. ;
  472. }
  473. protected function isValidCommon(): bool {
  474. return
  475. $this->getApp() !== ''
  476. &&
  477. $this->getUser() !== ''
  478. &&
  479. $this->getDateTime()->getTimestamp() !== 0
  480. &&
  481. $this->getObjectType() !== ''
  482. &&
  483. $this->getObjectId() !== ''
  484. ;
  485. }
  486. }