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.

456 lines
11 KiB

3 years ago
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  6. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  7. * @author Joas Schilling <coding@schilljs.com>
  8. * @author Roeland Jago Douma <roeland@famdouma.nl>
  9. * @author Thomas Müller <thomas.mueller@tmit.eu>
  10. *
  11. * @license AGPL-3.0
  12. *
  13. * This code is free software: you can redistribute it and/or modify
  14. * it under the terms of the GNU Affero General Public License, version 3,
  15. * as published by the Free Software Foundation.
  16. *
  17. * This program is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU Affero General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU Affero General Public License, version 3,
  23. * along with this program. If not, see <http://www.gnu.org/licenses/>
  24. *
  25. */
  26. namespace OC\Comments;
  27. use OCP\Comments\IComment;
  28. use OCP\Comments\IllegalIDChangeException;
  29. use OCP\Comments\MessageTooLongException;
  30. class Comment implements IComment {
  31. protected $data = [
  32. 'id' => '',
  33. 'parentId' => '0',
  34. 'topmostParentId' => '0',
  35. 'childrenCount' => '0',
  36. 'message' => '',
  37. 'verb' => '',
  38. 'actorType' => '',
  39. 'actorId' => '',
  40. 'objectType' => '',
  41. 'objectId' => '',
  42. 'referenceId' => null,
  43. 'creationDT' => null,
  44. 'latestChildDT' => null,
  45. ];
  46. /**
  47. * Comment constructor.
  48. *
  49. * @param array $data optional, array with keys according to column names from
  50. * the comments database scheme
  51. */
  52. public function __construct(array $data = null) {
  53. if (is_array($data)) {
  54. $this->fromArray($data);
  55. }
  56. }
  57. /**
  58. * returns the ID of the comment
  59. *
  60. * It may return an empty string, if the comment was not stored.
  61. * It is expected that the concrete Comment implementation gives an ID
  62. * by itself (e.g. after saving).
  63. *
  64. * @return string
  65. * @since 9.0.0
  66. */
  67. public function getId() {
  68. return $this->data['id'];
  69. }
  70. /**
  71. * sets the ID of the comment and returns itself
  72. *
  73. * It is only allowed to set the ID only, if the current id is an empty
  74. * string (which means it is not stored in a database, storage or whatever
  75. * the concrete implementation does), or vice versa. Changing a given ID is
  76. * not permitted and must result in an IllegalIDChangeException.
  77. *
  78. * @param string $id
  79. * @return IComment
  80. * @throws IllegalIDChangeException
  81. * @since 9.0.0
  82. */
  83. public function setId($id) {
  84. if (!is_string($id)) {
  85. throw new \InvalidArgumentException('String expected.');
  86. }
  87. $id = trim($id);
  88. if ($this->data['id'] === '' || ($this->data['id'] !== '' && $id === '')) {
  89. $this->data['id'] = $id;
  90. return $this;
  91. }
  92. throw new IllegalIDChangeException('Not allowed to assign a new ID to an already saved comment.');
  93. }
  94. /**
  95. * returns the parent ID of the comment
  96. *
  97. * @return string
  98. * @since 9.0.0
  99. */
  100. public function getParentId() {
  101. return $this->data['parentId'];
  102. }
  103. /**
  104. * sets the parent ID and returns itself
  105. *
  106. * @param string $parentId
  107. * @return IComment
  108. * @since 9.0.0
  109. */
  110. public function setParentId($parentId) {
  111. if (!is_string($parentId)) {
  112. throw new \InvalidArgumentException('String expected.');
  113. }
  114. $this->data['parentId'] = trim($parentId);
  115. return $this;
  116. }
  117. /**
  118. * returns the topmost parent ID of the comment
  119. *
  120. * @return string
  121. * @since 9.0.0
  122. */
  123. public function getTopmostParentId() {
  124. return $this->data['topmostParentId'];
  125. }
  126. /**
  127. * sets the topmost parent ID and returns itself
  128. *
  129. * @param string $id
  130. * @return IComment
  131. * @since 9.0.0
  132. */
  133. public function setTopmostParentId($id) {
  134. if (!is_string($id)) {
  135. throw new \InvalidArgumentException('String expected.');
  136. }
  137. $this->data['topmostParentId'] = trim($id);
  138. return $this;
  139. }
  140. /**
  141. * returns the number of children
  142. *
  143. * @return int
  144. * @since 9.0.0
  145. */
  146. public function getChildrenCount() {
  147. return $this->data['childrenCount'];
  148. }
  149. /**
  150. * sets the number of children
  151. *
  152. * @param int $count
  153. * @return IComment
  154. * @since 9.0.0
  155. */
  156. public function setChildrenCount($count) {
  157. if (!is_int($count)) {
  158. throw new \InvalidArgumentException('Integer expected.');
  159. }
  160. $this->data['childrenCount'] = $count;
  161. return $this;
  162. }
  163. /**
  164. * returns the message of the comment
  165. *
  166. * @return string
  167. * @since 9.0.0
  168. */
  169. public function getMessage() {
  170. return $this->data['message'];
  171. }
  172. /**
  173. * sets the message of the comment and returns itself
  174. *
  175. * @param string $message
  176. * @param int $maxLength
  177. * @return IComment
  178. * @throws MessageTooLongException
  179. * @since 9.0.0
  180. */
  181. public function setMessage($message, $maxLength = self::MAX_MESSAGE_LENGTH) {
  182. if (!is_string($message)) {
  183. throw new \InvalidArgumentException('String expected.');
  184. }
  185. $message = trim($message);
  186. if ($maxLength && mb_strlen($message, 'UTF-8') > $maxLength) {
  187. throw new MessageTooLongException('Comment message must not exceed ' . $maxLength. ' characters');
  188. }
  189. $this->data['message'] = $message;
  190. return $this;
  191. }
  192. /**
  193. * returns an array containing mentions that are included in the comment
  194. *
  195. * @return array each mention provides a 'type' and an 'id', see example below
  196. * @since 11.0.0
  197. *
  198. * The return array looks like:
  199. * [
  200. * [
  201. * 'type' => 'user',
  202. * 'id' => 'citizen4'
  203. * ],
  204. * [
  205. * 'type' => 'group',
  206. * 'id' => 'media'
  207. * ],
  208. *
  209. * ]
  210. *
  211. */
  212. public function getMentions() {
  213. $ok = preg_match_all("/\B(?<![^a-z0-9_\-@\.\'\s])@(\"guest\/[a-f0-9]+\"|\"[a-z0-9_\-@\.\' ]+\"|[a-z0-9_\-@\.\']+)/i", $this->getMessage(), $mentions);
  214. if (!$ok || !isset($mentions[0]) || !is_array($mentions[0])) {
  215. return [];
  216. }
  217. $uids = array_unique($mentions[0]);
  218. $result = [];
  219. foreach ($uids as $uid) {
  220. $cleanUid = trim(substr($uid, 1), '"');
  221. if (strpos($cleanUid, 'guest/') === 0) {
  222. $result[] = ['type' => 'guest', 'id' => $cleanUid];
  223. } else {
  224. $result[] = ['type' => 'user', 'id' => $cleanUid];
  225. }
  226. }
  227. return $result;
  228. }
  229. /**
  230. * returns the verb of the comment
  231. *
  232. * @return string
  233. * @since 9.0.0
  234. */
  235. public function getVerb() {
  236. return $this->data['verb'];
  237. }
  238. /**
  239. * sets the verb of the comment, e.g. 'comment' or 'like'
  240. *
  241. * @param string $verb
  242. * @return IComment
  243. * @since 9.0.0
  244. */
  245. public function setVerb($verb) {
  246. if (!is_string($verb) || !trim($verb)) {
  247. throw new \InvalidArgumentException('Non-empty String expected.');
  248. }
  249. $this->data['verb'] = trim($verb);
  250. return $this;
  251. }
  252. /**
  253. * returns the actor type
  254. *
  255. * @return string
  256. * @since 9.0.0
  257. */
  258. public function getActorType() {
  259. return $this->data['actorType'];
  260. }
  261. /**
  262. * returns the actor ID
  263. *
  264. * @return string
  265. * @since 9.0.0
  266. */
  267. public function getActorId() {
  268. return $this->data['actorId'];
  269. }
  270. /**
  271. * sets (overwrites) the actor type and id
  272. *
  273. * @param string $actorType e.g. 'users'
  274. * @param string $actorId e.g. 'zombie234'
  275. * @return IComment
  276. * @since 9.0.0
  277. */
  278. public function setActor($actorType, $actorId) {
  279. if (
  280. !is_string($actorType) || !trim($actorType)
  281. || !is_string($actorId) || $actorId === ''
  282. ) {
  283. throw new \InvalidArgumentException('String expected.');
  284. }
  285. $this->data['actorType'] = trim($actorType);
  286. $this->data['actorId'] = $actorId;
  287. return $this;
  288. }
  289. /**
  290. * returns the creation date of the comment.
  291. *
  292. * If not explicitly set, it shall default to the time of initialization.
  293. *
  294. * @return \DateTime
  295. * @since 9.0.0
  296. */
  297. public function getCreationDateTime() {
  298. return $this->data['creationDT'];
  299. }
  300. /**
  301. * sets the creation date of the comment and returns itself
  302. *
  303. * @param \DateTime $timestamp
  304. * @return IComment
  305. * @since 9.0.0
  306. */
  307. public function setCreationDateTime(\DateTime $timestamp) {
  308. $this->data['creationDT'] = $timestamp;
  309. return $this;
  310. }
  311. /**
  312. * returns the DateTime of the most recent child, if set, otherwise null
  313. *
  314. * @return \DateTime|null
  315. * @since 9.0.0
  316. */
  317. public function getLatestChildDateTime() {
  318. return $this->data['latestChildDT'];
  319. }
  320. /**
  321. * sets the date of the most recent child
  322. *
  323. * @param \DateTime $dateTime
  324. * @return IComment
  325. * @since 9.0.0
  326. */
  327. public function setLatestChildDateTime(\DateTime $dateTime = null) {
  328. $this->data['latestChildDT'] = $dateTime;
  329. return $this;
  330. }
  331. /**
  332. * returns the object type the comment is attached to
  333. *
  334. * @return string
  335. * @since 9.0.0
  336. */
  337. public function getObjectType() {
  338. return $this->data['objectType'];
  339. }
  340. /**
  341. * returns the object id the comment is attached to
  342. *
  343. * @return string
  344. * @since 9.0.0
  345. */
  346. public function getObjectId() {
  347. return $this->data['objectId'];
  348. }
  349. /**
  350. * sets (overwrites) the object of the comment
  351. *
  352. * @param string $objectType e.g. 'files'
  353. * @param string $objectId e.g. '16435'
  354. * @return IComment
  355. * @since 9.0.0
  356. */
  357. public function setObject($objectType, $objectId) {
  358. if (
  359. !is_string($objectType) || !trim($objectType)
  360. || !is_string($objectId) || trim($objectId) === ''
  361. ) {
  362. throw new \InvalidArgumentException('String expected.');
  363. }
  364. $this->data['objectType'] = trim($objectType);
  365. $this->data['objectId'] = trim($objectId);
  366. return $this;
  367. }
  368. /**
  369. * returns the reference id of the comment
  370. *
  371. * @return string|null
  372. * @since 19.0.0
  373. */
  374. public function getReferenceId(): ?string {
  375. return $this->data['referenceId'];
  376. }
  377. /**
  378. * sets (overwrites) the reference id of the comment
  379. *
  380. * @param string $referenceId e.g. sha256 hash sum
  381. * @return IComment
  382. * @since 19.0.0
  383. */
  384. public function setReferenceId(?string $referenceId): IComment {
  385. if ($referenceId === null) {
  386. $this->data['referenceId'] = $referenceId;
  387. } else {
  388. $referenceId = trim($referenceId);
  389. if ($referenceId === '') {
  390. throw new \InvalidArgumentException('Non empty string expected.');
  391. }
  392. $this->data['referenceId'] = $referenceId;
  393. }
  394. return $this;
  395. }
  396. /**
  397. * sets the comment data based on an array with keys as taken from the
  398. * database.
  399. *
  400. * @param array $data
  401. * @return IComment
  402. */
  403. protected function fromArray($data) {
  404. foreach (array_keys($data) as $key) {
  405. // translate DB keys to internal setter names
  406. $setter = 'set' . implode('', array_map('ucfirst', explode('_', $key)));
  407. $setter = str_replace('Timestamp', 'DateTime', $setter);
  408. if (method_exists($this, $setter)) {
  409. $this->$setter($data[$key]);
  410. }
  411. }
  412. foreach (['actor', 'object'] as $role) {
  413. if (isset($data[$role . '_type']) && isset($data[$role . '_id'])) {
  414. $setter = 'set' . ucfirst($role);
  415. $this->$setter($data[$role . '_type'], $data[$role . '_id']);
  416. }
  417. }
  418. return $this;
  419. }
  420. }