EventMerger.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
  4. *
  5. * @license GNU AGPL version 3 or any later version
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Affero General Public License as
  9. * published by the Free Software Foundation, either version 3 of the
  10. * License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Affero General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. namespace OC\Activity;
  22. use OCP\Activity\IEvent;
  23. use OCP\Activity\IEventMerger;
  24. use OCP\IL10N;
  25. class EventMerger implements IEventMerger {
  26. /** @var IL10N */
  27. protected $l10n;
  28. /**
  29. * @param IL10N $l10n
  30. */
  31. public function __construct(IL10N $l10n) {
  32. $this->l10n = $l10n;
  33. }
  34. /**
  35. * Combines two events when possible to have grouping:
  36. *
  37. * Example1: Two events with subject '{user} created {file}' and
  38. * $mergeParameter file with different file and same user will be merged
  39. * to '{user} created {file1} and {file2}' and the childEvent on the return
  40. * will be set, if the events have been merged.
  41. *
  42. * Example2: Two events with subject '{user} created {file}' and
  43. * $mergeParameter file with same file and same user will be merged to
  44. * '{user} created {file1}' and the childEvent on the return will be set, if
  45. * the events have been merged.
  46. *
  47. * The following requirements have to be met, in order to be merged:
  48. * - Both events need to have the same `getApp()`
  49. * - Both events must not have a message `getMessage()`
  50. * - Both events need to have the same subject `getSubject()`
  51. * - Both events need to have the same object type `getObjectType()`
  52. * - The time difference between both events must not be bigger then 3 hours
  53. * - Only up to 5 events can be merged.
  54. * - All parameters apart from such starting with $mergeParameter must be
  55. * the same for both events.
  56. *
  57. * @param string $mergeParameter
  58. * @param IEvent $event
  59. * @param IEvent|null $previousEvent
  60. * @return IEvent
  61. */
  62. public function mergeEvents($mergeParameter, IEvent $event, IEvent $previousEvent = null) {
  63. // No second event => can not combine
  64. if (!$previousEvent instanceof IEvent) {
  65. return $event;
  66. }
  67. // Different app => can not combine
  68. if ($event->getApp() !== $previousEvent->getApp()) {
  69. return $event;
  70. }
  71. // Message is set => can not combine
  72. if ($event->getMessage() !== '' || $previousEvent->getMessage() !== '') {
  73. return $event;
  74. }
  75. // Different subject => can not combine
  76. if ($event->getSubject() !== $previousEvent->getSubject()) {
  77. return $event;
  78. }
  79. // Different object type => can not combine
  80. if ($event->getObjectType() !== $previousEvent->getObjectType()) {
  81. return $event;
  82. }
  83. // More than 3 hours difference => can not combine
  84. if (abs($event->getTimestamp() - $previousEvent->getTimestamp()) > 3 * 60 * 60) {
  85. return $event;
  86. }
  87. // Other parameters are not the same => can not combine
  88. try {
  89. list($combined, $parameters) = $this->combineParameters($mergeParameter, $event, $previousEvent);
  90. } catch (\UnexpectedValueException $e) {
  91. return $event;
  92. }
  93. try {
  94. $newSubject = $this->getExtendedSubject($event->getRichSubject(), $mergeParameter, $combined);
  95. $parsedSubject = $this->generateParsedSubject($newSubject, $parameters);
  96. $event->setRichSubject($newSubject, $parameters)
  97. ->setParsedSubject($parsedSubject)
  98. ->setChildEvent($previousEvent);
  99. } catch (\UnexpectedValueException $e) {
  100. return $event;
  101. }
  102. return $event;
  103. }
  104. /**
  105. * @param string $mergeParameter
  106. * @param IEvent $event
  107. * @param IEvent $previousEvent
  108. * @return array
  109. * @throws \UnexpectedValueException
  110. */
  111. protected function combineParameters($mergeParameter, IEvent $event, IEvent $previousEvent) {
  112. $params1 = $event->getRichSubjectParameters();
  113. $params2 = $previousEvent->getRichSubjectParameters();
  114. $params = [];
  115. $combined = 0;
  116. // Check that all parameters from $event exist in $previousEvent
  117. foreach ($params1 as $key => $parameter) {
  118. if (preg_match('/^' . $mergeParameter . '(\d+)?$/', $key)) {
  119. if (!$this->checkParameterAlreadyExits($params, $mergeParameter, $parameter)) {
  120. $combined++;
  121. $params[$mergeParameter . $combined] = $parameter;
  122. }
  123. continue;
  124. }
  125. if (!isset($params2[$key]) || $params2[$key] !== $parameter) {
  126. // Parameter missing on $previousEvent or different => can not combine
  127. throw new \UnexpectedValueException();
  128. }
  129. $params[$key] = $parameter;
  130. }
  131. // Check that all parameters from $previousEvent exist in $event
  132. foreach ($params2 as $key => $parameter) {
  133. if (preg_match('/^' . $mergeParameter . '(\d+)?$/', $key)) {
  134. if (!$this->checkParameterAlreadyExits($params, $mergeParameter, $parameter)) {
  135. $combined++;
  136. $params[$mergeParameter . $combined] = $parameter;
  137. }
  138. continue;
  139. }
  140. if (!isset($params1[$key]) || $params1[$key] !== $parameter) {
  141. // Parameter missing on $event or different => can not combine
  142. throw new \UnexpectedValueException();
  143. }
  144. $params[$key] = $parameter;
  145. }
  146. return [$combined, $params];
  147. }
  148. /**
  149. * @param array[] $parameters
  150. * @param string $mergeParameter
  151. * @param array $parameter
  152. * @return bool
  153. */
  154. protected function checkParameterAlreadyExits($parameters, $mergeParameter, $parameter) {
  155. foreach ($parameters as $key => $param) {
  156. if (preg_match('/^' . $mergeParameter . '(\d+)?$/', $key)) {
  157. if ($param === $parameter) {
  158. return true;
  159. }
  160. }
  161. }
  162. return false;
  163. }
  164. /**
  165. * @param string $subject
  166. * @param string $parameter
  167. * @param int $counter
  168. * @return mixed
  169. */
  170. protected function getExtendedSubject($subject, $parameter, $counter) {
  171. switch ($counter) {
  172. case 1:
  173. $replacement = '{' . $parameter . '1}';
  174. break;
  175. case 2:
  176. $replacement = $this->l10n->t(
  177. '%1$s and %2$s',
  178. ['{' . $parameter . '1}', '{' . $parameter . '2}']
  179. );
  180. break;
  181. case 3:
  182. $replacement = $this->l10n->t(
  183. '%1$s, %2$s and %3$s',
  184. ['{' . $parameter . '1}', '{' . $parameter . '2}', '{' . $parameter . '3}']
  185. );
  186. break;
  187. case 4:
  188. $replacement = $this->l10n->t(
  189. '%1$s, %2$s, %3$s and %4$s',
  190. ['{' . $parameter . '1}', '{' . $parameter . '2}', '{' . $parameter . '3}', '{' . $parameter . '4}']
  191. );
  192. break;
  193. case 5:
  194. $replacement = $this->l10n->t(
  195. '%1$s, %2$s, %3$s, %4$s and %5$s',
  196. ['{' . $parameter . '1}', '{' . $parameter . '2}', '{' . $parameter . '3}', '{' . $parameter . '4}', '{' . $parameter . '5}']
  197. );
  198. break;
  199. default:
  200. throw new \UnexpectedValueException();
  201. }
  202. return str_replace(
  203. '{' . $parameter . '}',
  204. $replacement,
  205. $subject
  206. );
  207. }
  208. /**
  209. * @param string $subject
  210. * @param array[] $parameters
  211. * @return string
  212. */
  213. protected function generateParsedSubject($subject, $parameters) {
  214. $placeholders = $replacements = [];
  215. foreach ($parameters as $placeholder => $parameter) {
  216. $placeholders[] = '{' . $placeholder . '}';
  217. if ($parameter['type'] === 'file') {
  218. $replacements[] = trim($parameter['path'], '/');
  219. } else if (isset($parameter['name'])) {
  220. $replacements[] = $parameter['name'];
  221. } else {
  222. $replacements[] = $parameter['id'];
  223. }
  224. }
  225. return str_replace($placeholders, $replacements, $subject);
  226. }
  227. }