@@ -25,641 +25,641 @@ |
||
| 25 | 25 | * @since 4.0.0 |
| 26 | 26 | */ |
| 27 | 27 | class Util { |
| 28 | - private static ?IManager $shareManager = null; |
|
| 29 | - |
|
| 30 | - private static array $scriptsInit = []; |
|
| 31 | - private static array $scripts = []; |
|
| 32 | - private static array $scriptDeps = []; |
|
| 33 | - |
|
| 34 | - /** |
|
| 35 | - * get the current installed version of Nextcloud |
|
| 36 | - * @return array |
|
| 37 | - * @since 4.0.0 |
|
| 38 | - * @deprecated 31.0.0 Use \OCP\ServerVersion::getVersion |
|
| 39 | - */ |
|
| 40 | - public static function getVersion() { |
|
| 41 | - return Server::get(ServerVersion::class)->getVersion(); |
|
| 42 | - } |
|
| 43 | - |
|
| 44 | - /** |
|
| 45 | - * @since 17.0.0 |
|
| 46 | - */ |
|
| 47 | - public static function hasExtendedSupport(): bool { |
|
| 48 | - try { |
|
| 49 | - /** @var \OCP\Support\Subscription\IRegistry */ |
|
| 50 | - $subscriptionRegistry = Server::get(\OCP\Support\Subscription\IRegistry::class); |
|
| 51 | - return $subscriptionRegistry->delegateHasExtendedSupport(); |
|
| 52 | - } catch (ContainerExceptionInterface $e) { |
|
| 53 | - } |
|
| 54 | - return \OCP\Server::get(IConfig::class)->getSystemValueBool('extendedSupport', false); |
|
| 55 | - } |
|
| 56 | - |
|
| 57 | - /** |
|
| 58 | - * Set current update channel |
|
| 59 | - * @param string $channel |
|
| 60 | - * @since 8.1.0 |
|
| 61 | - */ |
|
| 62 | - public static function setChannel($channel) { |
|
| 63 | - \OCP\Server::get(IConfig::class)->setSystemValue('updater.release.channel', $channel); |
|
| 64 | - } |
|
| 65 | - |
|
| 66 | - /** |
|
| 67 | - * Get current update channel |
|
| 68 | - * @return string |
|
| 69 | - * @since 8.1.0 |
|
| 70 | - * @deprecated 31.0.0 Use \OCP\ServerVersion::getChannel |
|
| 71 | - */ |
|
| 72 | - public static function getChannel() { |
|
| 73 | - return \OCP\Server::get(ServerVersion::class)->getChannel(); |
|
| 74 | - } |
|
| 75 | - |
|
| 76 | - /** |
|
| 77 | - * check if sharing is disabled for the current user |
|
| 78 | - * |
|
| 79 | - * @return boolean |
|
| 80 | - * @since 7.0.0 |
|
| 81 | - * @deprecated 9.1.0 Use Server::get(\OCP\Share\IManager::class)->sharingDisabledForUser |
|
| 82 | - */ |
|
| 83 | - public static function isSharingDisabledForUser() { |
|
| 84 | - if (self::$shareManager === null) { |
|
| 85 | - self::$shareManager = Server::get(IManager::class); |
|
| 86 | - } |
|
| 87 | - |
|
| 88 | - $user = Server::get(\OCP\IUserSession::class)->getUser(); |
|
| 89 | - |
|
| 90 | - return self::$shareManager->sharingDisabledForUser($user?->getUID()); |
|
| 91 | - } |
|
| 92 | - |
|
| 93 | - /** |
|
| 94 | - * get l10n object |
|
| 95 | - * @since 6.0.0 - parameter $language was added in 8.0.0 |
|
| 96 | - */ |
|
| 97 | - public static function getL10N(string $application, ?string $language = null): IL10N { |
|
| 98 | - return Server::get(\OCP\L10N\IFactory::class)->get($application, $language); |
|
| 99 | - } |
|
| 100 | - |
|
| 101 | - /** |
|
| 102 | - * Add a css file |
|
| 103 | - * |
|
| 104 | - * @param string $application application id |
|
| 105 | - * @param ?string $file filename |
|
| 106 | - * @param bool $prepend prepend the style to the beginning of the list |
|
| 107 | - * @since 4.0.0 |
|
| 108 | - */ |
|
| 109 | - public static function addStyle(string $application, ?string $file = null, bool $prepend = false): void { |
|
| 110 | - \OC_Util::addStyle($application, $file, $prepend); |
|
| 111 | - } |
|
| 112 | - |
|
| 113 | - /** |
|
| 114 | - * Add a standalone init js file that is loaded for initialization |
|
| 115 | - * |
|
| 116 | - * Be careful loading scripts using this method as they are loaded early |
|
| 117 | - * and block the initial page rendering. They should not have dependencies |
|
| 118 | - * on any other scripts than core-common and core-main. |
|
| 119 | - * |
|
| 120 | - * @since 28.0.0 |
|
| 121 | - */ |
|
| 122 | - public static function addInitScript(string $application, string $file): void { |
|
| 123 | - if (!empty($application)) { |
|
| 124 | - $path = "$application/js/$file"; |
|
| 125 | - } else { |
|
| 126 | - $path = "js/$file"; |
|
| 127 | - } |
|
| 128 | - |
|
| 129 | - // We need to handle the translation BEFORE the init script |
|
| 130 | - // is loaded, as the init script might use translations |
|
| 131 | - if ($application !== 'core' && !str_contains($file, 'l10n')) { |
|
| 132 | - self::addTranslations($application, null, true); |
|
| 133 | - } |
|
| 134 | - |
|
| 135 | - self::$scriptsInit[] = $path; |
|
| 136 | - } |
|
| 137 | - |
|
| 138 | - /** |
|
| 139 | - * add a javascript file |
|
| 140 | - * |
|
| 141 | - * @param string $application |
|
| 142 | - * @param string|null $file |
|
| 143 | - * @param string $afterAppId |
|
| 144 | - * @param bool $prepend |
|
| 145 | - * @since 4.0.0 |
|
| 146 | - */ |
|
| 147 | - public static function addScript(string $application, ?string $file = null, string $afterAppId = 'core', bool $prepend = false): void { |
|
| 148 | - if (!empty($application)) { |
|
| 149 | - $path = "$application/js/$file"; |
|
| 150 | - } else { |
|
| 151 | - $path = "js/$file"; |
|
| 152 | - } |
|
| 153 | - |
|
| 154 | - // Inject js translations if we load a script for |
|
| 155 | - // a specific app that is not core, as those js files |
|
| 156 | - // need separate handling |
|
| 157 | - if ($application !== 'core' |
|
| 158 | - && $file !== null |
|
| 159 | - && !str_contains($file, 'l10n')) { |
|
| 160 | - self::addTranslations($application); |
|
| 161 | - } |
|
| 162 | - |
|
| 163 | - // store app in dependency list |
|
| 164 | - if (!array_key_exists($application, self::$scriptDeps)) { |
|
| 165 | - self::$scriptDeps[$application] = new AppScriptDependency($application, [$afterAppId]); |
|
| 166 | - } else { |
|
| 167 | - self::$scriptDeps[$application]->addDep($afterAppId); |
|
| 168 | - } |
|
| 169 | - |
|
| 170 | - if ($prepend) { |
|
| 171 | - array_unshift(self::$scripts[$application], $path); |
|
| 172 | - } else { |
|
| 173 | - self::$scripts[$application][] = $path; |
|
| 174 | - } |
|
| 175 | - } |
|
| 176 | - |
|
| 177 | - /** |
|
| 178 | - * Return the list of scripts injected to the page |
|
| 179 | - * |
|
| 180 | - * @return array |
|
| 181 | - * @since 24.0.0 |
|
| 182 | - */ |
|
| 183 | - public static function getScripts(): array { |
|
| 184 | - // Sort scriptDeps into sortedScriptDeps |
|
| 185 | - $scriptSort = \OCP\Server::get(AppScriptSort::class); |
|
| 186 | - $sortedScripts = $scriptSort->sort(self::$scripts, self::$scriptDeps); |
|
| 187 | - |
|
| 188 | - // Flatten array and remove duplicates |
|
| 189 | - $sortedScripts = array_merge([self::$scriptsInit], $sortedScripts); |
|
| 190 | - $sortedScripts = array_merge(...array_values($sortedScripts)); |
|
| 191 | - |
|
| 192 | - // Override core-common and core-main order |
|
| 193 | - if (in_array('core/js/main', $sortedScripts)) { |
|
| 194 | - array_unshift($sortedScripts, 'core/js/main'); |
|
| 195 | - } |
|
| 196 | - if (in_array('core/js/common', $sortedScripts)) { |
|
| 197 | - array_unshift($sortedScripts, 'core/js/common'); |
|
| 198 | - } |
|
| 199 | - |
|
| 200 | - return array_unique($sortedScripts); |
|
| 201 | - } |
|
| 202 | - |
|
| 203 | - /** |
|
| 204 | - * Add a translation JS file |
|
| 205 | - * @param string $application application id |
|
| 206 | - * @param string $languageCode language code, defaults to the current locale |
|
| 207 | - * @param bool $init whether the translations should be loaded early or not |
|
| 208 | - * @since 8.0.0 |
|
| 209 | - */ |
|
| 210 | - public static function addTranslations($application, $languageCode = null, $init = false) { |
|
| 211 | - if (is_null($languageCode)) { |
|
| 212 | - $languageCode = \OCP\Server::get(IFactory::class)->findLanguage($application); |
|
| 213 | - } |
|
| 214 | - if (!empty($application)) { |
|
| 215 | - $path = "$application/l10n/$languageCode"; |
|
| 216 | - } else { |
|
| 217 | - $path = "l10n/$languageCode"; |
|
| 218 | - } |
|
| 219 | - |
|
| 220 | - if ($init) { |
|
| 221 | - self::$scriptsInit[] = $path; |
|
| 222 | - } else { |
|
| 223 | - self::$scripts[$application][] = $path; |
|
| 224 | - } |
|
| 225 | - } |
|
| 226 | - |
|
| 227 | - /** |
|
| 228 | - * Add a custom element to the header |
|
| 229 | - * If $text is null then the element will be written as empty element. |
|
| 230 | - * So use "" to get a closing tag. |
|
| 231 | - * @param string $tag tag name of the element |
|
| 232 | - * @param array $attributes array of attributes for the element |
|
| 233 | - * @param string $text the text content for the element |
|
| 234 | - * @since 4.0.0 |
|
| 235 | - */ |
|
| 236 | - public static function addHeader($tag, $attributes, $text = null) { |
|
| 237 | - \OC_Util::addHeader($tag, $attributes, $text); |
|
| 238 | - } |
|
| 239 | - |
|
| 240 | - /** |
|
| 241 | - * Creates an absolute url to the given app and file. |
|
| 242 | - * @param string $app app |
|
| 243 | - * @param string $file file |
|
| 244 | - * @param array $args array with param=>value, will be appended to the returned url |
|
| 245 | - * The value of $args will be urlencoded |
|
| 246 | - * @return string the url |
|
| 247 | - * @since 4.0.0 - parameter $args was added in 4.5.0 |
|
| 248 | - */ |
|
| 249 | - public static function linkToAbsolute($app, $file, $args = []) { |
|
| 250 | - $urlGenerator = \OCP\Server::get(IURLGenerator::class); |
|
| 251 | - return $urlGenerator->getAbsoluteURL( |
|
| 252 | - $urlGenerator->linkTo($app, $file, $args) |
|
| 253 | - ); |
|
| 254 | - } |
|
| 255 | - |
|
| 256 | - /** |
|
| 257 | - * Creates an absolute url for remote use. |
|
| 258 | - * @param string $service id |
|
| 259 | - * @return string the url |
|
| 260 | - * @since 4.0.0 |
|
| 261 | - */ |
|
| 262 | - public static function linkToRemote($service) { |
|
| 263 | - $urlGenerator = \OCP\Server::get(IURLGenerator::class); |
|
| 264 | - $remoteBase = $urlGenerator->linkTo('', 'remote.php') . '/' . $service; |
|
| 265 | - return $urlGenerator->getAbsoluteURL( |
|
| 266 | - $remoteBase . (($service[strlen($service) - 1] != '/') ? '/' : '') |
|
| 267 | - ); |
|
| 268 | - } |
|
| 269 | - |
|
| 270 | - /** |
|
| 271 | - * Returns the server host name without an eventual port number |
|
| 272 | - * @return string the server hostname |
|
| 273 | - * @since 5.0.0 |
|
| 274 | - */ |
|
| 275 | - public static function getServerHostName() { |
|
| 276 | - $host_name = \OCP\Server::get(IRequest::class)->getServerHost(); |
|
| 277 | - // strip away port number (if existing) |
|
| 278 | - $colon_pos = strpos($host_name, ':'); |
|
| 279 | - if ($colon_pos != false) { |
|
| 280 | - $host_name = substr($host_name, 0, $colon_pos); |
|
| 281 | - } |
|
| 282 | - return $host_name; |
|
| 283 | - } |
|
| 284 | - |
|
| 285 | - /** |
|
| 286 | - * Returns the default email address |
|
| 287 | - * @param string $user_part the user part of the address |
|
| 288 | - * @return string the default email address |
|
| 289 | - * |
|
| 290 | - * Assembles a default email address (using the server hostname |
|
| 291 | - * and the given user part, and returns it |
|
| 292 | - * Example: when given lostpassword-noreply as $user_part param, |
|
| 293 | - * and is currently accessed via http(s)://example.com/, |
|
| 294 | - * it would return '[email protected]' |
|
| 295 | - * |
|
| 296 | - * If the configuration value 'mail_from_address' is set in |
|
| 297 | - * config.php, this value will override the $user_part that |
|
| 298 | - * is passed to this function |
|
| 299 | - * @since 5.0.0 |
|
| 300 | - */ |
|
| 301 | - public static function getDefaultEmailAddress(string $user_part): string { |
|
| 302 | - $config = \OCP\Server::get(IConfig::class); |
|
| 303 | - $user_part = $config->getSystemValueString('mail_from_address', $user_part); |
|
| 304 | - $host_name = self::getServerHostName(); |
|
| 305 | - $host_name = $config->getSystemValueString('mail_domain', $host_name); |
|
| 306 | - $defaultEmailAddress = $user_part . '@' . $host_name; |
|
| 307 | - |
|
| 308 | - $emailValidator = \OCP\Server::get(IEmailValidator::class); |
|
| 309 | - if ($emailValidator->isValid($defaultEmailAddress)) { |
|
| 310 | - return $defaultEmailAddress; |
|
| 311 | - } |
|
| 312 | - |
|
| 313 | - // in case we cannot build a valid email address from the hostname let's fallback to 'localhost.localdomain' |
|
| 314 | - return $user_part . '@localhost.localdomain'; |
|
| 315 | - } |
|
| 316 | - |
|
| 317 | - /** |
|
| 318 | - * Converts string to int of float depending on if it fits an int |
|
| 319 | - * @param numeric-string|float|int $number numeric string |
|
| 320 | - * @return int|float int if it fits, float if it is too big |
|
| 321 | - * @since 26.0.0 |
|
| 322 | - */ |
|
| 323 | - public static function numericToNumber(string|float|int $number): int|float { |
|
| 324 | - /* This is a hack to cast to (int|float) */ |
|
| 325 | - return 0 + (string)$number; |
|
| 326 | - } |
|
| 327 | - |
|
| 328 | - /** |
|
| 329 | - * Make a human file size (2048 to 2 kB) |
|
| 330 | - * @param int|float $bytes file size in bytes |
|
| 331 | - * @return string a human readable file size |
|
| 332 | - * @since 4.0.0 |
|
| 333 | - */ |
|
| 334 | - public static function humanFileSize(int|float $bytes): string { |
|
| 335 | - if ($bytes < 0) { |
|
| 336 | - return '?'; |
|
| 337 | - } |
|
| 338 | - if ($bytes < 1024) { |
|
| 339 | - return "$bytes B"; |
|
| 340 | - } |
|
| 341 | - $bytes = round($bytes / 1024, 0); |
|
| 342 | - if ($bytes < 1024) { |
|
| 343 | - return "$bytes KB"; |
|
| 344 | - } |
|
| 345 | - $bytes = round($bytes / 1024, 1); |
|
| 346 | - if ($bytes < 1024) { |
|
| 347 | - return "$bytes MB"; |
|
| 348 | - } |
|
| 349 | - $bytes = round($bytes / 1024, 1); |
|
| 350 | - if ($bytes < 1024) { |
|
| 351 | - return "$bytes GB"; |
|
| 352 | - } |
|
| 353 | - $bytes = round($bytes / 1024, 1); |
|
| 354 | - if ($bytes < 1024) { |
|
| 355 | - return "$bytes TB"; |
|
| 356 | - } |
|
| 357 | - |
|
| 358 | - $bytes = round($bytes / 1024, 1); |
|
| 359 | - return "$bytes PB"; |
|
| 360 | - } |
|
| 361 | - |
|
| 362 | - /** |
|
| 363 | - * Make a computer file size (2 kB to 2048) |
|
| 364 | - * Inspired by: https://www.php.net/manual/en/function.filesize.php#92418 |
|
| 365 | - * |
|
| 366 | - * @param string $str file size in a fancy format |
|
| 367 | - * @return false|int|float a file size in bytes |
|
| 368 | - * @since 4.0.0 |
|
| 369 | - */ |
|
| 370 | - public static function computerFileSize(string $str): false|int|float { |
|
| 371 | - $str = strtolower($str); |
|
| 372 | - if (is_numeric($str)) { |
|
| 373 | - return Util::numericToNumber($str); |
|
| 374 | - } |
|
| 375 | - |
|
| 376 | - $bytes_array = [ |
|
| 377 | - 'b' => 1, |
|
| 378 | - 'k' => 1024, |
|
| 379 | - 'kb' => 1024, |
|
| 380 | - 'mb' => 1024 * 1024, |
|
| 381 | - 'm' => 1024 * 1024, |
|
| 382 | - 'gb' => 1024 * 1024 * 1024, |
|
| 383 | - 'g' => 1024 * 1024 * 1024, |
|
| 384 | - 'tb' => 1024 * 1024 * 1024 * 1024, |
|
| 385 | - 't' => 1024 * 1024 * 1024 * 1024, |
|
| 386 | - 'pb' => 1024 * 1024 * 1024 * 1024 * 1024, |
|
| 387 | - 'p' => 1024 * 1024 * 1024 * 1024 * 1024, |
|
| 388 | - ]; |
|
| 389 | - |
|
| 390 | - $bytes = (float)$str; |
|
| 391 | - |
|
| 392 | - if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && isset($bytes_array[$matches[1]])) { |
|
| 393 | - $bytes *= $bytes_array[$matches[1]]; |
|
| 394 | - } else { |
|
| 395 | - return false; |
|
| 396 | - } |
|
| 397 | - |
|
| 398 | - return Util::numericToNumber(round($bytes)); |
|
| 399 | - } |
|
| 400 | - |
|
| 401 | - /** |
|
| 402 | - * connects a function to a hook |
|
| 403 | - * |
|
| 404 | - * @param string $signalClass class name of emitter |
|
| 405 | - * @param string $signalName name of signal |
|
| 406 | - * @param string|object $slotClass class name of slot |
|
| 407 | - * @param string $slotName name of slot |
|
| 408 | - * @return bool |
|
| 409 | - * |
|
| 410 | - * This function makes it very easy to connect to use hooks. |
|
| 411 | - * |
|
| 412 | - * TODO: write example |
|
| 413 | - * @since 4.0.0 |
|
| 414 | - * @deprecated 21.0.0 use \OCP\EventDispatcher\IEventDispatcher::addListener |
|
| 415 | - */ |
|
| 416 | - public static function connectHook($signalClass, $signalName, $slotClass, $slotName) { |
|
| 417 | - return \OC_Hook::connect($signalClass, $signalName, $slotClass, $slotName); |
|
| 418 | - } |
|
| 419 | - |
|
| 420 | - /** |
|
| 421 | - * Emits a signal. To get data from the slot use references! |
|
| 422 | - * @param string $signalclass class name of emitter |
|
| 423 | - * @param string $signalname name of signal |
|
| 424 | - * @param array $params default: array() array with additional data |
|
| 425 | - * @return bool true if slots exists or false if not |
|
| 426 | - * |
|
| 427 | - * TODO: write example |
|
| 428 | - * @since 4.0.0 |
|
| 429 | - * @deprecated 21.0.0 use \OCP\EventDispatcher\IEventDispatcher::dispatchTypedEvent |
|
| 430 | - */ |
|
| 431 | - public static function emitHook($signalclass, $signalname, $params = []) { |
|
| 432 | - return \OC_Hook::emit($signalclass, $signalname, $params); |
|
| 433 | - } |
|
| 434 | - |
|
| 435 | - /** |
|
| 436 | - * Cached encrypted CSRF token. Some static unit-tests of ownCloud compare |
|
| 437 | - * multiple Template elements which invoke `callRegister`. If the value |
|
| 438 | - * would not be cached these unit-tests would fail. |
|
| 439 | - * @var string |
|
| 440 | - */ |
|
| 441 | - private static $token = ''; |
|
| 442 | - |
|
| 443 | - /** |
|
| 444 | - * Register an get/post call. This is important to prevent CSRF attacks |
|
| 445 | - * @since 4.5.0 |
|
| 446 | - * @deprecated 32.0.0 directly use CsrfTokenManager instead |
|
| 447 | - */ |
|
| 448 | - public static function callRegister() { |
|
| 449 | - if (self::$token === '') { |
|
| 450 | - self::$token = \OCP\Server::get(CsrfTokenManager::class)->getToken()->getEncryptedValue(); |
|
| 451 | - } |
|
| 452 | - return self::$token; |
|
| 453 | - } |
|
| 454 | - |
|
| 455 | - /** |
|
| 456 | - * Used to sanitize HTML |
|
| 457 | - * |
|
| 458 | - * This function is used to sanitize HTML and should be applied on any |
|
| 459 | - * string or array of strings before displaying it on a web page. |
|
| 460 | - * |
|
| 461 | - * @param string|string[] $value |
|
| 462 | - * @return ($value is array ? string[] : string) an array of sanitized strings or a single sanitized string, depends on the input parameter. |
|
| 463 | - * @since 4.5.0 |
|
| 464 | - */ |
|
| 465 | - public static function sanitizeHTML($value) { |
|
| 466 | - return \OC_Util::sanitizeHTML($value); |
|
| 467 | - } |
|
| 468 | - |
|
| 469 | - /** |
|
| 470 | - * Public function to encode url parameters |
|
| 471 | - * |
|
| 472 | - * This function is used to encode path to file before output. |
|
| 473 | - * Encoding is done according to RFC 3986 with one exception: |
|
| 474 | - * Character '/' is preserved as is. |
|
| 475 | - * |
|
| 476 | - * @param string $component part of URI to encode |
|
| 477 | - * @return string |
|
| 478 | - * @since 6.0.0 |
|
| 479 | - */ |
|
| 480 | - public static function encodePath($component) { |
|
| 481 | - return \OC_Util::encodePath($component); |
|
| 482 | - } |
|
| 483 | - |
|
| 484 | - /** |
|
| 485 | - * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is. |
|
| 486 | - * |
|
| 487 | - * @param array $input The array to work on |
|
| 488 | - * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default) |
|
| 489 | - * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 |
|
| 490 | - * @return array |
|
| 491 | - * @since 4.5.0 |
|
| 492 | - */ |
|
| 493 | - public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') { |
|
| 494 | - $case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER; |
|
| 495 | - $ret = []; |
|
| 496 | - foreach ($input as $k => $v) { |
|
| 497 | - $ret[mb_convert_case($k, $case, $encoding)] = $v; |
|
| 498 | - } |
|
| 499 | - return $ret; |
|
| 500 | - } |
|
| 501 | - |
|
| 502 | - /** |
|
| 503 | - * performs a search in a nested array |
|
| 504 | - * |
|
| 505 | - * @param array $haystack the array to be searched |
|
| 506 | - * @param string $needle the search string |
|
| 507 | - * @param mixed $index optional, only search this key name |
|
| 508 | - * @return mixed the key of the matching field, otherwise false |
|
| 509 | - * @since 4.5.0 |
|
| 510 | - * @deprecated 15.0.0 |
|
| 511 | - */ |
|
| 512 | - public static function recursiveArraySearch($haystack, $needle, $index = null) { |
|
| 513 | - $aIt = new \RecursiveArrayIterator($haystack); |
|
| 514 | - $it = new \RecursiveIteratorIterator($aIt); |
|
| 515 | - |
|
| 516 | - while ($it->valid()) { |
|
| 517 | - if (((isset($index) && ($it->key() == $index)) || !isset($index)) && ($it->current() == $needle)) { |
|
| 518 | - return $aIt->key(); |
|
| 519 | - } |
|
| 520 | - |
|
| 521 | - $it->next(); |
|
| 522 | - } |
|
| 523 | - |
|
| 524 | - return false; |
|
| 525 | - } |
|
| 526 | - |
|
| 527 | - /** |
|
| 528 | - * calculates the maximum upload size respecting system settings, free space and user quota |
|
| 529 | - * |
|
| 530 | - * @param string $dir the current folder where the user currently operates |
|
| 531 | - * @param int|float|null $free the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly |
|
| 532 | - * @return int|float number of bytes representing |
|
| 533 | - * @since 5.0.0 |
|
| 534 | - */ |
|
| 535 | - public static function maxUploadFilesize(string $dir, int|float|null $free = null): int|float { |
|
| 536 | - if (is_null($free) || $free < 0) { |
|
| 537 | - $free = self::freeSpace($dir); |
|
| 538 | - } |
|
| 539 | - return min($free, self::uploadLimit()); |
|
| 540 | - } |
|
| 541 | - |
|
| 542 | - /** |
|
| 543 | - * Calculate free space left within user quota |
|
| 544 | - * @param string $dir the current folder where the user currently operates |
|
| 545 | - * @return int|float number of bytes representing |
|
| 546 | - * @since 7.0.0 |
|
| 547 | - */ |
|
| 548 | - public static function freeSpace(string $dir): int|float { |
|
| 549 | - $freeSpace = \OC\Files\Filesystem::free_space($dir); |
|
| 550 | - if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) { |
|
| 551 | - $freeSpace = max($freeSpace, 0); |
|
| 552 | - return $freeSpace; |
|
| 553 | - } else { |
|
| 554 | - return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188 |
|
| 555 | - } |
|
| 556 | - } |
|
| 557 | - |
|
| 558 | - /** |
|
| 559 | - * Calculate PHP upload limit |
|
| 560 | - * |
|
| 561 | - * @return int|float number of bytes representing |
|
| 562 | - * @since 7.0.0 |
|
| 563 | - */ |
|
| 564 | - public static function uploadLimit(): int|float { |
|
| 565 | - $ini = Server::get(IniGetWrapper::class); |
|
| 566 | - $upload_max_filesize = self::computerFileSize($ini->get('upload_max_filesize')) ?: 0; |
|
| 567 | - $post_max_size = self::computerFileSize($ini->get('post_max_size')) ?: 0; |
|
| 568 | - if ($upload_max_filesize === 0 && $post_max_size === 0) { |
|
| 569 | - return INF; |
|
| 570 | - } elseif ($upload_max_filesize === 0 || $post_max_size === 0) { |
|
| 571 | - return max($upload_max_filesize, $post_max_size); //only the non 0 value counts |
|
| 572 | - } else { |
|
| 573 | - return min($upload_max_filesize, $post_max_size); |
|
| 574 | - } |
|
| 575 | - } |
|
| 576 | - |
|
| 577 | - /** |
|
| 578 | - * Compare two strings to provide a natural sort |
|
| 579 | - * @param string $a first string to compare |
|
| 580 | - * @param string $b second string to compare |
|
| 581 | - * @return int -1 if $b comes before $a, 1 if $a comes before $b |
|
| 582 | - * or 0 if the strings are identical |
|
| 583 | - * @since 7.0.0 |
|
| 584 | - */ |
|
| 585 | - public static function naturalSortCompare($a, $b) { |
|
| 586 | - return \OC\NaturalSort::getInstance()->compare($a, $b); |
|
| 587 | - } |
|
| 588 | - |
|
| 589 | - /** |
|
| 590 | - * Check if a password is required for each public link |
|
| 591 | - * |
|
| 592 | - * @param bool $checkGroupMembership Check group membership exclusion |
|
| 593 | - * @return boolean |
|
| 594 | - * @since 7.0.0 |
|
| 595 | - */ |
|
| 596 | - public static function isPublicLinkPasswordRequired(bool $checkGroupMembership = true) { |
|
| 597 | - return \OC_Util::isPublicLinkPasswordRequired($checkGroupMembership); |
|
| 598 | - } |
|
| 599 | - |
|
| 600 | - /** |
|
| 601 | - * check if share API enforces a default expire date |
|
| 602 | - * @return boolean |
|
| 603 | - * @since 8.0.0 |
|
| 604 | - */ |
|
| 605 | - public static function isDefaultExpireDateEnforced() { |
|
| 606 | - return \OC_Util::isDefaultExpireDateEnforced(); |
|
| 607 | - } |
|
| 608 | - |
|
| 609 | - protected static $needUpgradeCache = null; |
|
| 610 | - |
|
| 611 | - /** |
|
| 612 | - * Checks whether the current version needs upgrade. |
|
| 613 | - * |
|
| 614 | - * @return bool true if upgrade is needed, false otherwise |
|
| 615 | - * @since 7.0.0 |
|
| 616 | - */ |
|
| 617 | - public static function needUpgrade() { |
|
| 618 | - if (!isset(self::$needUpgradeCache)) { |
|
| 619 | - self::$needUpgradeCache = \OC_Util::needUpgrade(\OCP\Server::get(\OC\SystemConfig::class)); |
|
| 620 | - } |
|
| 621 | - return self::$needUpgradeCache; |
|
| 622 | - } |
|
| 623 | - |
|
| 624 | - /** |
|
| 625 | - * Sometimes a string has to be shortened to fit within a certain maximum |
|
| 626 | - * data length in bytes. substr() you may break multibyte characters, |
|
| 627 | - * because it operates on single byte level. mb_substr() operates on |
|
| 628 | - * characters, so does not ensure that the shortened string satisfies the |
|
| 629 | - * max length in bytes. |
|
| 630 | - * |
|
| 631 | - * For example, json_encode is messing with multibyte characters a lot, |
|
| 632 | - * replacing them with something along "\u1234". |
|
| 633 | - * |
|
| 634 | - * This function shortens the string with by $accuracy (-5) from |
|
| 635 | - * $dataLength characters, until it fits within $dataLength bytes. |
|
| 636 | - * |
|
| 637 | - * @since 23.0.0 |
|
| 638 | - */ |
|
| 639 | - public static function shortenMultibyteString(string $subject, int $dataLength, int $accuracy = 5): string { |
|
| 640 | - $temp = mb_substr($subject, 0, $dataLength); |
|
| 641 | - // json encodes encapsulates the string in double quotes, they need to be substracted |
|
| 642 | - while ((strlen(json_encode($temp)) - 2) > $dataLength) { |
|
| 643 | - $temp = mb_substr($temp, 0, -$accuracy); |
|
| 644 | - } |
|
| 645 | - return $temp; |
|
| 646 | - } |
|
| 647 | - |
|
| 648 | - /** |
|
| 649 | - * Check if a function is enabled in the php configuration |
|
| 650 | - * |
|
| 651 | - * @since 25.0.0 |
|
| 652 | - */ |
|
| 653 | - public static function isFunctionEnabled(string $functionName): bool { |
|
| 654 | - if (!function_exists($functionName)) { |
|
| 655 | - return false; |
|
| 656 | - } |
|
| 657 | - $ini = Server::get(IniGetWrapper::class); |
|
| 658 | - $disabled = explode(',', $ini->get('disable_functions') ?: ''); |
|
| 659 | - $disabled = array_map('trim', $disabled); |
|
| 660 | - if (in_array($functionName, $disabled)) { |
|
| 661 | - return false; |
|
| 662 | - } |
|
| 663 | - return true; |
|
| 664 | - } |
|
| 28 | + private static ?IManager $shareManager = null; |
|
| 29 | + |
|
| 30 | + private static array $scriptsInit = []; |
|
| 31 | + private static array $scripts = []; |
|
| 32 | + private static array $scriptDeps = []; |
|
| 33 | + |
|
| 34 | + /** |
|
| 35 | + * get the current installed version of Nextcloud |
|
| 36 | + * @return array |
|
| 37 | + * @since 4.0.0 |
|
| 38 | + * @deprecated 31.0.0 Use \OCP\ServerVersion::getVersion |
|
| 39 | + */ |
|
| 40 | + public static function getVersion() { |
|
| 41 | + return Server::get(ServerVersion::class)->getVersion(); |
|
| 42 | + } |
|
| 43 | + |
|
| 44 | + /** |
|
| 45 | + * @since 17.0.0 |
|
| 46 | + */ |
|
| 47 | + public static function hasExtendedSupport(): bool { |
|
| 48 | + try { |
|
| 49 | + /** @var \OCP\Support\Subscription\IRegistry */ |
|
| 50 | + $subscriptionRegistry = Server::get(\OCP\Support\Subscription\IRegistry::class); |
|
| 51 | + return $subscriptionRegistry->delegateHasExtendedSupport(); |
|
| 52 | + } catch (ContainerExceptionInterface $e) { |
|
| 53 | + } |
|
| 54 | + return \OCP\Server::get(IConfig::class)->getSystemValueBool('extendedSupport', false); |
|
| 55 | + } |
|
| 56 | + |
|
| 57 | + /** |
|
| 58 | + * Set current update channel |
|
| 59 | + * @param string $channel |
|
| 60 | + * @since 8.1.0 |
|
| 61 | + */ |
|
| 62 | + public static function setChannel($channel) { |
|
| 63 | + \OCP\Server::get(IConfig::class)->setSystemValue('updater.release.channel', $channel); |
|
| 64 | + } |
|
| 65 | + |
|
| 66 | + /** |
|
| 67 | + * Get current update channel |
|
| 68 | + * @return string |
|
| 69 | + * @since 8.1.0 |
|
| 70 | + * @deprecated 31.0.0 Use \OCP\ServerVersion::getChannel |
|
| 71 | + */ |
|
| 72 | + public static function getChannel() { |
|
| 73 | + return \OCP\Server::get(ServerVersion::class)->getChannel(); |
|
| 74 | + } |
|
| 75 | + |
|
| 76 | + /** |
|
| 77 | + * check if sharing is disabled for the current user |
|
| 78 | + * |
|
| 79 | + * @return boolean |
|
| 80 | + * @since 7.0.0 |
|
| 81 | + * @deprecated 9.1.0 Use Server::get(\OCP\Share\IManager::class)->sharingDisabledForUser |
|
| 82 | + */ |
|
| 83 | + public static function isSharingDisabledForUser() { |
|
| 84 | + if (self::$shareManager === null) { |
|
| 85 | + self::$shareManager = Server::get(IManager::class); |
|
| 86 | + } |
|
| 87 | + |
|
| 88 | + $user = Server::get(\OCP\IUserSession::class)->getUser(); |
|
| 89 | + |
|
| 90 | + return self::$shareManager->sharingDisabledForUser($user?->getUID()); |
|
| 91 | + } |
|
| 92 | + |
|
| 93 | + /** |
|
| 94 | + * get l10n object |
|
| 95 | + * @since 6.0.0 - parameter $language was added in 8.0.0 |
|
| 96 | + */ |
|
| 97 | + public static function getL10N(string $application, ?string $language = null): IL10N { |
|
| 98 | + return Server::get(\OCP\L10N\IFactory::class)->get($application, $language); |
|
| 99 | + } |
|
| 100 | + |
|
| 101 | + /** |
|
| 102 | + * Add a css file |
|
| 103 | + * |
|
| 104 | + * @param string $application application id |
|
| 105 | + * @param ?string $file filename |
|
| 106 | + * @param bool $prepend prepend the style to the beginning of the list |
|
| 107 | + * @since 4.0.0 |
|
| 108 | + */ |
|
| 109 | + public static function addStyle(string $application, ?string $file = null, bool $prepend = false): void { |
|
| 110 | + \OC_Util::addStyle($application, $file, $prepend); |
|
| 111 | + } |
|
| 112 | + |
|
| 113 | + /** |
|
| 114 | + * Add a standalone init js file that is loaded for initialization |
|
| 115 | + * |
|
| 116 | + * Be careful loading scripts using this method as they are loaded early |
|
| 117 | + * and block the initial page rendering. They should not have dependencies |
|
| 118 | + * on any other scripts than core-common and core-main. |
|
| 119 | + * |
|
| 120 | + * @since 28.0.0 |
|
| 121 | + */ |
|
| 122 | + public static function addInitScript(string $application, string $file): void { |
|
| 123 | + if (!empty($application)) { |
|
| 124 | + $path = "$application/js/$file"; |
|
| 125 | + } else { |
|
| 126 | + $path = "js/$file"; |
|
| 127 | + } |
|
| 128 | + |
|
| 129 | + // We need to handle the translation BEFORE the init script |
|
| 130 | + // is loaded, as the init script might use translations |
|
| 131 | + if ($application !== 'core' && !str_contains($file, 'l10n')) { |
|
| 132 | + self::addTranslations($application, null, true); |
|
| 133 | + } |
|
| 134 | + |
|
| 135 | + self::$scriptsInit[] = $path; |
|
| 136 | + } |
|
| 137 | + |
|
| 138 | + /** |
|
| 139 | + * add a javascript file |
|
| 140 | + * |
|
| 141 | + * @param string $application |
|
| 142 | + * @param string|null $file |
|
| 143 | + * @param string $afterAppId |
|
| 144 | + * @param bool $prepend |
|
| 145 | + * @since 4.0.0 |
|
| 146 | + */ |
|
| 147 | + public static function addScript(string $application, ?string $file = null, string $afterAppId = 'core', bool $prepend = false): void { |
|
| 148 | + if (!empty($application)) { |
|
| 149 | + $path = "$application/js/$file"; |
|
| 150 | + } else { |
|
| 151 | + $path = "js/$file"; |
|
| 152 | + } |
|
| 153 | + |
|
| 154 | + // Inject js translations if we load a script for |
|
| 155 | + // a specific app that is not core, as those js files |
|
| 156 | + // need separate handling |
|
| 157 | + if ($application !== 'core' |
|
| 158 | + && $file !== null |
|
| 159 | + && !str_contains($file, 'l10n')) { |
|
| 160 | + self::addTranslations($application); |
|
| 161 | + } |
|
| 162 | + |
|
| 163 | + // store app in dependency list |
|
| 164 | + if (!array_key_exists($application, self::$scriptDeps)) { |
|
| 165 | + self::$scriptDeps[$application] = new AppScriptDependency($application, [$afterAppId]); |
|
| 166 | + } else { |
|
| 167 | + self::$scriptDeps[$application]->addDep($afterAppId); |
|
| 168 | + } |
|
| 169 | + |
|
| 170 | + if ($prepend) { |
|
| 171 | + array_unshift(self::$scripts[$application], $path); |
|
| 172 | + } else { |
|
| 173 | + self::$scripts[$application][] = $path; |
|
| 174 | + } |
|
| 175 | + } |
|
| 176 | + |
|
| 177 | + /** |
|
| 178 | + * Return the list of scripts injected to the page |
|
| 179 | + * |
|
| 180 | + * @return array |
|
| 181 | + * @since 24.0.0 |
|
| 182 | + */ |
|
| 183 | + public static function getScripts(): array { |
|
| 184 | + // Sort scriptDeps into sortedScriptDeps |
|
| 185 | + $scriptSort = \OCP\Server::get(AppScriptSort::class); |
|
| 186 | + $sortedScripts = $scriptSort->sort(self::$scripts, self::$scriptDeps); |
|
| 187 | + |
|
| 188 | + // Flatten array and remove duplicates |
|
| 189 | + $sortedScripts = array_merge([self::$scriptsInit], $sortedScripts); |
|
| 190 | + $sortedScripts = array_merge(...array_values($sortedScripts)); |
|
| 191 | + |
|
| 192 | + // Override core-common and core-main order |
|
| 193 | + if (in_array('core/js/main', $sortedScripts)) { |
|
| 194 | + array_unshift($sortedScripts, 'core/js/main'); |
|
| 195 | + } |
|
| 196 | + if (in_array('core/js/common', $sortedScripts)) { |
|
| 197 | + array_unshift($sortedScripts, 'core/js/common'); |
|
| 198 | + } |
|
| 199 | + |
|
| 200 | + return array_unique($sortedScripts); |
|
| 201 | + } |
|
| 202 | + |
|
| 203 | + /** |
|
| 204 | + * Add a translation JS file |
|
| 205 | + * @param string $application application id |
|
| 206 | + * @param string $languageCode language code, defaults to the current locale |
|
| 207 | + * @param bool $init whether the translations should be loaded early or not |
|
| 208 | + * @since 8.0.0 |
|
| 209 | + */ |
|
| 210 | + public static function addTranslations($application, $languageCode = null, $init = false) { |
|
| 211 | + if (is_null($languageCode)) { |
|
| 212 | + $languageCode = \OCP\Server::get(IFactory::class)->findLanguage($application); |
|
| 213 | + } |
|
| 214 | + if (!empty($application)) { |
|
| 215 | + $path = "$application/l10n/$languageCode"; |
|
| 216 | + } else { |
|
| 217 | + $path = "l10n/$languageCode"; |
|
| 218 | + } |
|
| 219 | + |
|
| 220 | + if ($init) { |
|
| 221 | + self::$scriptsInit[] = $path; |
|
| 222 | + } else { |
|
| 223 | + self::$scripts[$application][] = $path; |
|
| 224 | + } |
|
| 225 | + } |
|
| 226 | + |
|
| 227 | + /** |
|
| 228 | + * Add a custom element to the header |
|
| 229 | + * If $text is null then the element will be written as empty element. |
|
| 230 | + * So use "" to get a closing tag. |
|
| 231 | + * @param string $tag tag name of the element |
|
| 232 | + * @param array $attributes array of attributes for the element |
|
| 233 | + * @param string $text the text content for the element |
|
| 234 | + * @since 4.0.0 |
|
| 235 | + */ |
|
| 236 | + public static function addHeader($tag, $attributes, $text = null) { |
|
| 237 | + \OC_Util::addHeader($tag, $attributes, $text); |
|
| 238 | + } |
|
| 239 | + |
|
| 240 | + /** |
|
| 241 | + * Creates an absolute url to the given app and file. |
|
| 242 | + * @param string $app app |
|
| 243 | + * @param string $file file |
|
| 244 | + * @param array $args array with param=>value, will be appended to the returned url |
|
| 245 | + * The value of $args will be urlencoded |
|
| 246 | + * @return string the url |
|
| 247 | + * @since 4.0.0 - parameter $args was added in 4.5.0 |
|
| 248 | + */ |
|
| 249 | + public static function linkToAbsolute($app, $file, $args = []) { |
|
| 250 | + $urlGenerator = \OCP\Server::get(IURLGenerator::class); |
|
| 251 | + return $urlGenerator->getAbsoluteURL( |
|
| 252 | + $urlGenerator->linkTo($app, $file, $args) |
|
| 253 | + ); |
|
| 254 | + } |
|
| 255 | + |
|
| 256 | + /** |
|
| 257 | + * Creates an absolute url for remote use. |
|
| 258 | + * @param string $service id |
|
| 259 | + * @return string the url |
|
| 260 | + * @since 4.0.0 |
|
| 261 | + */ |
|
| 262 | + public static function linkToRemote($service) { |
|
| 263 | + $urlGenerator = \OCP\Server::get(IURLGenerator::class); |
|
| 264 | + $remoteBase = $urlGenerator->linkTo('', 'remote.php') . '/' . $service; |
|
| 265 | + return $urlGenerator->getAbsoluteURL( |
|
| 266 | + $remoteBase . (($service[strlen($service) - 1] != '/') ? '/' : '') |
|
| 267 | + ); |
|
| 268 | + } |
|
| 269 | + |
|
| 270 | + /** |
|
| 271 | + * Returns the server host name without an eventual port number |
|
| 272 | + * @return string the server hostname |
|
| 273 | + * @since 5.0.0 |
|
| 274 | + */ |
|
| 275 | + public static function getServerHostName() { |
|
| 276 | + $host_name = \OCP\Server::get(IRequest::class)->getServerHost(); |
|
| 277 | + // strip away port number (if existing) |
|
| 278 | + $colon_pos = strpos($host_name, ':'); |
|
| 279 | + if ($colon_pos != false) { |
|
| 280 | + $host_name = substr($host_name, 0, $colon_pos); |
|
| 281 | + } |
|
| 282 | + return $host_name; |
|
| 283 | + } |
|
| 284 | + |
|
| 285 | + /** |
|
| 286 | + * Returns the default email address |
|
| 287 | + * @param string $user_part the user part of the address |
|
| 288 | + * @return string the default email address |
|
| 289 | + * |
|
| 290 | + * Assembles a default email address (using the server hostname |
|
| 291 | + * and the given user part, and returns it |
|
| 292 | + * Example: when given lostpassword-noreply as $user_part param, |
|
| 293 | + * and is currently accessed via http(s)://example.com/, |
|
| 294 | + * it would return '[email protected]' |
|
| 295 | + * |
|
| 296 | + * If the configuration value 'mail_from_address' is set in |
|
| 297 | + * config.php, this value will override the $user_part that |
|
| 298 | + * is passed to this function |
|
| 299 | + * @since 5.0.0 |
|
| 300 | + */ |
|
| 301 | + public static function getDefaultEmailAddress(string $user_part): string { |
|
| 302 | + $config = \OCP\Server::get(IConfig::class); |
|
| 303 | + $user_part = $config->getSystemValueString('mail_from_address', $user_part); |
|
| 304 | + $host_name = self::getServerHostName(); |
|
| 305 | + $host_name = $config->getSystemValueString('mail_domain', $host_name); |
|
| 306 | + $defaultEmailAddress = $user_part . '@' . $host_name; |
|
| 307 | + |
|
| 308 | + $emailValidator = \OCP\Server::get(IEmailValidator::class); |
|
| 309 | + if ($emailValidator->isValid($defaultEmailAddress)) { |
|
| 310 | + return $defaultEmailAddress; |
|
| 311 | + } |
|
| 312 | + |
|
| 313 | + // in case we cannot build a valid email address from the hostname let's fallback to 'localhost.localdomain' |
|
| 314 | + return $user_part . '@localhost.localdomain'; |
|
| 315 | + } |
|
| 316 | + |
|
| 317 | + /** |
|
| 318 | + * Converts string to int of float depending on if it fits an int |
|
| 319 | + * @param numeric-string|float|int $number numeric string |
|
| 320 | + * @return int|float int if it fits, float if it is too big |
|
| 321 | + * @since 26.0.0 |
|
| 322 | + */ |
|
| 323 | + public static function numericToNumber(string|float|int $number): int|float { |
|
| 324 | + /* This is a hack to cast to (int|float) */ |
|
| 325 | + return 0 + (string)$number; |
|
| 326 | + } |
|
| 327 | + |
|
| 328 | + /** |
|
| 329 | + * Make a human file size (2048 to 2 kB) |
|
| 330 | + * @param int|float $bytes file size in bytes |
|
| 331 | + * @return string a human readable file size |
|
| 332 | + * @since 4.0.0 |
|
| 333 | + */ |
|
| 334 | + public static function humanFileSize(int|float $bytes): string { |
|
| 335 | + if ($bytes < 0) { |
|
| 336 | + return '?'; |
|
| 337 | + } |
|
| 338 | + if ($bytes < 1024) { |
|
| 339 | + return "$bytes B"; |
|
| 340 | + } |
|
| 341 | + $bytes = round($bytes / 1024, 0); |
|
| 342 | + if ($bytes < 1024) { |
|
| 343 | + return "$bytes KB"; |
|
| 344 | + } |
|
| 345 | + $bytes = round($bytes / 1024, 1); |
|
| 346 | + if ($bytes < 1024) { |
|
| 347 | + return "$bytes MB"; |
|
| 348 | + } |
|
| 349 | + $bytes = round($bytes / 1024, 1); |
|
| 350 | + if ($bytes < 1024) { |
|
| 351 | + return "$bytes GB"; |
|
| 352 | + } |
|
| 353 | + $bytes = round($bytes / 1024, 1); |
|
| 354 | + if ($bytes < 1024) { |
|
| 355 | + return "$bytes TB"; |
|
| 356 | + } |
|
| 357 | + |
|
| 358 | + $bytes = round($bytes / 1024, 1); |
|
| 359 | + return "$bytes PB"; |
|
| 360 | + } |
|
| 361 | + |
|
| 362 | + /** |
|
| 363 | + * Make a computer file size (2 kB to 2048) |
|
| 364 | + * Inspired by: https://www.php.net/manual/en/function.filesize.php#92418 |
|
| 365 | + * |
|
| 366 | + * @param string $str file size in a fancy format |
|
| 367 | + * @return false|int|float a file size in bytes |
|
| 368 | + * @since 4.0.0 |
|
| 369 | + */ |
|
| 370 | + public static function computerFileSize(string $str): false|int|float { |
|
| 371 | + $str = strtolower($str); |
|
| 372 | + if (is_numeric($str)) { |
|
| 373 | + return Util::numericToNumber($str); |
|
| 374 | + } |
|
| 375 | + |
|
| 376 | + $bytes_array = [ |
|
| 377 | + 'b' => 1, |
|
| 378 | + 'k' => 1024, |
|
| 379 | + 'kb' => 1024, |
|
| 380 | + 'mb' => 1024 * 1024, |
|
| 381 | + 'm' => 1024 * 1024, |
|
| 382 | + 'gb' => 1024 * 1024 * 1024, |
|
| 383 | + 'g' => 1024 * 1024 * 1024, |
|
| 384 | + 'tb' => 1024 * 1024 * 1024 * 1024, |
|
| 385 | + 't' => 1024 * 1024 * 1024 * 1024, |
|
| 386 | + 'pb' => 1024 * 1024 * 1024 * 1024 * 1024, |
|
| 387 | + 'p' => 1024 * 1024 * 1024 * 1024 * 1024, |
|
| 388 | + ]; |
|
| 389 | + |
|
| 390 | + $bytes = (float)$str; |
|
| 391 | + |
|
| 392 | + if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && isset($bytes_array[$matches[1]])) { |
|
| 393 | + $bytes *= $bytes_array[$matches[1]]; |
|
| 394 | + } else { |
|
| 395 | + return false; |
|
| 396 | + } |
|
| 397 | + |
|
| 398 | + return Util::numericToNumber(round($bytes)); |
|
| 399 | + } |
|
| 400 | + |
|
| 401 | + /** |
|
| 402 | + * connects a function to a hook |
|
| 403 | + * |
|
| 404 | + * @param string $signalClass class name of emitter |
|
| 405 | + * @param string $signalName name of signal |
|
| 406 | + * @param string|object $slotClass class name of slot |
|
| 407 | + * @param string $slotName name of slot |
|
| 408 | + * @return bool |
|
| 409 | + * |
|
| 410 | + * This function makes it very easy to connect to use hooks. |
|
| 411 | + * |
|
| 412 | + * TODO: write example |
|
| 413 | + * @since 4.0.0 |
|
| 414 | + * @deprecated 21.0.0 use \OCP\EventDispatcher\IEventDispatcher::addListener |
|
| 415 | + */ |
|
| 416 | + public static function connectHook($signalClass, $signalName, $slotClass, $slotName) { |
|
| 417 | + return \OC_Hook::connect($signalClass, $signalName, $slotClass, $slotName); |
|
| 418 | + } |
|
| 419 | + |
|
| 420 | + /** |
|
| 421 | + * Emits a signal. To get data from the slot use references! |
|
| 422 | + * @param string $signalclass class name of emitter |
|
| 423 | + * @param string $signalname name of signal |
|
| 424 | + * @param array $params default: array() array with additional data |
|
| 425 | + * @return bool true if slots exists or false if not |
|
| 426 | + * |
|
| 427 | + * TODO: write example |
|
| 428 | + * @since 4.0.0 |
|
| 429 | + * @deprecated 21.0.0 use \OCP\EventDispatcher\IEventDispatcher::dispatchTypedEvent |
|
| 430 | + */ |
|
| 431 | + public static function emitHook($signalclass, $signalname, $params = []) { |
|
| 432 | + return \OC_Hook::emit($signalclass, $signalname, $params); |
|
| 433 | + } |
|
| 434 | + |
|
| 435 | + /** |
|
| 436 | + * Cached encrypted CSRF token. Some static unit-tests of ownCloud compare |
|
| 437 | + * multiple Template elements which invoke `callRegister`. If the value |
|
| 438 | + * would not be cached these unit-tests would fail. |
|
| 439 | + * @var string |
|
| 440 | + */ |
|
| 441 | + private static $token = ''; |
|
| 442 | + |
|
| 443 | + /** |
|
| 444 | + * Register an get/post call. This is important to prevent CSRF attacks |
|
| 445 | + * @since 4.5.0 |
|
| 446 | + * @deprecated 32.0.0 directly use CsrfTokenManager instead |
|
| 447 | + */ |
|
| 448 | + public static function callRegister() { |
|
| 449 | + if (self::$token === '') { |
|
| 450 | + self::$token = \OCP\Server::get(CsrfTokenManager::class)->getToken()->getEncryptedValue(); |
|
| 451 | + } |
|
| 452 | + return self::$token; |
|
| 453 | + } |
|
| 454 | + |
|
| 455 | + /** |
|
| 456 | + * Used to sanitize HTML |
|
| 457 | + * |
|
| 458 | + * This function is used to sanitize HTML and should be applied on any |
|
| 459 | + * string or array of strings before displaying it on a web page. |
|
| 460 | + * |
|
| 461 | + * @param string|string[] $value |
|
| 462 | + * @return ($value is array ? string[] : string) an array of sanitized strings or a single sanitized string, depends on the input parameter. |
|
| 463 | + * @since 4.5.0 |
|
| 464 | + */ |
|
| 465 | + public static function sanitizeHTML($value) { |
|
| 466 | + return \OC_Util::sanitizeHTML($value); |
|
| 467 | + } |
|
| 468 | + |
|
| 469 | + /** |
|
| 470 | + * Public function to encode url parameters |
|
| 471 | + * |
|
| 472 | + * This function is used to encode path to file before output. |
|
| 473 | + * Encoding is done according to RFC 3986 with one exception: |
|
| 474 | + * Character '/' is preserved as is. |
|
| 475 | + * |
|
| 476 | + * @param string $component part of URI to encode |
|
| 477 | + * @return string |
|
| 478 | + * @since 6.0.0 |
|
| 479 | + */ |
|
| 480 | + public static function encodePath($component) { |
|
| 481 | + return \OC_Util::encodePath($component); |
|
| 482 | + } |
|
| 483 | + |
|
| 484 | + /** |
|
| 485 | + * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is. |
|
| 486 | + * |
|
| 487 | + * @param array $input The array to work on |
|
| 488 | + * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default) |
|
| 489 | + * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 |
|
| 490 | + * @return array |
|
| 491 | + * @since 4.5.0 |
|
| 492 | + */ |
|
| 493 | + public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') { |
|
| 494 | + $case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER; |
|
| 495 | + $ret = []; |
|
| 496 | + foreach ($input as $k => $v) { |
|
| 497 | + $ret[mb_convert_case($k, $case, $encoding)] = $v; |
|
| 498 | + } |
|
| 499 | + return $ret; |
|
| 500 | + } |
|
| 501 | + |
|
| 502 | + /** |
|
| 503 | + * performs a search in a nested array |
|
| 504 | + * |
|
| 505 | + * @param array $haystack the array to be searched |
|
| 506 | + * @param string $needle the search string |
|
| 507 | + * @param mixed $index optional, only search this key name |
|
| 508 | + * @return mixed the key of the matching field, otherwise false |
|
| 509 | + * @since 4.5.0 |
|
| 510 | + * @deprecated 15.0.0 |
|
| 511 | + */ |
|
| 512 | + public static function recursiveArraySearch($haystack, $needle, $index = null) { |
|
| 513 | + $aIt = new \RecursiveArrayIterator($haystack); |
|
| 514 | + $it = new \RecursiveIteratorIterator($aIt); |
|
| 515 | + |
|
| 516 | + while ($it->valid()) { |
|
| 517 | + if (((isset($index) && ($it->key() == $index)) || !isset($index)) && ($it->current() == $needle)) { |
|
| 518 | + return $aIt->key(); |
|
| 519 | + } |
|
| 520 | + |
|
| 521 | + $it->next(); |
|
| 522 | + } |
|
| 523 | + |
|
| 524 | + return false; |
|
| 525 | + } |
|
| 526 | + |
|
| 527 | + /** |
|
| 528 | + * calculates the maximum upload size respecting system settings, free space and user quota |
|
| 529 | + * |
|
| 530 | + * @param string $dir the current folder where the user currently operates |
|
| 531 | + * @param int|float|null $free the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly |
|
| 532 | + * @return int|float number of bytes representing |
|
| 533 | + * @since 5.0.0 |
|
| 534 | + */ |
|
| 535 | + public static function maxUploadFilesize(string $dir, int|float|null $free = null): int|float { |
|
| 536 | + if (is_null($free) || $free < 0) { |
|
| 537 | + $free = self::freeSpace($dir); |
|
| 538 | + } |
|
| 539 | + return min($free, self::uploadLimit()); |
|
| 540 | + } |
|
| 541 | + |
|
| 542 | + /** |
|
| 543 | + * Calculate free space left within user quota |
|
| 544 | + * @param string $dir the current folder where the user currently operates |
|
| 545 | + * @return int|float number of bytes representing |
|
| 546 | + * @since 7.0.0 |
|
| 547 | + */ |
|
| 548 | + public static function freeSpace(string $dir): int|float { |
|
| 549 | + $freeSpace = \OC\Files\Filesystem::free_space($dir); |
|
| 550 | + if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) { |
|
| 551 | + $freeSpace = max($freeSpace, 0); |
|
| 552 | + return $freeSpace; |
|
| 553 | + } else { |
|
| 554 | + return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188 |
|
| 555 | + } |
|
| 556 | + } |
|
| 557 | + |
|
| 558 | + /** |
|
| 559 | + * Calculate PHP upload limit |
|
| 560 | + * |
|
| 561 | + * @return int|float number of bytes representing |
|
| 562 | + * @since 7.0.0 |
|
| 563 | + */ |
|
| 564 | + public static function uploadLimit(): int|float { |
|
| 565 | + $ini = Server::get(IniGetWrapper::class); |
|
| 566 | + $upload_max_filesize = self::computerFileSize($ini->get('upload_max_filesize')) ?: 0; |
|
| 567 | + $post_max_size = self::computerFileSize($ini->get('post_max_size')) ?: 0; |
|
| 568 | + if ($upload_max_filesize === 0 && $post_max_size === 0) { |
|
| 569 | + return INF; |
|
| 570 | + } elseif ($upload_max_filesize === 0 || $post_max_size === 0) { |
|
| 571 | + return max($upload_max_filesize, $post_max_size); //only the non 0 value counts |
|
| 572 | + } else { |
|
| 573 | + return min($upload_max_filesize, $post_max_size); |
|
| 574 | + } |
|
| 575 | + } |
|
| 576 | + |
|
| 577 | + /** |
|
| 578 | + * Compare two strings to provide a natural sort |
|
| 579 | + * @param string $a first string to compare |
|
| 580 | + * @param string $b second string to compare |
|
| 581 | + * @return int -1 if $b comes before $a, 1 if $a comes before $b |
|
| 582 | + * or 0 if the strings are identical |
|
| 583 | + * @since 7.0.0 |
|
| 584 | + */ |
|
| 585 | + public static function naturalSortCompare($a, $b) { |
|
| 586 | + return \OC\NaturalSort::getInstance()->compare($a, $b); |
|
| 587 | + } |
|
| 588 | + |
|
| 589 | + /** |
|
| 590 | + * Check if a password is required for each public link |
|
| 591 | + * |
|
| 592 | + * @param bool $checkGroupMembership Check group membership exclusion |
|
| 593 | + * @return boolean |
|
| 594 | + * @since 7.0.0 |
|
| 595 | + */ |
|
| 596 | + public static function isPublicLinkPasswordRequired(bool $checkGroupMembership = true) { |
|
| 597 | + return \OC_Util::isPublicLinkPasswordRequired($checkGroupMembership); |
|
| 598 | + } |
|
| 599 | + |
|
| 600 | + /** |
|
| 601 | + * check if share API enforces a default expire date |
|
| 602 | + * @return boolean |
|
| 603 | + * @since 8.0.0 |
|
| 604 | + */ |
|
| 605 | + public static function isDefaultExpireDateEnforced() { |
|
| 606 | + return \OC_Util::isDefaultExpireDateEnforced(); |
|
| 607 | + } |
|
| 608 | + |
|
| 609 | + protected static $needUpgradeCache = null; |
|
| 610 | + |
|
| 611 | + /** |
|
| 612 | + * Checks whether the current version needs upgrade. |
|
| 613 | + * |
|
| 614 | + * @return bool true if upgrade is needed, false otherwise |
|
| 615 | + * @since 7.0.0 |
|
| 616 | + */ |
|
| 617 | + public static function needUpgrade() { |
|
| 618 | + if (!isset(self::$needUpgradeCache)) { |
|
| 619 | + self::$needUpgradeCache = \OC_Util::needUpgrade(\OCP\Server::get(\OC\SystemConfig::class)); |
|
| 620 | + } |
|
| 621 | + return self::$needUpgradeCache; |
|
| 622 | + } |
|
| 623 | + |
|
| 624 | + /** |
|
| 625 | + * Sometimes a string has to be shortened to fit within a certain maximum |
|
| 626 | + * data length in bytes. substr() you may break multibyte characters, |
|
| 627 | + * because it operates on single byte level. mb_substr() operates on |
|
| 628 | + * characters, so does not ensure that the shortened string satisfies the |
|
| 629 | + * max length in bytes. |
|
| 630 | + * |
|
| 631 | + * For example, json_encode is messing with multibyte characters a lot, |
|
| 632 | + * replacing them with something along "\u1234". |
|
| 633 | + * |
|
| 634 | + * This function shortens the string with by $accuracy (-5) from |
|
| 635 | + * $dataLength characters, until it fits within $dataLength bytes. |
|
| 636 | + * |
|
| 637 | + * @since 23.0.0 |
|
| 638 | + */ |
|
| 639 | + public static function shortenMultibyteString(string $subject, int $dataLength, int $accuracy = 5): string { |
|
| 640 | + $temp = mb_substr($subject, 0, $dataLength); |
|
| 641 | + // json encodes encapsulates the string in double quotes, they need to be substracted |
|
| 642 | + while ((strlen(json_encode($temp)) - 2) > $dataLength) { |
|
| 643 | + $temp = mb_substr($temp, 0, -$accuracy); |
|
| 644 | + } |
|
| 645 | + return $temp; |
|
| 646 | + } |
|
| 647 | + |
|
| 648 | + /** |
|
| 649 | + * Check if a function is enabled in the php configuration |
|
| 650 | + * |
|
| 651 | + * @since 25.0.0 |
|
| 652 | + */ |
|
| 653 | + public static function isFunctionEnabled(string $functionName): bool { |
|
| 654 | + if (!function_exists($functionName)) { |
|
| 655 | + return false; |
|
| 656 | + } |
|
| 657 | + $ini = Server::get(IniGetWrapper::class); |
|
| 658 | + $disabled = explode(',', $ini->get('disable_functions') ?: ''); |
|
| 659 | + $disabled = array_map('trim', $disabled); |
|
| 660 | + if (in_array($functionName, $disabled)) { |
|
| 661 | + return false; |
|
| 662 | + } |
|
| 663 | + return true; |
|
| 664 | + } |
|
| 665 | 665 | } |
@@ -261,9 +261,9 @@ discard block |
||
| 261 | 261 | */ |
| 262 | 262 | public static function linkToRemote($service) { |
| 263 | 263 | $urlGenerator = \OCP\Server::get(IURLGenerator::class); |
| 264 | - $remoteBase = $urlGenerator->linkTo('', 'remote.php') . '/' . $service; |
|
| 264 | + $remoteBase = $urlGenerator->linkTo('', 'remote.php').'/'.$service; |
|
| 265 | 265 | return $urlGenerator->getAbsoluteURL( |
| 266 | - $remoteBase . (($service[strlen($service) - 1] != '/') ? '/' : '') |
|
| 266 | + $remoteBase.(($service[strlen($service) - 1] != '/') ? '/' : '') |
|
| 267 | 267 | ); |
| 268 | 268 | } |
| 269 | 269 | |
@@ -303,7 +303,7 @@ discard block |
||
| 303 | 303 | $user_part = $config->getSystemValueString('mail_from_address', $user_part); |
| 304 | 304 | $host_name = self::getServerHostName(); |
| 305 | 305 | $host_name = $config->getSystemValueString('mail_domain', $host_name); |
| 306 | - $defaultEmailAddress = $user_part . '@' . $host_name; |
|
| 306 | + $defaultEmailAddress = $user_part.'@'.$host_name; |
|
| 307 | 307 | |
| 308 | 308 | $emailValidator = \OCP\Server::get(IEmailValidator::class); |
| 309 | 309 | if ($emailValidator->isValid($defaultEmailAddress)) { |
@@ -311,7 +311,7 @@ discard block |
||
| 311 | 311 | } |
| 312 | 312 | |
| 313 | 313 | // in case we cannot build a valid email address from the hostname let's fallback to 'localhost.localdomain' |
| 314 | - return $user_part . '@localhost.localdomain'; |
|
| 314 | + return $user_part.'@localhost.localdomain'; |
|
| 315 | 315 | } |
| 316 | 316 | |
| 317 | 317 | /** |
@@ -320,9 +320,9 @@ discard block |
||
| 320 | 320 | * @return int|float int if it fits, float if it is too big |
| 321 | 321 | * @since 26.0.0 |
| 322 | 322 | */ |
| 323 | - public static function numericToNumber(string|float|int $number): int|float { |
|
| 323 | + public static function numericToNumber(string | float | int $number): int | float { |
|
| 324 | 324 | /* This is a hack to cast to (int|float) */ |
| 325 | - return 0 + (string)$number; |
|
| 325 | + return 0 + (string) $number; |
|
| 326 | 326 | } |
| 327 | 327 | |
| 328 | 328 | /** |
@@ -331,7 +331,7 @@ discard block |
||
| 331 | 331 | * @return string a human readable file size |
| 332 | 332 | * @since 4.0.0 |
| 333 | 333 | */ |
| 334 | - public static function humanFileSize(int|float $bytes): string { |
|
| 334 | + public static function humanFileSize(int | float $bytes): string { |
|
| 335 | 335 | if ($bytes < 0) { |
| 336 | 336 | return '?'; |
| 337 | 337 | } |
@@ -367,7 +367,7 @@ discard block |
||
| 367 | 367 | * @return false|int|float a file size in bytes |
| 368 | 368 | * @since 4.0.0 |
| 369 | 369 | */ |
| 370 | - public static function computerFileSize(string $str): false|int|float { |
|
| 370 | + public static function computerFileSize(string $str): false | int | float { |
|
| 371 | 371 | $str = strtolower($str); |
| 372 | 372 | if (is_numeric($str)) { |
| 373 | 373 | return Util::numericToNumber($str); |
@@ -387,7 +387,7 @@ discard block |
||
| 387 | 387 | 'p' => 1024 * 1024 * 1024 * 1024 * 1024, |
| 388 | 388 | ]; |
| 389 | 389 | |
| 390 | - $bytes = (float)$str; |
|
| 390 | + $bytes = (float) $str; |
|
| 391 | 391 | |
| 392 | 392 | if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && isset($bytes_array[$matches[1]])) { |
| 393 | 393 | $bytes *= $bytes_array[$matches[1]]; |
@@ -532,7 +532,7 @@ discard block |
||
| 532 | 532 | * @return int|float number of bytes representing |
| 533 | 533 | * @since 5.0.0 |
| 534 | 534 | */ |
| 535 | - public static function maxUploadFilesize(string $dir, int|float|null $free = null): int|float { |
|
| 535 | + public static function maxUploadFilesize(string $dir, int | float | null $free = null): int | float { |
|
| 536 | 536 | if (is_null($free) || $free < 0) { |
| 537 | 537 | $free = self::freeSpace($dir); |
| 538 | 538 | } |
@@ -545,13 +545,13 @@ discard block |
||
| 545 | 545 | * @return int|float number of bytes representing |
| 546 | 546 | * @since 7.0.0 |
| 547 | 547 | */ |
| 548 | - public static function freeSpace(string $dir): int|float { |
|
| 548 | + public static function freeSpace(string $dir): int | float { |
|
| 549 | 549 | $freeSpace = \OC\Files\Filesystem::free_space($dir); |
| 550 | 550 | if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) { |
| 551 | 551 | $freeSpace = max($freeSpace, 0); |
| 552 | 552 | return $freeSpace; |
| 553 | 553 | } else { |
| 554 | - return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188 |
|
| 554 | + return (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188 |
|
| 555 | 555 | } |
| 556 | 556 | } |
| 557 | 557 | |
@@ -561,7 +561,7 @@ discard block |
||
| 561 | 561 | * @return int|float number of bytes representing |
| 562 | 562 | * @since 7.0.0 |
| 563 | 563 | */ |
| 564 | - public static function uploadLimit(): int|float { |
|
| 564 | + public static function uploadLimit(): int | float { |
|
| 565 | 565 | $ini = Server::get(IniGetWrapper::class); |
| 566 | 566 | $upload_max_filesize = self::computerFileSize($ini->get('upload_max_filesize')) ?: 0; |
| 567 | 567 | $post_max_size = self::computerFileSize($ini->get('post_max_size')) ?: 0; |
@@ -21,242 +21,242 @@ |
||
| 21 | 21 | use OCP\Share\IShare; |
| 22 | 22 | |
| 23 | 23 | class MailPlugin implements ISearchPlugin { |
| 24 | - protected bool $shareWithGroupOnly; |
|
| 25 | - |
|
| 26 | - protected bool $shareeEnumeration; |
|
| 27 | - |
|
| 28 | - protected bool $shareeEnumerationInGroupOnly; |
|
| 29 | - |
|
| 30 | - protected bool $shareeEnumerationPhone; |
|
| 31 | - |
|
| 32 | - protected bool $shareeEnumerationFullMatch; |
|
| 33 | - |
|
| 34 | - protected bool $shareeEnumerationFullMatchEmail; |
|
| 35 | - |
|
| 36 | - public function __construct( |
|
| 37 | - private IManager $contactsManager, |
|
| 38 | - private ICloudIdManager $cloudIdManager, |
|
| 39 | - private IConfig $config, |
|
| 40 | - private IGroupManager $groupManager, |
|
| 41 | - private KnownUserService $knownUserService, |
|
| 42 | - private IUserSession $userSession, |
|
| 43 | - private IEmailValidator $emailValidator, |
|
| 44 | - private mixed $shareWithGroupOnlyExcludeGroupsList = [], |
|
| 45 | - ) { |
|
| 46 | - $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; |
|
| 47 | - $this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes'; |
|
| 48 | - $this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes'; |
|
| 49 | - $this->shareeEnumerationPhone = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes'; |
|
| 50 | - $this->shareeEnumerationFullMatch = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes'; |
|
| 51 | - $this->shareeEnumerationFullMatchEmail = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes'; |
|
| 52 | - |
|
| 53 | - if ($this->shareWithGroupOnly) { |
|
| 54 | - $this->shareWithGroupOnlyExcludeGroupsList = json_decode($this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', ''), true) ?? []; |
|
| 55 | - } |
|
| 56 | - } |
|
| 57 | - |
|
| 58 | - /** |
|
| 59 | - * {@inheritdoc} |
|
| 60 | - */ |
|
| 61 | - public function search($search, $limit, $offset, ISearchResult $searchResult): bool { |
|
| 62 | - if ($this->shareeEnumerationFullMatch && !$this->shareeEnumerationFullMatchEmail) { |
|
| 63 | - return false; |
|
| 64 | - } |
|
| 65 | - |
|
| 66 | - // Extract the email address from "Foo Bar <[email protected]>" and then search with "[email protected]" instead |
|
| 67 | - $result = preg_match('/<([^@]+@.+)>$/', $search, $matches); |
|
| 68 | - if ($result && filter_var($matches[1], FILTER_VALIDATE_EMAIL)) { |
|
| 69 | - return $this->search($matches[1], $limit, $offset, $searchResult); |
|
| 70 | - } |
|
| 71 | - |
|
| 72 | - $currentUserId = $this->userSession->getUser()->getUID(); |
|
| 73 | - |
|
| 74 | - $result = $userResults = ['wide' => [], 'exact' => []]; |
|
| 75 | - $userType = new SearchResultType('users'); |
|
| 76 | - $emailType = new SearchResultType('emails'); |
|
| 77 | - |
|
| 78 | - // Search in contacts |
|
| 79 | - $addressBookContacts = $this->contactsManager->search( |
|
| 80 | - $search, |
|
| 81 | - ['EMAIL', 'FN'], |
|
| 82 | - [ |
|
| 83 | - 'limit' => $limit, |
|
| 84 | - 'offset' => $offset, |
|
| 85 | - 'enumeration' => $this->shareeEnumeration, |
|
| 86 | - 'fullmatch' => $this->shareeEnumerationFullMatch, |
|
| 87 | - ] |
|
| 88 | - ); |
|
| 89 | - $lowerSearch = strtolower($search); |
|
| 90 | - foreach ($addressBookContacts as $contact) { |
|
| 91 | - if (isset($contact['EMAIL'])) { |
|
| 92 | - $emailAddresses = $contact['EMAIL']; |
|
| 93 | - if (\is_string($emailAddresses)) { |
|
| 94 | - $emailAddresses = [$emailAddresses]; |
|
| 95 | - } |
|
| 96 | - foreach ($emailAddresses as $type => $emailAddress) { |
|
| 97 | - $displayName = $emailAddress; |
|
| 98 | - $emailAddressType = null; |
|
| 99 | - if (\is_array($emailAddress)) { |
|
| 100 | - $emailAddressData = $emailAddress; |
|
| 101 | - $emailAddress = $emailAddressData['value']; |
|
| 102 | - $emailAddressType = $emailAddressData['type']; |
|
| 103 | - } |
|
| 104 | - |
|
| 105 | - if (!filter_var($emailAddress, FILTER_VALIDATE_EMAIL)) { |
|
| 106 | - continue; |
|
| 107 | - } |
|
| 108 | - |
|
| 109 | - if (isset($contact['FN'])) { |
|
| 110 | - $displayName = $contact['FN'] . ' (' . $emailAddress . ')'; |
|
| 111 | - } |
|
| 112 | - $exactEmailMatch = strtolower($emailAddress) === $lowerSearch; |
|
| 113 | - |
|
| 114 | - if (isset($contact['isLocalSystemBook'])) { |
|
| 115 | - if ($this->shareWithGroupOnly) { |
|
| 116 | - /* |
|
| 24 | + protected bool $shareWithGroupOnly; |
|
| 25 | + |
|
| 26 | + protected bool $shareeEnumeration; |
|
| 27 | + |
|
| 28 | + protected bool $shareeEnumerationInGroupOnly; |
|
| 29 | + |
|
| 30 | + protected bool $shareeEnumerationPhone; |
|
| 31 | + |
|
| 32 | + protected bool $shareeEnumerationFullMatch; |
|
| 33 | + |
|
| 34 | + protected bool $shareeEnumerationFullMatchEmail; |
|
| 35 | + |
|
| 36 | + public function __construct( |
|
| 37 | + private IManager $contactsManager, |
|
| 38 | + private ICloudIdManager $cloudIdManager, |
|
| 39 | + private IConfig $config, |
|
| 40 | + private IGroupManager $groupManager, |
|
| 41 | + private KnownUserService $knownUserService, |
|
| 42 | + private IUserSession $userSession, |
|
| 43 | + private IEmailValidator $emailValidator, |
|
| 44 | + private mixed $shareWithGroupOnlyExcludeGroupsList = [], |
|
| 45 | + ) { |
|
| 46 | + $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; |
|
| 47 | + $this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes'; |
|
| 48 | + $this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes'; |
|
| 49 | + $this->shareeEnumerationPhone = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes'; |
|
| 50 | + $this->shareeEnumerationFullMatch = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes'; |
|
| 51 | + $this->shareeEnumerationFullMatchEmail = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes'; |
|
| 52 | + |
|
| 53 | + if ($this->shareWithGroupOnly) { |
|
| 54 | + $this->shareWithGroupOnlyExcludeGroupsList = json_decode($this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', ''), true) ?? []; |
|
| 55 | + } |
|
| 56 | + } |
|
| 57 | + |
|
| 58 | + /** |
|
| 59 | + * {@inheritdoc} |
|
| 60 | + */ |
|
| 61 | + public function search($search, $limit, $offset, ISearchResult $searchResult): bool { |
|
| 62 | + if ($this->shareeEnumerationFullMatch && !$this->shareeEnumerationFullMatchEmail) { |
|
| 63 | + return false; |
|
| 64 | + } |
|
| 65 | + |
|
| 66 | + // Extract the email address from "Foo Bar <[email protected]>" and then search with "[email protected]" instead |
|
| 67 | + $result = preg_match('/<([^@]+@.+)>$/', $search, $matches); |
|
| 68 | + if ($result && filter_var($matches[1], FILTER_VALIDATE_EMAIL)) { |
|
| 69 | + return $this->search($matches[1], $limit, $offset, $searchResult); |
|
| 70 | + } |
|
| 71 | + |
|
| 72 | + $currentUserId = $this->userSession->getUser()->getUID(); |
|
| 73 | + |
|
| 74 | + $result = $userResults = ['wide' => [], 'exact' => []]; |
|
| 75 | + $userType = new SearchResultType('users'); |
|
| 76 | + $emailType = new SearchResultType('emails'); |
|
| 77 | + |
|
| 78 | + // Search in contacts |
|
| 79 | + $addressBookContacts = $this->contactsManager->search( |
|
| 80 | + $search, |
|
| 81 | + ['EMAIL', 'FN'], |
|
| 82 | + [ |
|
| 83 | + 'limit' => $limit, |
|
| 84 | + 'offset' => $offset, |
|
| 85 | + 'enumeration' => $this->shareeEnumeration, |
|
| 86 | + 'fullmatch' => $this->shareeEnumerationFullMatch, |
|
| 87 | + ] |
|
| 88 | + ); |
|
| 89 | + $lowerSearch = strtolower($search); |
|
| 90 | + foreach ($addressBookContacts as $contact) { |
|
| 91 | + if (isset($contact['EMAIL'])) { |
|
| 92 | + $emailAddresses = $contact['EMAIL']; |
|
| 93 | + if (\is_string($emailAddresses)) { |
|
| 94 | + $emailAddresses = [$emailAddresses]; |
|
| 95 | + } |
|
| 96 | + foreach ($emailAddresses as $type => $emailAddress) { |
|
| 97 | + $displayName = $emailAddress; |
|
| 98 | + $emailAddressType = null; |
|
| 99 | + if (\is_array($emailAddress)) { |
|
| 100 | + $emailAddressData = $emailAddress; |
|
| 101 | + $emailAddress = $emailAddressData['value']; |
|
| 102 | + $emailAddressType = $emailAddressData['type']; |
|
| 103 | + } |
|
| 104 | + |
|
| 105 | + if (!filter_var($emailAddress, FILTER_VALIDATE_EMAIL)) { |
|
| 106 | + continue; |
|
| 107 | + } |
|
| 108 | + |
|
| 109 | + if (isset($contact['FN'])) { |
|
| 110 | + $displayName = $contact['FN'] . ' (' . $emailAddress . ')'; |
|
| 111 | + } |
|
| 112 | + $exactEmailMatch = strtolower($emailAddress) === $lowerSearch; |
|
| 113 | + |
|
| 114 | + if (isset($contact['isLocalSystemBook'])) { |
|
| 115 | + if ($this->shareWithGroupOnly) { |
|
| 116 | + /* |
|
| 117 | 117 | * Check if the user may share with the user associated with the e-mail of the just found contact |
| 118 | 118 | */ |
| 119 | - $userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser()); |
|
| 120 | - |
|
| 121 | - // ShareWithGroupOnly filtering |
|
| 122 | - $userGroups = array_diff($userGroups, $this->shareWithGroupOnlyExcludeGroupsList); |
|
| 123 | - |
|
| 124 | - $found = false; |
|
| 125 | - foreach ($userGroups as $userGroup) { |
|
| 126 | - if ($this->groupManager->isInGroup($contact['UID'], $userGroup)) { |
|
| 127 | - $found = true; |
|
| 128 | - break; |
|
| 129 | - } |
|
| 130 | - } |
|
| 131 | - if (!$found) { |
|
| 132 | - continue; |
|
| 133 | - } |
|
| 134 | - } |
|
| 135 | - if ($exactEmailMatch && $this->shareeEnumerationFullMatch) { |
|
| 136 | - try { |
|
| 137 | - $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0] ?? ''); |
|
| 138 | - } catch (\InvalidArgumentException $e) { |
|
| 139 | - continue; |
|
| 140 | - } |
|
| 141 | - |
|
| 142 | - if (!$this->isCurrentUser($cloud) && !$searchResult->hasResult($userType, $cloud->getUser())) { |
|
| 143 | - $singleResult = [[ |
|
| 144 | - 'label' => $displayName, |
|
| 145 | - 'uuid' => $contact['UID'] ?? $emailAddress, |
|
| 146 | - 'name' => $contact['FN'] ?? $displayName, |
|
| 147 | - 'value' => [ |
|
| 148 | - 'shareType' => IShare::TYPE_USER, |
|
| 149 | - 'shareWith' => $cloud->getUser(), |
|
| 150 | - ], |
|
| 151 | - 'shareWithDisplayNameUnique' => !empty($emailAddress) ? $emailAddress : $cloud->getUser() |
|
| 152 | - |
|
| 153 | - ]]; |
|
| 154 | - $searchResult->addResultSet($userType, [], $singleResult); |
|
| 155 | - $searchResult->markExactIdMatch($emailType); |
|
| 156 | - } |
|
| 157 | - return false; |
|
| 158 | - } |
|
| 159 | - |
|
| 160 | - if ($this->shareeEnumeration) { |
|
| 161 | - try { |
|
| 162 | - $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0] ?? ''); |
|
| 163 | - } catch (\InvalidArgumentException $e) { |
|
| 164 | - continue; |
|
| 165 | - } |
|
| 166 | - |
|
| 167 | - $addToWide = !($this->shareeEnumerationInGroupOnly || $this->shareeEnumerationPhone); |
|
| 168 | - if (!$addToWide && $this->shareeEnumerationPhone && $this->knownUserService->isKnownToUser($currentUserId, $contact['UID'])) { |
|
| 169 | - $addToWide = true; |
|
| 170 | - } |
|
| 171 | - |
|
| 172 | - if (!$addToWide && $this->shareeEnumerationInGroupOnly) { |
|
| 173 | - $addToWide = false; |
|
| 174 | - $userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser()); |
|
| 175 | - foreach ($userGroups as $userGroup) { |
|
| 176 | - if ($this->groupManager->isInGroup($contact['UID'], $userGroup)) { |
|
| 177 | - $addToWide = true; |
|
| 178 | - break; |
|
| 179 | - } |
|
| 180 | - } |
|
| 181 | - } |
|
| 182 | - if ($addToWide && !$this->isCurrentUser($cloud) && !$searchResult->hasResult($userType, $cloud->getUser())) { |
|
| 183 | - $userResults['wide'][] = [ |
|
| 184 | - 'label' => $displayName, |
|
| 185 | - 'uuid' => $contact['UID'] ?? $emailAddress, |
|
| 186 | - 'name' => $contact['FN'] ?? $displayName, |
|
| 187 | - 'value' => [ |
|
| 188 | - 'shareType' => IShare::TYPE_USER, |
|
| 189 | - 'shareWith' => $cloud->getUser(), |
|
| 190 | - ], |
|
| 191 | - 'shareWithDisplayNameUnique' => !empty($emailAddress) ? $emailAddress : $cloud->getUser() |
|
| 192 | - ]; |
|
| 193 | - continue; |
|
| 194 | - } |
|
| 195 | - } |
|
| 196 | - continue; |
|
| 197 | - } |
|
| 198 | - |
|
| 199 | - if ($exactEmailMatch |
|
| 200 | - || (isset($contact['FN']) && strtolower($contact['FN']) === $lowerSearch)) { |
|
| 201 | - if ($exactEmailMatch) { |
|
| 202 | - $searchResult->markExactIdMatch($emailType); |
|
| 203 | - } |
|
| 204 | - $result['exact'][] = [ |
|
| 205 | - 'label' => $displayName, |
|
| 206 | - 'uuid' => $contact['UID'] ?? $emailAddress, |
|
| 207 | - 'name' => $contact['FN'] ?? $displayName, |
|
| 208 | - 'type' => $emailAddressType ?? '', |
|
| 209 | - 'value' => [ |
|
| 210 | - 'shareType' => IShare::TYPE_EMAIL, |
|
| 211 | - 'shareWith' => $emailAddress, |
|
| 212 | - ], |
|
| 213 | - ]; |
|
| 214 | - } else { |
|
| 215 | - $result['wide'][] = [ |
|
| 216 | - 'label' => $displayName, |
|
| 217 | - 'uuid' => $contact['UID'] ?? $emailAddress, |
|
| 218 | - 'name' => $contact['FN'] ?? $displayName, |
|
| 219 | - 'type' => $emailAddressType ?? '', |
|
| 220 | - 'value' => [ |
|
| 221 | - 'shareType' => IShare::TYPE_EMAIL, |
|
| 222 | - 'shareWith' => $emailAddress, |
|
| 223 | - ], |
|
| 224 | - ]; |
|
| 225 | - } |
|
| 226 | - } |
|
| 227 | - } |
|
| 228 | - } |
|
| 229 | - |
|
| 230 | - $reachedEnd = true; |
|
| 231 | - if ($this->shareeEnumeration) { |
|
| 232 | - $reachedEnd = (count($result['wide']) < $offset + $limit) |
|
| 233 | - && (count($userResults['wide']) < $offset + $limit); |
|
| 234 | - |
|
| 235 | - $result['wide'] = array_slice($result['wide'], $offset, $limit); |
|
| 236 | - $userResults['wide'] = array_slice($userResults['wide'], $offset, $limit); |
|
| 237 | - } |
|
| 238 | - |
|
| 239 | - if (!$searchResult->hasExactIdMatch($emailType) && $this->emailValidator->isValid($search)) { |
|
| 240 | - $result['exact'][] = [ |
|
| 241 | - 'label' => $search, |
|
| 242 | - 'uuid' => $search, |
|
| 243 | - 'value' => [ |
|
| 244 | - 'shareType' => IShare::TYPE_EMAIL, |
|
| 245 | - 'shareWith' => $search, |
|
| 246 | - ], |
|
| 247 | - ]; |
|
| 248 | - } |
|
| 249 | - |
|
| 250 | - if (!empty($userResults['wide'])) { |
|
| 251 | - $searchResult->addResultSet($userType, $userResults['wide'], []); |
|
| 252 | - } |
|
| 253 | - $searchResult->addResultSet($emailType, $result['wide'], $result['exact']); |
|
| 254 | - |
|
| 255 | - return !$reachedEnd; |
|
| 256 | - } |
|
| 257 | - |
|
| 258 | - public function isCurrentUser(ICloudId $cloud): bool { |
|
| 259 | - $currentUser = $this->userSession->getUser(); |
|
| 260 | - return $currentUser instanceof IUser && $currentUser->getUID() === $cloud->getUser(); |
|
| 261 | - } |
|
| 119 | + $userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser()); |
|
| 120 | + |
|
| 121 | + // ShareWithGroupOnly filtering |
|
| 122 | + $userGroups = array_diff($userGroups, $this->shareWithGroupOnlyExcludeGroupsList); |
|
| 123 | + |
|
| 124 | + $found = false; |
|
| 125 | + foreach ($userGroups as $userGroup) { |
|
| 126 | + if ($this->groupManager->isInGroup($contact['UID'], $userGroup)) { |
|
| 127 | + $found = true; |
|
| 128 | + break; |
|
| 129 | + } |
|
| 130 | + } |
|
| 131 | + if (!$found) { |
|
| 132 | + continue; |
|
| 133 | + } |
|
| 134 | + } |
|
| 135 | + if ($exactEmailMatch && $this->shareeEnumerationFullMatch) { |
|
| 136 | + try { |
|
| 137 | + $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0] ?? ''); |
|
| 138 | + } catch (\InvalidArgumentException $e) { |
|
| 139 | + continue; |
|
| 140 | + } |
|
| 141 | + |
|
| 142 | + if (!$this->isCurrentUser($cloud) && !$searchResult->hasResult($userType, $cloud->getUser())) { |
|
| 143 | + $singleResult = [[ |
|
| 144 | + 'label' => $displayName, |
|
| 145 | + 'uuid' => $contact['UID'] ?? $emailAddress, |
|
| 146 | + 'name' => $contact['FN'] ?? $displayName, |
|
| 147 | + 'value' => [ |
|
| 148 | + 'shareType' => IShare::TYPE_USER, |
|
| 149 | + 'shareWith' => $cloud->getUser(), |
|
| 150 | + ], |
|
| 151 | + 'shareWithDisplayNameUnique' => !empty($emailAddress) ? $emailAddress : $cloud->getUser() |
|
| 152 | + |
|
| 153 | + ]]; |
|
| 154 | + $searchResult->addResultSet($userType, [], $singleResult); |
|
| 155 | + $searchResult->markExactIdMatch($emailType); |
|
| 156 | + } |
|
| 157 | + return false; |
|
| 158 | + } |
|
| 159 | + |
|
| 160 | + if ($this->shareeEnumeration) { |
|
| 161 | + try { |
|
| 162 | + $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0] ?? ''); |
|
| 163 | + } catch (\InvalidArgumentException $e) { |
|
| 164 | + continue; |
|
| 165 | + } |
|
| 166 | + |
|
| 167 | + $addToWide = !($this->shareeEnumerationInGroupOnly || $this->shareeEnumerationPhone); |
|
| 168 | + if (!$addToWide && $this->shareeEnumerationPhone && $this->knownUserService->isKnownToUser($currentUserId, $contact['UID'])) { |
|
| 169 | + $addToWide = true; |
|
| 170 | + } |
|
| 171 | + |
|
| 172 | + if (!$addToWide && $this->shareeEnumerationInGroupOnly) { |
|
| 173 | + $addToWide = false; |
|
| 174 | + $userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser()); |
|
| 175 | + foreach ($userGroups as $userGroup) { |
|
| 176 | + if ($this->groupManager->isInGroup($contact['UID'], $userGroup)) { |
|
| 177 | + $addToWide = true; |
|
| 178 | + break; |
|
| 179 | + } |
|
| 180 | + } |
|
| 181 | + } |
|
| 182 | + if ($addToWide && !$this->isCurrentUser($cloud) && !$searchResult->hasResult($userType, $cloud->getUser())) { |
|
| 183 | + $userResults['wide'][] = [ |
|
| 184 | + 'label' => $displayName, |
|
| 185 | + 'uuid' => $contact['UID'] ?? $emailAddress, |
|
| 186 | + 'name' => $contact['FN'] ?? $displayName, |
|
| 187 | + 'value' => [ |
|
| 188 | + 'shareType' => IShare::TYPE_USER, |
|
| 189 | + 'shareWith' => $cloud->getUser(), |
|
| 190 | + ], |
|
| 191 | + 'shareWithDisplayNameUnique' => !empty($emailAddress) ? $emailAddress : $cloud->getUser() |
|
| 192 | + ]; |
|
| 193 | + continue; |
|
| 194 | + } |
|
| 195 | + } |
|
| 196 | + continue; |
|
| 197 | + } |
|
| 198 | + |
|
| 199 | + if ($exactEmailMatch |
|
| 200 | + || (isset($contact['FN']) && strtolower($contact['FN']) === $lowerSearch)) { |
|
| 201 | + if ($exactEmailMatch) { |
|
| 202 | + $searchResult->markExactIdMatch($emailType); |
|
| 203 | + } |
|
| 204 | + $result['exact'][] = [ |
|
| 205 | + 'label' => $displayName, |
|
| 206 | + 'uuid' => $contact['UID'] ?? $emailAddress, |
|
| 207 | + 'name' => $contact['FN'] ?? $displayName, |
|
| 208 | + 'type' => $emailAddressType ?? '', |
|
| 209 | + 'value' => [ |
|
| 210 | + 'shareType' => IShare::TYPE_EMAIL, |
|
| 211 | + 'shareWith' => $emailAddress, |
|
| 212 | + ], |
|
| 213 | + ]; |
|
| 214 | + } else { |
|
| 215 | + $result['wide'][] = [ |
|
| 216 | + 'label' => $displayName, |
|
| 217 | + 'uuid' => $contact['UID'] ?? $emailAddress, |
|
| 218 | + 'name' => $contact['FN'] ?? $displayName, |
|
| 219 | + 'type' => $emailAddressType ?? '', |
|
| 220 | + 'value' => [ |
|
| 221 | + 'shareType' => IShare::TYPE_EMAIL, |
|
| 222 | + 'shareWith' => $emailAddress, |
|
| 223 | + ], |
|
| 224 | + ]; |
|
| 225 | + } |
|
| 226 | + } |
|
| 227 | + } |
|
| 228 | + } |
|
| 229 | + |
|
| 230 | + $reachedEnd = true; |
|
| 231 | + if ($this->shareeEnumeration) { |
|
| 232 | + $reachedEnd = (count($result['wide']) < $offset + $limit) |
|
| 233 | + && (count($userResults['wide']) < $offset + $limit); |
|
| 234 | + |
|
| 235 | + $result['wide'] = array_slice($result['wide'], $offset, $limit); |
|
| 236 | + $userResults['wide'] = array_slice($userResults['wide'], $offset, $limit); |
|
| 237 | + } |
|
| 238 | + |
|
| 239 | + if (!$searchResult->hasExactIdMatch($emailType) && $this->emailValidator->isValid($search)) { |
|
| 240 | + $result['exact'][] = [ |
|
| 241 | + 'label' => $search, |
|
| 242 | + 'uuid' => $search, |
|
| 243 | + 'value' => [ |
|
| 244 | + 'shareType' => IShare::TYPE_EMAIL, |
|
| 245 | + 'shareWith' => $search, |
|
| 246 | + ], |
|
| 247 | + ]; |
|
| 248 | + } |
|
| 249 | + |
|
| 250 | + if (!empty($userResults['wide'])) { |
|
| 251 | + $searchResult->addResultSet($userType, $userResults['wide'], []); |
|
| 252 | + } |
|
| 253 | + $searchResult->addResultSet($emailType, $result['wide'], $result['exact']); |
|
| 254 | + |
|
| 255 | + return !$reachedEnd; |
|
| 256 | + } |
|
| 257 | + |
|
| 258 | + public function isCurrentUser(ICloudId $cloud): bool { |
|
| 259 | + $currentUser = $this->userSession->getUser(); |
|
| 260 | + return $currentUser instanceof IUser && $currentUser->getUID() === $cloud->getUser(); |
|
| 261 | + } |
|
| 262 | 262 | } |
@@ -27,169 +27,169 @@ |
||
| 27 | 27 | use Symfony\Component\Console\Question\Question; |
| 28 | 28 | |
| 29 | 29 | class Add extends Command { |
| 30 | - public function __construct( |
|
| 31 | - protected IUserManager $userManager, |
|
| 32 | - protected IGroupManager $groupManager, |
|
| 33 | - private IEmailValidator $emailValidator, |
|
| 34 | - private IAppConfig $appConfig, |
|
| 35 | - private NewUserMailHelper $mailHelper, |
|
| 36 | - private IEventDispatcher $eventDispatcher, |
|
| 37 | - private ISecureRandom $secureRandom, |
|
| 38 | - ) { |
|
| 39 | - parent::__construct(); |
|
| 40 | - } |
|
| 41 | - |
|
| 42 | - protected function configure(): void { |
|
| 43 | - $this |
|
| 44 | - ->setName('user:add') |
|
| 45 | - ->setDescription('adds an account') |
|
| 46 | - ->addArgument( |
|
| 47 | - 'uid', |
|
| 48 | - InputArgument::REQUIRED, |
|
| 49 | - 'Account ID used to login (must only contain a-z, A-Z, 0-9, -, _ and @)' |
|
| 50 | - ) |
|
| 51 | - ->addOption( |
|
| 52 | - 'password-from-env', |
|
| 53 | - null, |
|
| 54 | - InputOption::VALUE_NONE, |
|
| 55 | - 'read password from environment variable NC_PASS/OC_PASS' |
|
| 56 | - ) |
|
| 57 | - ->addOption( |
|
| 58 | - 'generate-password', |
|
| 59 | - null, |
|
| 60 | - InputOption::VALUE_NONE, |
|
| 61 | - 'Generate a secure password. A welcome email with a reset link will be sent to the user via an email if --email option and newUser.sendEmail config are set' |
|
| 62 | - ) |
|
| 63 | - ->addOption( |
|
| 64 | - 'display-name', |
|
| 65 | - null, |
|
| 66 | - InputOption::VALUE_OPTIONAL, |
|
| 67 | - 'Login used in the web UI (can contain any characters)' |
|
| 68 | - ) |
|
| 69 | - ->addOption( |
|
| 70 | - 'group', |
|
| 71 | - 'g', |
|
| 72 | - InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, |
|
| 73 | - 'groups the account should be added to (The group will be created if it does not exist)' |
|
| 74 | - ) |
|
| 75 | - ->addOption( |
|
| 76 | - 'email', |
|
| 77 | - null, |
|
| 78 | - InputOption::VALUE_REQUIRED, |
|
| 79 | - 'When set, users may register using the default email verification workflow' |
|
| 80 | - ); |
|
| 81 | - } |
|
| 82 | - |
|
| 83 | - protected function execute(InputInterface $input, OutputInterface $output): int { |
|
| 84 | - $uid = $input->getArgument('uid'); |
|
| 85 | - if ($this->userManager->userExists($uid)) { |
|
| 86 | - $output->writeln('<error>The account "' . $uid . '" already exists.</error>'); |
|
| 87 | - return 1; |
|
| 88 | - } |
|
| 89 | - |
|
| 90 | - $password = ''; |
|
| 91 | - |
|
| 92 | - // Setup password. |
|
| 93 | - if ($input->getOption('password-from-env')) { |
|
| 94 | - $password = getenv('NC_PASS') ?: getenv('OC_PASS'); |
|
| 95 | - |
|
| 96 | - if (!$password) { |
|
| 97 | - $output->writeln('<error>--password-from-env given, but NC_PASS/OC_PASS is empty!</error>'); |
|
| 98 | - return 1; |
|
| 99 | - } |
|
| 100 | - } elseif ($input->getOption('generate-password')) { |
|
| 101 | - $passwordEvent = new GenerateSecurePasswordEvent(); |
|
| 102 | - $this->eventDispatcher->dispatchTyped($passwordEvent); |
|
| 103 | - $password = $passwordEvent->getPassword() ?? $this->secureRandom->generate(20); |
|
| 104 | - } elseif ($input->isInteractive()) { |
|
| 105 | - /** @var QuestionHelper $helper */ |
|
| 106 | - $helper = $this->getHelper('question'); |
|
| 107 | - |
|
| 108 | - $question = new Question('Enter password: '); |
|
| 109 | - $question->setHidden(true); |
|
| 110 | - $password = $helper->ask($input, $output, $question); |
|
| 111 | - |
|
| 112 | - $question = new Question('Confirm password: '); |
|
| 113 | - $question->setHidden(true); |
|
| 114 | - $confirm = $helper->ask($input, $output, $question); |
|
| 115 | - |
|
| 116 | - if ($password !== $confirm) { |
|
| 117 | - $output->writeln('<error>Passwords did not match!</error>'); |
|
| 118 | - return 1; |
|
| 119 | - } |
|
| 120 | - } else { |
|
| 121 | - $output->writeln('<error>Interactive input or --password-from-env or --generate-password is needed for setting a password!</error>'); |
|
| 122 | - return 1; |
|
| 123 | - } |
|
| 124 | - |
|
| 125 | - try { |
|
| 126 | - $user = $this->userManager->createUser( |
|
| 127 | - $input->getArgument('uid'), |
|
| 128 | - $password, |
|
| 129 | - ); |
|
| 130 | - } catch (\Exception $e) { |
|
| 131 | - $output->writeln('<error>' . $e->getMessage() . '</error>'); |
|
| 132 | - return 1; |
|
| 133 | - } |
|
| 134 | - |
|
| 135 | - if ($user instanceof IUser) { |
|
| 136 | - $output->writeln('<info>The account "' . $user->getUID() . '" was created successfully</info>'); |
|
| 137 | - } else { |
|
| 138 | - $output->writeln('<error>An error occurred while creating the account</error>'); |
|
| 139 | - return 1; |
|
| 140 | - } |
|
| 141 | - |
|
| 142 | - if ($input->getOption('display-name')) { |
|
| 143 | - $user->setDisplayName($input->getOption('display-name')); |
|
| 144 | - $output->writeln('Display name set to "' . $user->getDisplayName() . '"'); |
|
| 145 | - } |
|
| 146 | - |
|
| 147 | - $groups = $input->getOption('group'); |
|
| 148 | - |
|
| 149 | - if (!empty($groups)) { |
|
| 150 | - // Make sure we init the Filesystem for the user, in case we need to |
|
| 151 | - // init some group shares. |
|
| 152 | - Filesystem::init($user->getUID(), ''); |
|
| 153 | - } |
|
| 154 | - |
|
| 155 | - foreach ($groups as $groupName) { |
|
| 156 | - $group = $this->groupManager->get($groupName); |
|
| 157 | - if (!$group) { |
|
| 158 | - $this->groupManager->createGroup($groupName); |
|
| 159 | - $group = $this->groupManager->get($groupName); |
|
| 160 | - if ($group instanceof IGroup) { |
|
| 161 | - $output->writeln('Created group "' . $group->getGID() . '"'); |
|
| 162 | - } |
|
| 163 | - } |
|
| 164 | - if ($group instanceof IGroup) { |
|
| 165 | - $group->addUser($user); |
|
| 166 | - $output->writeln('Account "' . $user->getUID() . '" added to group "' . $group->getGID() . '"'); |
|
| 167 | - } |
|
| 168 | - } |
|
| 169 | - |
|
| 170 | - $email = $input->getOption('email'); |
|
| 171 | - if (!empty($email)) { |
|
| 172 | - if (!$this->emailValidator->isValid($email)) { |
|
| 173 | - $output->writeln(\sprintf( |
|
| 174 | - '<error>The given email address "%s" is invalid. Email not set for the user.</error>', |
|
| 175 | - $email, |
|
| 176 | - )); |
|
| 177 | - |
|
| 178 | - return 1; |
|
| 179 | - } |
|
| 180 | - |
|
| 181 | - $user->setSystemEMailAddress($email); |
|
| 182 | - |
|
| 183 | - if ($this->appConfig->getValueString('core', 'newUser.sendEmail', 'yes') === 'yes') { |
|
| 184 | - try { |
|
| 185 | - $this->mailHelper->sendMail($user, $this->mailHelper->generateTemplate($user, true)); |
|
| 186 | - $output->writeln('Welcome email sent to ' . $email); |
|
| 187 | - } catch (\Exception $e) { |
|
| 188 | - $output->writeln('Unable to send the welcome email to ' . $email); |
|
| 189 | - } |
|
| 190 | - } |
|
| 191 | - } |
|
| 192 | - |
|
| 193 | - return 0; |
|
| 194 | - } |
|
| 30 | + public function __construct( |
|
| 31 | + protected IUserManager $userManager, |
|
| 32 | + protected IGroupManager $groupManager, |
|
| 33 | + private IEmailValidator $emailValidator, |
|
| 34 | + private IAppConfig $appConfig, |
|
| 35 | + private NewUserMailHelper $mailHelper, |
|
| 36 | + private IEventDispatcher $eventDispatcher, |
|
| 37 | + private ISecureRandom $secureRandom, |
|
| 38 | + ) { |
|
| 39 | + parent::__construct(); |
|
| 40 | + } |
|
| 41 | + |
|
| 42 | + protected function configure(): void { |
|
| 43 | + $this |
|
| 44 | + ->setName('user:add') |
|
| 45 | + ->setDescription('adds an account') |
|
| 46 | + ->addArgument( |
|
| 47 | + 'uid', |
|
| 48 | + InputArgument::REQUIRED, |
|
| 49 | + 'Account ID used to login (must only contain a-z, A-Z, 0-9, -, _ and @)' |
|
| 50 | + ) |
|
| 51 | + ->addOption( |
|
| 52 | + 'password-from-env', |
|
| 53 | + null, |
|
| 54 | + InputOption::VALUE_NONE, |
|
| 55 | + 'read password from environment variable NC_PASS/OC_PASS' |
|
| 56 | + ) |
|
| 57 | + ->addOption( |
|
| 58 | + 'generate-password', |
|
| 59 | + null, |
|
| 60 | + InputOption::VALUE_NONE, |
|
| 61 | + 'Generate a secure password. A welcome email with a reset link will be sent to the user via an email if --email option and newUser.sendEmail config are set' |
|
| 62 | + ) |
|
| 63 | + ->addOption( |
|
| 64 | + 'display-name', |
|
| 65 | + null, |
|
| 66 | + InputOption::VALUE_OPTIONAL, |
|
| 67 | + 'Login used in the web UI (can contain any characters)' |
|
| 68 | + ) |
|
| 69 | + ->addOption( |
|
| 70 | + 'group', |
|
| 71 | + 'g', |
|
| 72 | + InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, |
|
| 73 | + 'groups the account should be added to (The group will be created if it does not exist)' |
|
| 74 | + ) |
|
| 75 | + ->addOption( |
|
| 76 | + 'email', |
|
| 77 | + null, |
|
| 78 | + InputOption::VALUE_REQUIRED, |
|
| 79 | + 'When set, users may register using the default email verification workflow' |
|
| 80 | + ); |
|
| 81 | + } |
|
| 82 | + |
|
| 83 | + protected function execute(InputInterface $input, OutputInterface $output): int { |
|
| 84 | + $uid = $input->getArgument('uid'); |
|
| 85 | + if ($this->userManager->userExists($uid)) { |
|
| 86 | + $output->writeln('<error>The account "' . $uid . '" already exists.</error>'); |
|
| 87 | + return 1; |
|
| 88 | + } |
|
| 89 | + |
|
| 90 | + $password = ''; |
|
| 91 | + |
|
| 92 | + // Setup password. |
|
| 93 | + if ($input->getOption('password-from-env')) { |
|
| 94 | + $password = getenv('NC_PASS') ?: getenv('OC_PASS'); |
|
| 95 | + |
|
| 96 | + if (!$password) { |
|
| 97 | + $output->writeln('<error>--password-from-env given, but NC_PASS/OC_PASS is empty!</error>'); |
|
| 98 | + return 1; |
|
| 99 | + } |
|
| 100 | + } elseif ($input->getOption('generate-password')) { |
|
| 101 | + $passwordEvent = new GenerateSecurePasswordEvent(); |
|
| 102 | + $this->eventDispatcher->dispatchTyped($passwordEvent); |
|
| 103 | + $password = $passwordEvent->getPassword() ?? $this->secureRandom->generate(20); |
|
| 104 | + } elseif ($input->isInteractive()) { |
|
| 105 | + /** @var QuestionHelper $helper */ |
|
| 106 | + $helper = $this->getHelper('question'); |
|
| 107 | + |
|
| 108 | + $question = new Question('Enter password: '); |
|
| 109 | + $question->setHidden(true); |
|
| 110 | + $password = $helper->ask($input, $output, $question); |
|
| 111 | + |
|
| 112 | + $question = new Question('Confirm password: '); |
|
| 113 | + $question->setHidden(true); |
|
| 114 | + $confirm = $helper->ask($input, $output, $question); |
|
| 115 | + |
|
| 116 | + if ($password !== $confirm) { |
|
| 117 | + $output->writeln('<error>Passwords did not match!</error>'); |
|
| 118 | + return 1; |
|
| 119 | + } |
|
| 120 | + } else { |
|
| 121 | + $output->writeln('<error>Interactive input or --password-from-env or --generate-password is needed for setting a password!</error>'); |
|
| 122 | + return 1; |
|
| 123 | + } |
|
| 124 | + |
|
| 125 | + try { |
|
| 126 | + $user = $this->userManager->createUser( |
|
| 127 | + $input->getArgument('uid'), |
|
| 128 | + $password, |
|
| 129 | + ); |
|
| 130 | + } catch (\Exception $e) { |
|
| 131 | + $output->writeln('<error>' . $e->getMessage() . '</error>'); |
|
| 132 | + return 1; |
|
| 133 | + } |
|
| 134 | + |
|
| 135 | + if ($user instanceof IUser) { |
|
| 136 | + $output->writeln('<info>The account "' . $user->getUID() . '" was created successfully</info>'); |
|
| 137 | + } else { |
|
| 138 | + $output->writeln('<error>An error occurred while creating the account</error>'); |
|
| 139 | + return 1; |
|
| 140 | + } |
|
| 141 | + |
|
| 142 | + if ($input->getOption('display-name')) { |
|
| 143 | + $user->setDisplayName($input->getOption('display-name')); |
|
| 144 | + $output->writeln('Display name set to "' . $user->getDisplayName() . '"'); |
|
| 145 | + } |
|
| 146 | + |
|
| 147 | + $groups = $input->getOption('group'); |
|
| 148 | + |
|
| 149 | + if (!empty($groups)) { |
|
| 150 | + // Make sure we init the Filesystem for the user, in case we need to |
|
| 151 | + // init some group shares. |
|
| 152 | + Filesystem::init($user->getUID(), ''); |
|
| 153 | + } |
|
| 154 | + |
|
| 155 | + foreach ($groups as $groupName) { |
|
| 156 | + $group = $this->groupManager->get($groupName); |
|
| 157 | + if (!$group) { |
|
| 158 | + $this->groupManager->createGroup($groupName); |
|
| 159 | + $group = $this->groupManager->get($groupName); |
|
| 160 | + if ($group instanceof IGroup) { |
|
| 161 | + $output->writeln('Created group "' . $group->getGID() . '"'); |
|
| 162 | + } |
|
| 163 | + } |
|
| 164 | + if ($group instanceof IGroup) { |
|
| 165 | + $group->addUser($user); |
|
| 166 | + $output->writeln('Account "' . $user->getUID() . '" added to group "' . $group->getGID() . '"'); |
|
| 167 | + } |
|
| 168 | + } |
|
| 169 | + |
|
| 170 | + $email = $input->getOption('email'); |
|
| 171 | + if (!empty($email)) { |
|
| 172 | + if (!$this->emailValidator->isValid($email)) { |
|
| 173 | + $output->writeln(\sprintf( |
|
| 174 | + '<error>The given email address "%s" is invalid. Email not set for the user.</error>', |
|
| 175 | + $email, |
|
| 176 | + )); |
|
| 177 | + |
|
| 178 | + return 1; |
|
| 179 | + } |
|
| 180 | + |
|
| 181 | + $user->setSystemEMailAddress($email); |
|
| 182 | + |
|
| 183 | + if ($this->appConfig->getValueString('core', 'newUser.sendEmail', 'yes') === 'yes') { |
|
| 184 | + try { |
|
| 185 | + $this->mailHelper->sendMail($user, $this->mailHelper->generateTemplate($user, true)); |
|
| 186 | + $output->writeln('Welcome email sent to ' . $email); |
|
| 187 | + } catch (\Exception $e) { |
|
| 188 | + $output->writeln('Unable to send the welcome email to ' . $email); |
|
| 189 | + } |
|
| 190 | + } |
|
| 191 | + } |
|
| 192 | + |
|
| 193 | + return 0; |
|
| 194 | + } |
|
| 195 | 195 | } |
@@ -46,1200 +46,1200 @@ |
||
| 46 | 46 | * @package OCA\ShareByMail |
| 47 | 47 | */ |
| 48 | 48 | class ShareByMailProvider extends DefaultShareProvider implements IShareProviderWithNotification { |
| 49 | - /** |
|
| 50 | - * Return the identifier of this provider. |
|
| 51 | - * |
|
| 52 | - * @return string Containing only [a-zA-Z0-9] |
|
| 53 | - */ |
|
| 54 | - public function identifier(): string { |
|
| 55 | - return 'ocMailShare'; |
|
| 56 | - } |
|
| 57 | - |
|
| 58 | - public function __construct( |
|
| 59 | - private IConfig $config, |
|
| 60 | - private IDBConnection $dbConnection, |
|
| 61 | - private ISecureRandom $secureRandom, |
|
| 62 | - private IUserManager $userManager, |
|
| 63 | - private IRootFolder $rootFolder, |
|
| 64 | - private IL10N $l, |
|
| 65 | - private LoggerInterface $logger, |
|
| 66 | - private IMailer $mailer, |
|
| 67 | - private IURLGenerator $urlGenerator, |
|
| 68 | - private IManager $activityManager, |
|
| 69 | - private SettingsManager $settingsManager, |
|
| 70 | - private Defaults $defaults, |
|
| 71 | - private IHasher $hasher, |
|
| 72 | - private IEventDispatcher $eventDispatcher, |
|
| 73 | - private IShareManager $shareManager, |
|
| 74 | - private IEmailValidator $emailValidator, |
|
| 75 | - ) { |
|
| 76 | - } |
|
| 77 | - |
|
| 78 | - /** |
|
| 79 | - * Share a path |
|
| 80 | - * |
|
| 81 | - * @throws ShareNotFound |
|
| 82 | - * @throws \Exception |
|
| 83 | - */ |
|
| 84 | - public function create(IShare $share): IShare { |
|
| 85 | - $shareWith = $share->getSharedWith(); |
|
| 86 | - // Check if file is not already shared with the given email, |
|
| 87 | - // if we have an email at all. |
|
| 88 | - $alreadyShared = $this->getSharedWith($shareWith, IShare::TYPE_EMAIL, $share->getNode(), 1, 0); |
|
| 89 | - if ($shareWith !== '' && !empty($alreadyShared)) { |
|
| 90 | - $message = 'Sharing %1$s failed, because this item is already shared with the account %2$s'; |
|
| 91 | - $message_t = $this->l->t('Sharing %1$s failed, because this item is already shared with the account %2$s', [$share->getNode()->getName(), $shareWith]); |
|
| 92 | - $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); |
|
| 93 | - throw new \Exception($message_t); |
|
| 94 | - } |
|
| 95 | - |
|
| 96 | - // if the admin enforces a password for all mail shares we create a |
|
| 97 | - // random password and send it to the recipient |
|
| 98 | - $password = $share->getPassword() ?: ''; |
|
| 99 | - $passwordEnforced = $this->shareManager->shareApiLinkEnforcePassword(); |
|
| 100 | - if ($passwordEnforced && empty($password)) { |
|
| 101 | - $password = $this->autoGeneratePassword($share); |
|
| 102 | - } |
|
| 103 | - |
|
| 104 | - if (!empty($password)) { |
|
| 105 | - $share->setPassword($this->hasher->hash($password)); |
|
| 106 | - } |
|
| 107 | - |
|
| 108 | - $shareId = $this->createMailShare($share); |
|
| 109 | - |
|
| 110 | - $this->createShareActivity($share); |
|
| 111 | - $data = $this->getRawShare($shareId); |
|
| 112 | - |
|
| 113 | - // Temporary set the clear password again to send it by mail |
|
| 114 | - // This need to be done after the share was created in the database |
|
| 115 | - // as the password is hashed in between. |
|
| 116 | - if (!empty($password)) { |
|
| 117 | - $data['password'] = $password; |
|
| 118 | - } |
|
| 119 | - |
|
| 120 | - return $this->createShareObject($data); |
|
| 121 | - } |
|
| 122 | - |
|
| 123 | - /** |
|
| 124 | - * auto generate password in case of password enforcement on mail shares |
|
| 125 | - * |
|
| 126 | - * @throws \Exception |
|
| 127 | - */ |
|
| 128 | - protected function autoGeneratePassword(IShare $share): string { |
|
| 129 | - $initiatorUser = $this->userManager->get($share->getSharedBy()); |
|
| 130 | - $initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
| 131 | - $allowPasswordByMail = $this->settingsManager->sendPasswordByMail(); |
|
| 132 | - |
|
| 133 | - if ($initiatorEMailAddress === null && !$allowPasswordByMail) { |
|
| 134 | - throw new \Exception( |
|
| 135 | - $this->l->t('We cannot send you the auto-generated password. Please set a valid email address in your personal settings and try again.') |
|
| 136 | - ); |
|
| 137 | - } |
|
| 138 | - |
|
| 139 | - $passwordEvent = new GenerateSecurePasswordEvent(PasswordContext::SHARING); |
|
| 140 | - $this->eventDispatcher->dispatchTyped($passwordEvent); |
|
| 141 | - |
|
| 142 | - $password = $passwordEvent->getPassword(); |
|
| 143 | - if ($password === null) { |
|
| 144 | - $password = $this->secureRandom->generate(8, ISecureRandom::CHAR_HUMAN_READABLE); |
|
| 145 | - } |
|
| 146 | - |
|
| 147 | - return $password; |
|
| 148 | - } |
|
| 149 | - |
|
| 150 | - /** |
|
| 151 | - * create activity if a file/folder was shared by mail |
|
| 152 | - */ |
|
| 153 | - protected function createShareActivity(IShare $share, string $type = 'share'): void { |
|
| 154 | - $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
| 155 | - |
|
| 156 | - $this->publishActivity( |
|
| 157 | - $type === 'share' ? Activity::SUBJECT_SHARED_EMAIL_SELF : Activity::SUBJECT_UNSHARED_EMAIL_SELF, |
|
| 158 | - [$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()], |
|
| 159 | - $share->getSharedBy(), |
|
| 160 | - $share->getNode()->getId(), |
|
| 161 | - (string)$userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 162 | - ); |
|
| 163 | - |
|
| 164 | - if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
| 165 | - $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); |
|
| 166 | - $fileId = $share->getNode()->getId(); |
|
| 167 | - $nodes = $ownerFolder->getById($fileId); |
|
| 168 | - $ownerPath = $nodes[0]->getPath(); |
|
| 169 | - $this->publishActivity( |
|
| 170 | - $type === 'share' ? Activity::SUBJECT_SHARED_EMAIL_BY : Activity::SUBJECT_UNSHARED_EMAIL_BY, |
|
| 171 | - [$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()], |
|
| 172 | - $share->getShareOwner(), |
|
| 173 | - $fileId, |
|
| 174 | - (string)$ownerFolder->getRelativePath($ownerPath) |
|
| 175 | - ); |
|
| 176 | - } |
|
| 177 | - } |
|
| 178 | - |
|
| 179 | - /** |
|
| 180 | - * create activity if a file/folder was shared by mail |
|
| 181 | - */ |
|
| 182 | - protected function createPasswordSendActivity(IShare $share, string $sharedWith, bool $sendToSelf): void { |
|
| 183 | - $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
| 184 | - |
|
| 185 | - if ($sendToSelf) { |
|
| 186 | - $this->publishActivity( |
|
| 187 | - Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF, |
|
| 188 | - [$userFolder->getRelativePath($share->getNode()->getPath())], |
|
| 189 | - $share->getSharedBy(), |
|
| 190 | - $share->getNode()->getId(), |
|
| 191 | - (string)$userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 192 | - ); |
|
| 193 | - } else { |
|
| 194 | - $this->publishActivity( |
|
| 195 | - Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND, |
|
| 196 | - [$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith], |
|
| 197 | - $share->getSharedBy(), |
|
| 198 | - $share->getNode()->getId(), |
|
| 199 | - (string)$userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 200 | - ); |
|
| 201 | - } |
|
| 202 | - } |
|
| 203 | - |
|
| 204 | - |
|
| 205 | - /** |
|
| 206 | - * publish activity if a file/folder was shared by mail |
|
| 207 | - */ |
|
| 208 | - protected function publishActivity(string $subject, array $parameters, string $affectedUser, int $fileId, string $filePath): void { |
|
| 209 | - $event = $this->activityManager->generateEvent(); |
|
| 210 | - $event->setApp('sharebymail') |
|
| 211 | - ->setType('shared') |
|
| 212 | - ->setSubject($subject, $parameters) |
|
| 213 | - ->setAffectedUser($affectedUser) |
|
| 214 | - ->setObject('files', $fileId, $filePath); |
|
| 215 | - $this->activityManager->publish($event); |
|
| 216 | - } |
|
| 217 | - |
|
| 218 | - /** |
|
| 219 | - * @throws \Exception |
|
| 220 | - */ |
|
| 221 | - protected function createMailShare(IShare $share): int { |
|
| 222 | - $share->setToken($this->generateToken()); |
|
| 223 | - return $this->addShareToDB( |
|
| 224 | - $share->getNodeId(), |
|
| 225 | - $share->getNodeType(), |
|
| 226 | - $share->getSharedWith(), |
|
| 227 | - $share->getSharedBy(), |
|
| 228 | - $share->getShareOwner(), |
|
| 229 | - $share->getPermissions(), |
|
| 230 | - $share->getToken(), |
|
| 231 | - $share->getPassword(), |
|
| 232 | - $share->getPasswordExpirationTime(), |
|
| 233 | - $share->getSendPasswordByTalk(), |
|
| 234 | - $share->getHideDownload(), |
|
| 235 | - $share->getLabel(), |
|
| 236 | - $share->getExpirationDate(), |
|
| 237 | - $share->getNote(), |
|
| 238 | - $share->getAttributes(), |
|
| 239 | - $share->getMailSend(), |
|
| 240 | - ); |
|
| 241 | - } |
|
| 242 | - |
|
| 243 | - /** |
|
| 244 | - * @inheritDoc |
|
| 245 | - */ |
|
| 246 | - public function sendMailNotification(IShare $share): bool { |
|
| 247 | - $shareId = $share->getId(); |
|
| 248 | - |
|
| 249 | - $emails = $this->getSharedWithEmails($share); |
|
| 250 | - $validEmails = array_filter($emails, function (string $email) { |
|
| 251 | - return $this->emailValidator->isValid($email); |
|
| 252 | - }); |
|
| 253 | - |
|
| 254 | - if (count($validEmails) === 0) { |
|
| 255 | - $this->removeShareFromTable((int)$shareId); |
|
| 256 | - $e = new HintException('Failed to send share by mail. Could not find a valid email address: ' . join(', ', $emails), |
|
| 257 | - $this->l->t('Failed to send share by email. Got an invalid email address')); |
|
| 258 | - $this->logger->error('Failed to send share by mail. Could not find a valid email address ' . join(', ', $emails), [ |
|
| 259 | - 'app' => 'sharebymail', |
|
| 260 | - 'exception' => $e, |
|
| 261 | - ]); |
|
| 262 | - } |
|
| 263 | - |
|
| 264 | - try { |
|
| 265 | - $this->sendEmail($share, $validEmails); |
|
| 266 | - |
|
| 267 | - // If we have a password set, we send it to the recipient |
|
| 268 | - if ($share->getPassword() !== null) { |
|
| 269 | - // If share-by-talk password is enabled, we do not send the notification |
|
| 270 | - // to the recipient. They will have to request it to the owner after opening the link. |
|
| 271 | - // Secondly, if the password expiration is disabled, we send the notification to the recipient |
|
| 272 | - // Lastly, if the mail to recipient failed, we send the password to the owner as a fallback. |
|
| 273 | - // If a password expires, the recipient will still be able to request a new one via talk. |
|
| 274 | - $passwordExpire = $this->config->getSystemValue('sharing.enable_mail_link_password_expiration', false); |
|
| 275 | - $passwordEnforced = $this->shareManager->shareApiLinkEnforcePassword(); |
|
| 276 | - if ($passwordExpire === false || $share->getSendPasswordByTalk()) { |
|
| 277 | - $send = $this->sendPassword($share, $share->getPassword(), $validEmails); |
|
| 278 | - if ($passwordEnforced && $send === false) { |
|
| 279 | - $this->sendPasswordToOwner($share, $share->getPassword()); |
|
| 280 | - } |
|
| 281 | - } |
|
| 282 | - } |
|
| 283 | - |
|
| 284 | - return true; |
|
| 285 | - } catch (HintException $hintException) { |
|
| 286 | - $this->logger->error('Failed to send share by mail.', [ |
|
| 287 | - 'app' => 'sharebymail', |
|
| 288 | - 'exception' => $hintException, |
|
| 289 | - ]); |
|
| 290 | - $this->removeShareFromTable((int)$shareId); |
|
| 291 | - throw $hintException; |
|
| 292 | - } catch (\Exception $e) { |
|
| 293 | - $this->logger->error('Failed to send share by mail.', [ |
|
| 294 | - 'app' => 'sharebymail', |
|
| 295 | - 'exception' => $e, |
|
| 296 | - ]); |
|
| 297 | - $this->removeShareFromTable((int)$shareId); |
|
| 298 | - throw new HintException( |
|
| 299 | - 'Failed to send share by mail', |
|
| 300 | - $this->l->t('Failed to send share by email'), |
|
| 301 | - 0, |
|
| 302 | - $e, |
|
| 303 | - ); |
|
| 304 | - } |
|
| 305 | - return false; |
|
| 306 | - } |
|
| 307 | - |
|
| 308 | - /** |
|
| 309 | - * @param IShare $share The share to send the email for |
|
| 310 | - * @param array $emails The email addresses to send the email to |
|
| 311 | - */ |
|
| 312 | - protected function sendEmail(IShare $share, array $emails): void { |
|
| 313 | - $link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', [ |
|
| 314 | - 'token' => $share->getToken() |
|
| 315 | - ]); |
|
| 316 | - |
|
| 317 | - $expiration = $share->getExpirationDate(); |
|
| 318 | - $filename = $share->getNode()->getName(); |
|
| 319 | - $initiator = $share->getSharedBy(); |
|
| 320 | - $note = $share->getNote(); |
|
| 321 | - $shareWith = $share->getSharedWith(); |
|
| 322 | - |
|
| 323 | - $initiatorUser = $this->userManager->get($initiator); |
|
| 324 | - $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
| 325 | - $message = $this->mailer->createMessage(); |
|
| 326 | - |
|
| 327 | - $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientNotification', [ |
|
| 328 | - 'filename' => $filename, |
|
| 329 | - 'link' => $link, |
|
| 330 | - 'initiator' => $initiatorDisplayName, |
|
| 331 | - 'expiration' => $expiration, |
|
| 332 | - 'shareWith' => $shareWith, |
|
| 333 | - 'note' => $note |
|
| 334 | - ]); |
|
| 335 | - |
|
| 336 | - $emailTemplate->setSubject($this->l->t('%1$s shared %2$s with you', [$initiatorDisplayName, $filename])); |
|
| 337 | - $emailTemplate->addHeader(); |
|
| 338 | - $emailTemplate->addHeading($this->l->t('%1$s shared %2$s with you', [$initiatorDisplayName, $filename]), false); |
|
| 339 | - |
|
| 340 | - if ($note !== '') { |
|
| 341 | - $emailTemplate->addBodyListItem( |
|
| 342 | - htmlspecialchars($note), |
|
| 343 | - $this->l->t('Note:'), |
|
| 344 | - $this->getAbsoluteImagePath('caldav/description.png'), |
|
| 345 | - $note |
|
| 346 | - ); |
|
| 347 | - } |
|
| 348 | - |
|
| 349 | - if ($expiration !== null) { |
|
| 350 | - $dateString = (string)$this->l->l('date', $expiration, ['width' => 'medium']); |
|
| 351 | - $emailTemplate->addBodyListItem( |
|
| 352 | - $this->l->t('This share is valid until %s at midnight', [$dateString]), |
|
| 353 | - $this->l->t('Expiration:'), |
|
| 354 | - $this->getAbsoluteImagePath('caldav/time.png'), |
|
| 355 | - ); |
|
| 356 | - } |
|
| 357 | - |
|
| 358 | - $emailTemplate->addBodyButton( |
|
| 359 | - $this->l->t('Open %s', [$filename]), |
|
| 360 | - $link |
|
| 361 | - ); |
|
| 362 | - |
|
| 363 | - // If multiple recipients are given, we send the mail to all of them |
|
| 364 | - if (count($emails) > 1) { |
|
| 365 | - // We do not want to expose the email addresses of the other recipients |
|
| 366 | - $message->setBcc($emails); |
|
| 367 | - } else { |
|
| 368 | - $message->setTo($emails); |
|
| 369 | - } |
|
| 370 | - |
|
| 371 | - // The "From" contains the sharers name |
|
| 372 | - $instanceName = $this->defaults->getName(); |
|
| 373 | - $senderName = $instanceName; |
|
| 374 | - if ($this->settingsManager->replyToInitiator()) { |
|
| 375 | - $senderName = $this->l->t( |
|
| 376 | - '%1$s via %2$s', |
|
| 377 | - [ |
|
| 378 | - $initiatorDisplayName, |
|
| 379 | - $instanceName |
|
| 380 | - ] |
|
| 381 | - ); |
|
| 382 | - } |
|
| 383 | - $message->setFrom([Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
| 384 | - |
|
| 385 | - // The "Reply-To" is set to the sharer if an mail address is configured |
|
| 386 | - // also the default footer contains a "Do not reply" which needs to be adjusted. |
|
| 387 | - if ($initiatorUser && $this->settingsManager->replyToInitiator()) { |
|
| 388 | - $initiatorEmail = $initiatorUser->getEMailAddress(); |
|
| 389 | - if ($initiatorEmail !== null) { |
|
| 390 | - $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]); |
|
| 391 | - $emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')); |
|
| 392 | - } else { |
|
| 393 | - $emailTemplate->addFooter(); |
|
| 394 | - } |
|
| 395 | - } else { |
|
| 396 | - $emailTemplate->addFooter(); |
|
| 397 | - } |
|
| 398 | - |
|
| 399 | - $message->useTemplate($emailTemplate); |
|
| 400 | - $failedRecipients = $this->mailer->send($message); |
|
| 401 | - if (!empty($failedRecipients)) { |
|
| 402 | - $this->logger->error('Share notification mail could not be sent to: ' . implode(', ', $failedRecipients)); |
|
| 403 | - return; |
|
| 404 | - } |
|
| 405 | - } |
|
| 406 | - |
|
| 407 | - /** |
|
| 408 | - * Send password to recipient of a mail share |
|
| 409 | - * Will return false if |
|
| 410 | - * 1. the password is empty |
|
| 411 | - * 2. the setting to send the password by mail is disabled |
|
| 412 | - * 3. the share is set to send the password by talk |
|
| 413 | - * |
|
| 414 | - * @param IShare $share |
|
| 415 | - * @param string $password |
|
| 416 | - * @param array $emails |
|
| 417 | - * @return bool |
|
| 418 | - */ |
|
| 419 | - protected function sendPassword(IShare $share, string $password, array $emails): bool { |
|
| 420 | - $filename = $share->getNode()->getName(); |
|
| 421 | - $initiator = $share->getSharedBy(); |
|
| 422 | - $shareWith = $share->getSharedWith(); |
|
| 423 | - |
|
| 424 | - if ($password === '' || $this->settingsManager->sendPasswordByMail() === false || $share->getSendPasswordByTalk()) { |
|
| 425 | - return false; |
|
| 426 | - } |
|
| 427 | - |
|
| 428 | - $initiatorUser = $this->userManager->get($initiator); |
|
| 429 | - $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
| 430 | - $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
| 431 | - |
|
| 432 | - $plainBodyPart = $this->l->t('%1$s shared %2$s with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]); |
|
| 433 | - $htmlBodyPart = $this->l->t('%1$s shared %2$s with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]); |
|
| 434 | - |
|
| 435 | - $message = $this->mailer->createMessage(); |
|
| 436 | - |
|
| 437 | - $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientPasswordNotification', [ |
|
| 438 | - 'filename' => $filename, |
|
| 439 | - 'password' => $password, |
|
| 440 | - 'initiator' => $initiatorDisplayName, |
|
| 441 | - 'initiatorEmail' => $initiatorEmailAddress, |
|
| 442 | - 'shareWith' => $shareWith, |
|
| 443 | - ]); |
|
| 444 | - |
|
| 445 | - $emailTemplate->setSubject($this->l->t('Password to access %1$s shared to you by %2$s', [$filename, $initiatorDisplayName])); |
|
| 446 | - $emailTemplate->addHeader(); |
|
| 447 | - $emailTemplate->addHeading($this->l->t('Password to access %s', [$filename]), false); |
|
| 448 | - $emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart); |
|
| 449 | - $emailTemplate->addBodyText($this->l->t('It is protected with the following password:')); |
|
| 450 | - $emailTemplate->addBodyText($password); |
|
| 451 | - |
|
| 452 | - if ($this->config->getSystemValue('sharing.enable_mail_link_password_expiration', false) === true) { |
|
| 453 | - $expirationTime = new \DateTime(); |
|
| 454 | - $expirationInterval = $this->config->getSystemValue('sharing.mail_link_password_expiration_interval', 3600); |
|
| 455 | - $expirationTime = $expirationTime->add(new \DateInterval('PT' . $expirationInterval . 'S')); |
|
| 456 | - $emailTemplate->addBodyText($this->l->t('This password will expire at %s', [$expirationTime->format('r')])); |
|
| 457 | - } |
|
| 458 | - |
|
| 459 | - // If multiple recipients are given, we send the mail to all of them |
|
| 460 | - if (count($emails) > 1) { |
|
| 461 | - // We do not want to expose the email addresses of the other recipients |
|
| 462 | - $message->setBcc($emails); |
|
| 463 | - } else { |
|
| 464 | - $message->setTo($emails); |
|
| 465 | - } |
|
| 466 | - |
|
| 467 | - // The "From" contains the sharers name |
|
| 468 | - $instanceName = $this->defaults->getName(); |
|
| 469 | - $senderName = $instanceName; |
|
| 470 | - if ($this->settingsManager->replyToInitiator()) { |
|
| 471 | - $senderName = $this->l->t( |
|
| 472 | - '%1$s via %2$s', |
|
| 473 | - [ |
|
| 474 | - $initiatorDisplayName, |
|
| 475 | - $instanceName |
|
| 476 | - ] |
|
| 477 | - ); |
|
| 478 | - } |
|
| 479 | - $message->setFrom([Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
| 480 | - |
|
| 481 | - // The "Reply-To" is set to the sharer if an mail address is configured |
|
| 482 | - // also the default footer contains a "Do not reply" which needs to be adjusted. |
|
| 483 | - if ($initiatorUser && $this->settingsManager->replyToInitiator()) { |
|
| 484 | - $initiatorEmail = $initiatorUser->getEMailAddress(); |
|
| 485 | - if ($initiatorEmail !== null) { |
|
| 486 | - $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]); |
|
| 487 | - $emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')); |
|
| 488 | - } else { |
|
| 489 | - $emailTemplate->addFooter(); |
|
| 490 | - } |
|
| 491 | - } else { |
|
| 492 | - $emailTemplate->addFooter(); |
|
| 493 | - } |
|
| 494 | - |
|
| 495 | - $message->useTemplate($emailTemplate); |
|
| 496 | - $failedRecipients = $this->mailer->send($message); |
|
| 497 | - if (!empty($failedRecipients)) { |
|
| 498 | - $this->logger->error('Share password mail could not be sent to: ' . implode(', ', $failedRecipients)); |
|
| 499 | - return false; |
|
| 500 | - } |
|
| 501 | - |
|
| 502 | - $this->createPasswordSendActivity($share, $shareWith, false); |
|
| 503 | - return true; |
|
| 504 | - } |
|
| 505 | - |
|
| 506 | - protected function sendNote(IShare $share): void { |
|
| 507 | - $recipient = $share->getSharedWith(); |
|
| 508 | - |
|
| 509 | - |
|
| 510 | - $filename = $share->getNode()->getName(); |
|
| 511 | - $initiator = $share->getSharedBy(); |
|
| 512 | - $note = $share->getNote(); |
|
| 513 | - |
|
| 514 | - $initiatorUser = $this->userManager->get($initiator); |
|
| 515 | - $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
| 516 | - $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
| 517 | - |
|
| 518 | - $plainHeading = $this->l->t('%1$s shared %2$s with you and wants to add:', [$initiatorDisplayName, $filename]); |
|
| 519 | - $htmlHeading = $this->l->t('%1$s shared %2$s with you and wants to add', [$initiatorDisplayName, $filename]); |
|
| 520 | - |
|
| 521 | - $message = $this->mailer->createMessage(); |
|
| 522 | - |
|
| 523 | - $emailTemplate = $this->mailer->createEMailTemplate('shareByMail.sendNote'); |
|
| 524 | - |
|
| 525 | - $emailTemplate->setSubject($this->l->t('%s added a note to a file shared with you', [$initiatorDisplayName])); |
|
| 526 | - $emailTemplate->addHeader(); |
|
| 527 | - $emailTemplate->addHeading(htmlspecialchars($htmlHeading), $plainHeading); |
|
| 528 | - $emailTemplate->addBodyText(htmlspecialchars($note), $note); |
|
| 529 | - |
|
| 530 | - $link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', |
|
| 531 | - ['token' => $share->getToken()]); |
|
| 532 | - $emailTemplate->addBodyButton( |
|
| 533 | - $this->l->t('Open %s', [$filename]), |
|
| 534 | - $link |
|
| 535 | - ); |
|
| 536 | - |
|
| 537 | - // The "From" contains the sharers name |
|
| 538 | - $instanceName = $this->defaults->getName(); |
|
| 539 | - $senderName = $instanceName; |
|
| 540 | - if ($this->settingsManager->replyToInitiator()) { |
|
| 541 | - $senderName = $this->l->t( |
|
| 542 | - '%1$s via %2$s', |
|
| 543 | - [ |
|
| 544 | - $initiatorDisplayName, |
|
| 545 | - $instanceName |
|
| 546 | - ] |
|
| 547 | - ); |
|
| 548 | - } |
|
| 549 | - $message->setFrom([Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
| 550 | - if ($this->settingsManager->replyToInitiator() && $initiatorEmailAddress !== null) { |
|
| 551 | - $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]); |
|
| 552 | - $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan()); |
|
| 553 | - } else { |
|
| 554 | - $emailTemplate->addFooter(); |
|
| 555 | - } |
|
| 556 | - |
|
| 557 | - $message->setTo([$recipient]); |
|
| 558 | - $message->useTemplate($emailTemplate); |
|
| 559 | - $this->mailer->send($message); |
|
| 560 | - } |
|
| 561 | - |
|
| 562 | - /** |
|
| 563 | - * send auto generated password to the owner. This happens if the admin enforces |
|
| 564 | - * a password for mail shares and forbid to send the password by mail to the recipient |
|
| 565 | - * |
|
| 566 | - * @throws \Exception |
|
| 567 | - */ |
|
| 568 | - protected function sendPasswordToOwner(IShare $share, string $password): bool { |
|
| 569 | - $filename = $share->getNode()->getName(); |
|
| 570 | - $initiator = $this->userManager->get($share->getSharedBy()); |
|
| 571 | - $initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null; |
|
| 572 | - $initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy(); |
|
| 573 | - $shareWith = implode(', ', $this->getSharedWithEmails($share)); |
|
| 574 | - |
|
| 575 | - if ($initiatorEMailAddress === null) { |
|
| 576 | - throw new \Exception( |
|
| 577 | - $this->l->t('We cannot send you the auto-generated password. Please set a valid email address in your personal settings and try again.') |
|
| 578 | - ); |
|
| 579 | - } |
|
| 580 | - |
|
| 581 | - $bodyPart = $this->l->t('You just shared %1$s with %2$s. The share was already sent to the recipient. Due to the security policies defined by the administrator of %3$s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient.', [$filename, $shareWith, $this->defaults->getName()]); |
|
| 582 | - |
|
| 583 | - $message = $this->mailer->createMessage(); |
|
| 584 | - $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.OwnerPasswordNotification', [ |
|
| 585 | - 'filename' => $filename, |
|
| 586 | - 'password' => $password, |
|
| 587 | - 'initiator' => $initiatorDisplayName, |
|
| 588 | - 'initiatorEmail' => $initiatorEMailAddress, |
|
| 589 | - 'shareWith' => $shareWith, |
|
| 590 | - ]); |
|
| 591 | - |
|
| 592 | - $emailTemplate->setSubject($this->l->t('Password to access %1$s shared by you with %2$s', [$filename, $shareWith])); |
|
| 593 | - $emailTemplate->addHeader(); |
|
| 594 | - $emailTemplate->addHeading($this->l->t('Password to access %s', [$filename]), false); |
|
| 595 | - $emailTemplate->addBodyText($bodyPart); |
|
| 596 | - $emailTemplate->addBodyText($this->l->t('This is the password:')); |
|
| 597 | - $emailTemplate->addBodyText($password); |
|
| 598 | - |
|
| 599 | - if ($this->config->getSystemValue('sharing.enable_mail_link_password_expiration', false) === true) { |
|
| 600 | - $expirationTime = new \DateTime(); |
|
| 601 | - $expirationInterval = $this->config->getSystemValue('sharing.mail_link_password_expiration_interval', 3600); |
|
| 602 | - $expirationTime = $expirationTime->add(new \DateInterval('PT' . $expirationInterval . 'S')); |
|
| 603 | - $emailTemplate->addBodyText($this->l->t('This password will expire at %s', [$expirationTime->format('r')])); |
|
| 604 | - } |
|
| 605 | - |
|
| 606 | - $emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.')); |
|
| 607 | - |
|
| 608 | - $emailTemplate->addFooter(); |
|
| 609 | - |
|
| 610 | - $instanceName = $this->defaults->getName(); |
|
| 611 | - $senderName = $this->l->t( |
|
| 612 | - '%1$s via %2$s', |
|
| 613 | - [ |
|
| 614 | - $initiatorDisplayName, |
|
| 615 | - $instanceName |
|
| 616 | - ] |
|
| 617 | - ); |
|
| 618 | - $message->setFrom([Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
| 619 | - $message->setTo([$initiatorEMailAddress => $initiatorDisplayName]); |
|
| 620 | - $message->useTemplate($emailTemplate); |
|
| 621 | - $this->mailer->send($message); |
|
| 622 | - |
|
| 623 | - $this->createPasswordSendActivity($share, $shareWith, true); |
|
| 624 | - |
|
| 625 | - return true; |
|
| 626 | - } |
|
| 627 | - |
|
| 628 | - private function getAbsoluteImagePath(string $path):string { |
|
| 629 | - return $this->urlGenerator->getAbsoluteURL( |
|
| 630 | - $this->urlGenerator->imagePath('core', $path) |
|
| 631 | - ); |
|
| 632 | - } |
|
| 633 | - |
|
| 634 | - /** |
|
| 635 | - * generate share token |
|
| 636 | - */ |
|
| 637 | - protected function generateToken(int $size = 15): string { |
|
| 638 | - $token = $this->secureRandom->generate($size, ISecureRandom::CHAR_HUMAN_READABLE); |
|
| 639 | - return $token; |
|
| 640 | - } |
|
| 641 | - |
|
| 642 | - public function getChildren(IShare $parent): array { |
|
| 643 | - $children = []; |
|
| 644 | - |
|
| 645 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 646 | - $qb->select('*') |
|
| 647 | - ->from('share') |
|
| 648 | - ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) |
|
| 649 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL))) |
|
| 650 | - ->orderBy('id'); |
|
| 651 | - |
|
| 652 | - $cursor = $qb->executeQuery(); |
|
| 653 | - while ($data = $cursor->fetch()) { |
|
| 654 | - $children[] = $this->createShareObject($data); |
|
| 655 | - } |
|
| 656 | - $cursor->closeCursor(); |
|
| 657 | - |
|
| 658 | - return $children; |
|
| 659 | - } |
|
| 660 | - |
|
| 661 | - /** |
|
| 662 | - * Add share to the database and return the ID |
|
| 663 | - */ |
|
| 664 | - protected function addShareToDB( |
|
| 665 | - ?int $itemSource, |
|
| 666 | - ?string $itemType, |
|
| 667 | - ?string $shareWith, |
|
| 668 | - ?string $sharedBy, |
|
| 669 | - ?string $uidOwner, |
|
| 670 | - ?int $permissions, |
|
| 671 | - ?string $token, |
|
| 672 | - ?string $password, |
|
| 673 | - ?\DateTimeInterface $passwordExpirationTime, |
|
| 674 | - ?bool $sendPasswordByTalk, |
|
| 675 | - ?bool $hideDownload, |
|
| 676 | - ?string $label, |
|
| 677 | - ?\DateTimeInterface $expirationTime, |
|
| 678 | - ?string $note = '', |
|
| 679 | - ?IAttributes $attributes = null, |
|
| 680 | - ?bool $mailSend = true, |
|
| 681 | - ): int { |
|
| 682 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 683 | - $qb->insert('share') |
|
| 684 | - ->setValue('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL)) |
|
| 685 | - ->setValue('item_type', $qb->createNamedParameter($itemType)) |
|
| 686 | - ->setValue('item_source', $qb->createNamedParameter($itemSource)) |
|
| 687 | - ->setValue('file_source', $qb->createNamedParameter($itemSource)) |
|
| 688 | - ->setValue('share_with', $qb->createNamedParameter($shareWith)) |
|
| 689 | - ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) |
|
| 690 | - ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) |
|
| 691 | - ->setValue('permissions', $qb->createNamedParameter($permissions)) |
|
| 692 | - ->setValue('token', $qb->createNamedParameter($token)) |
|
| 693 | - ->setValue('password', $qb->createNamedParameter($password)) |
|
| 694 | - ->setValue('password_expiration_time', $qb->createNamedParameter($passwordExpirationTime, IQueryBuilder::PARAM_DATETIME_MUTABLE)) |
|
| 695 | - ->setValue('password_by_talk', $qb->createNamedParameter($sendPasswordByTalk, IQueryBuilder::PARAM_BOOL)) |
|
| 696 | - ->setValue('stime', $qb->createNamedParameter(time())) |
|
| 697 | - ->setValue('hide_download', $qb->createNamedParameter((int)$hideDownload, IQueryBuilder::PARAM_INT)) |
|
| 698 | - ->setValue('label', $qb->createNamedParameter($label)) |
|
| 699 | - ->setValue('note', $qb->createNamedParameter($note)) |
|
| 700 | - ->setValue('mail_send', $qb->createNamedParameter((int)$mailSend, IQueryBuilder::PARAM_INT)); |
|
| 701 | - |
|
| 702 | - // set share attributes |
|
| 703 | - $shareAttributes = $this->formatShareAttributes($attributes); |
|
| 704 | - |
|
| 705 | - $qb->setValue('attributes', $qb->createNamedParameter($shareAttributes)); |
|
| 706 | - if ($expirationTime !== null) { |
|
| 707 | - $qb->setValue('expiration', $qb->createNamedParameter($expirationTime, IQueryBuilder::PARAM_DATETIME_MUTABLE)); |
|
| 708 | - } |
|
| 709 | - |
|
| 710 | - $qb->executeStatement(); |
|
| 711 | - return $qb->getLastInsertId(); |
|
| 712 | - } |
|
| 713 | - |
|
| 714 | - /** |
|
| 715 | - * Update a share |
|
| 716 | - */ |
|
| 717 | - public function update(IShare $share, ?string $plainTextPassword = null): IShare { |
|
| 718 | - $originalShare = $this->getShareById($share->getId()); |
|
| 719 | - |
|
| 720 | - // a real password was given |
|
| 721 | - $validPassword = $plainTextPassword !== null && $plainTextPassword !== ''; |
|
| 722 | - |
|
| 723 | - if ($validPassword && ($originalShare->getPassword() !== $share->getPassword() |
|
| 724 | - || ($originalShare->getSendPasswordByTalk() && !$share->getSendPasswordByTalk()))) { |
|
| 725 | - $emails = $this->getSharedWithEmails($share); |
|
| 726 | - $validEmails = array_filter($emails, function ($email) { |
|
| 727 | - return $this->emailValidator->isValid($email); |
|
| 728 | - }); |
|
| 729 | - $this->sendPassword($share, $plainTextPassword, $validEmails); |
|
| 730 | - } |
|
| 731 | - |
|
| 732 | - $shareAttributes = $this->formatShareAttributes($share->getAttributes()); |
|
| 733 | - |
|
| 734 | - /* |
|
| 49 | + /** |
|
| 50 | + * Return the identifier of this provider. |
|
| 51 | + * |
|
| 52 | + * @return string Containing only [a-zA-Z0-9] |
|
| 53 | + */ |
|
| 54 | + public function identifier(): string { |
|
| 55 | + return 'ocMailShare'; |
|
| 56 | + } |
|
| 57 | + |
|
| 58 | + public function __construct( |
|
| 59 | + private IConfig $config, |
|
| 60 | + private IDBConnection $dbConnection, |
|
| 61 | + private ISecureRandom $secureRandom, |
|
| 62 | + private IUserManager $userManager, |
|
| 63 | + private IRootFolder $rootFolder, |
|
| 64 | + private IL10N $l, |
|
| 65 | + private LoggerInterface $logger, |
|
| 66 | + private IMailer $mailer, |
|
| 67 | + private IURLGenerator $urlGenerator, |
|
| 68 | + private IManager $activityManager, |
|
| 69 | + private SettingsManager $settingsManager, |
|
| 70 | + private Defaults $defaults, |
|
| 71 | + private IHasher $hasher, |
|
| 72 | + private IEventDispatcher $eventDispatcher, |
|
| 73 | + private IShareManager $shareManager, |
|
| 74 | + private IEmailValidator $emailValidator, |
|
| 75 | + ) { |
|
| 76 | + } |
|
| 77 | + |
|
| 78 | + /** |
|
| 79 | + * Share a path |
|
| 80 | + * |
|
| 81 | + * @throws ShareNotFound |
|
| 82 | + * @throws \Exception |
|
| 83 | + */ |
|
| 84 | + public function create(IShare $share): IShare { |
|
| 85 | + $shareWith = $share->getSharedWith(); |
|
| 86 | + // Check if file is not already shared with the given email, |
|
| 87 | + // if we have an email at all. |
|
| 88 | + $alreadyShared = $this->getSharedWith($shareWith, IShare::TYPE_EMAIL, $share->getNode(), 1, 0); |
|
| 89 | + if ($shareWith !== '' && !empty($alreadyShared)) { |
|
| 90 | + $message = 'Sharing %1$s failed, because this item is already shared with the account %2$s'; |
|
| 91 | + $message_t = $this->l->t('Sharing %1$s failed, because this item is already shared with the account %2$s', [$share->getNode()->getName(), $shareWith]); |
|
| 92 | + $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); |
|
| 93 | + throw new \Exception($message_t); |
|
| 94 | + } |
|
| 95 | + |
|
| 96 | + // if the admin enforces a password for all mail shares we create a |
|
| 97 | + // random password and send it to the recipient |
|
| 98 | + $password = $share->getPassword() ?: ''; |
|
| 99 | + $passwordEnforced = $this->shareManager->shareApiLinkEnforcePassword(); |
|
| 100 | + if ($passwordEnforced && empty($password)) { |
|
| 101 | + $password = $this->autoGeneratePassword($share); |
|
| 102 | + } |
|
| 103 | + |
|
| 104 | + if (!empty($password)) { |
|
| 105 | + $share->setPassword($this->hasher->hash($password)); |
|
| 106 | + } |
|
| 107 | + |
|
| 108 | + $shareId = $this->createMailShare($share); |
|
| 109 | + |
|
| 110 | + $this->createShareActivity($share); |
|
| 111 | + $data = $this->getRawShare($shareId); |
|
| 112 | + |
|
| 113 | + // Temporary set the clear password again to send it by mail |
|
| 114 | + // This need to be done after the share was created in the database |
|
| 115 | + // as the password is hashed in between. |
|
| 116 | + if (!empty($password)) { |
|
| 117 | + $data['password'] = $password; |
|
| 118 | + } |
|
| 119 | + |
|
| 120 | + return $this->createShareObject($data); |
|
| 121 | + } |
|
| 122 | + |
|
| 123 | + /** |
|
| 124 | + * auto generate password in case of password enforcement on mail shares |
|
| 125 | + * |
|
| 126 | + * @throws \Exception |
|
| 127 | + */ |
|
| 128 | + protected function autoGeneratePassword(IShare $share): string { |
|
| 129 | + $initiatorUser = $this->userManager->get($share->getSharedBy()); |
|
| 130 | + $initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
| 131 | + $allowPasswordByMail = $this->settingsManager->sendPasswordByMail(); |
|
| 132 | + |
|
| 133 | + if ($initiatorEMailAddress === null && !$allowPasswordByMail) { |
|
| 134 | + throw new \Exception( |
|
| 135 | + $this->l->t('We cannot send you the auto-generated password. Please set a valid email address in your personal settings and try again.') |
|
| 136 | + ); |
|
| 137 | + } |
|
| 138 | + |
|
| 139 | + $passwordEvent = new GenerateSecurePasswordEvent(PasswordContext::SHARING); |
|
| 140 | + $this->eventDispatcher->dispatchTyped($passwordEvent); |
|
| 141 | + |
|
| 142 | + $password = $passwordEvent->getPassword(); |
|
| 143 | + if ($password === null) { |
|
| 144 | + $password = $this->secureRandom->generate(8, ISecureRandom::CHAR_HUMAN_READABLE); |
|
| 145 | + } |
|
| 146 | + |
|
| 147 | + return $password; |
|
| 148 | + } |
|
| 149 | + |
|
| 150 | + /** |
|
| 151 | + * create activity if a file/folder was shared by mail |
|
| 152 | + */ |
|
| 153 | + protected function createShareActivity(IShare $share, string $type = 'share'): void { |
|
| 154 | + $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
| 155 | + |
|
| 156 | + $this->publishActivity( |
|
| 157 | + $type === 'share' ? Activity::SUBJECT_SHARED_EMAIL_SELF : Activity::SUBJECT_UNSHARED_EMAIL_SELF, |
|
| 158 | + [$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()], |
|
| 159 | + $share->getSharedBy(), |
|
| 160 | + $share->getNode()->getId(), |
|
| 161 | + (string)$userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 162 | + ); |
|
| 163 | + |
|
| 164 | + if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
| 165 | + $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); |
|
| 166 | + $fileId = $share->getNode()->getId(); |
|
| 167 | + $nodes = $ownerFolder->getById($fileId); |
|
| 168 | + $ownerPath = $nodes[0]->getPath(); |
|
| 169 | + $this->publishActivity( |
|
| 170 | + $type === 'share' ? Activity::SUBJECT_SHARED_EMAIL_BY : Activity::SUBJECT_UNSHARED_EMAIL_BY, |
|
| 171 | + [$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()], |
|
| 172 | + $share->getShareOwner(), |
|
| 173 | + $fileId, |
|
| 174 | + (string)$ownerFolder->getRelativePath($ownerPath) |
|
| 175 | + ); |
|
| 176 | + } |
|
| 177 | + } |
|
| 178 | + |
|
| 179 | + /** |
|
| 180 | + * create activity if a file/folder was shared by mail |
|
| 181 | + */ |
|
| 182 | + protected function createPasswordSendActivity(IShare $share, string $sharedWith, bool $sendToSelf): void { |
|
| 183 | + $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
| 184 | + |
|
| 185 | + if ($sendToSelf) { |
|
| 186 | + $this->publishActivity( |
|
| 187 | + Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF, |
|
| 188 | + [$userFolder->getRelativePath($share->getNode()->getPath())], |
|
| 189 | + $share->getSharedBy(), |
|
| 190 | + $share->getNode()->getId(), |
|
| 191 | + (string)$userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 192 | + ); |
|
| 193 | + } else { |
|
| 194 | + $this->publishActivity( |
|
| 195 | + Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND, |
|
| 196 | + [$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith], |
|
| 197 | + $share->getSharedBy(), |
|
| 198 | + $share->getNode()->getId(), |
|
| 199 | + (string)$userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 200 | + ); |
|
| 201 | + } |
|
| 202 | + } |
|
| 203 | + |
|
| 204 | + |
|
| 205 | + /** |
|
| 206 | + * publish activity if a file/folder was shared by mail |
|
| 207 | + */ |
|
| 208 | + protected function publishActivity(string $subject, array $parameters, string $affectedUser, int $fileId, string $filePath): void { |
|
| 209 | + $event = $this->activityManager->generateEvent(); |
|
| 210 | + $event->setApp('sharebymail') |
|
| 211 | + ->setType('shared') |
|
| 212 | + ->setSubject($subject, $parameters) |
|
| 213 | + ->setAffectedUser($affectedUser) |
|
| 214 | + ->setObject('files', $fileId, $filePath); |
|
| 215 | + $this->activityManager->publish($event); |
|
| 216 | + } |
|
| 217 | + |
|
| 218 | + /** |
|
| 219 | + * @throws \Exception |
|
| 220 | + */ |
|
| 221 | + protected function createMailShare(IShare $share): int { |
|
| 222 | + $share->setToken($this->generateToken()); |
|
| 223 | + return $this->addShareToDB( |
|
| 224 | + $share->getNodeId(), |
|
| 225 | + $share->getNodeType(), |
|
| 226 | + $share->getSharedWith(), |
|
| 227 | + $share->getSharedBy(), |
|
| 228 | + $share->getShareOwner(), |
|
| 229 | + $share->getPermissions(), |
|
| 230 | + $share->getToken(), |
|
| 231 | + $share->getPassword(), |
|
| 232 | + $share->getPasswordExpirationTime(), |
|
| 233 | + $share->getSendPasswordByTalk(), |
|
| 234 | + $share->getHideDownload(), |
|
| 235 | + $share->getLabel(), |
|
| 236 | + $share->getExpirationDate(), |
|
| 237 | + $share->getNote(), |
|
| 238 | + $share->getAttributes(), |
|
| 239 | + $share->getMailSend(), |
|
| 240 | + ); |
|
| 241 | + } |
|
| 242 | + |
|
| 243 | + /** |
|
| 244 | + * @inheritDoc |
|
| 245 | + */ |
|
| 246 | + public function sendMailNotification(IShare $share): bool { |
|
| 247 | + $shareId = $share->getId(); |
|
| 248 | + |
|
| 249 | + $emails = $this->getSharedWithEmails($share); |
|
| 250 | + $validEmails = array_filter($emails, function (string $email) { |
|
| 251 | + return $this->emailValidator->isValid($email); |
|
| 252 | + }); |
|
| 253 | + |
|
| 254 | + if (count($validEmails) === 0) { |
|
| 255 | + $this->removeShareFromTable((int)$shareId); |
|
| 256 | + $e = new HintException('Failed to send share by mail. Could not find a valid email address: ' . join(', ', $emails), |
|
| 257 | + $this->l->t('Failed to send share by email. Got an invalid email address')); |
|
| 258 | + $this->logger->error('Failed to send share by mail. Could not find a valid email address ' . join(', ', $emails), [ |
|
| 259 | + 'app' => 'sharebymail', |
|
| 260 | + 'exception' => $e, |
|
| 261 | + ]); |
|
| 262 | + } |
|
| 263 | + |
|
| 264 | + try { |
|
| 265 | + $this->sendEmail($share, $validEmails); |
|
| 266 | + |
|
| 267 | + // If we have a password set, we send it to the recipient |
|
| 268 | + if ($share->getPassword() !== null) { |
|
| 269 | + // If share-by-talk password is enabled, we do not send the notification |
|
| 270 | + // to the recipient. They will have to request it to the owner after opening the link. |
|
| 271 | + // Secondly, if the password expiration is disabled, we send the notification to the recipient |
|
| 272 | + // Lastly, if the mail to recipient failed, we send the password to the owner as a fallback. |
|
| 273 | + // If a password expires, the recipient will still be able to request a new one via talk. |
|
| 274 | + $passwordExpire = $this->config->getSystemValue('sharing.enable_mail_link_password_expiration', false); |
|
| 275 | + $passwordEnforced = $this->shareManager->shareApiLinkEnforcePassword(); |
|
| 276 | + if ($passwordExpire === false || $share->getSendPasswordByTalk()) { |
|
| 277 | + $send = $this->sendPassword($share, $share->getPassword(), $validEmails); |
|
| 278 | + if ($passwordEnforced && $send === false) { |
|
| 279 | + $this->sendPasswordToOwner($share, $share->getPassword()); |
|
| 280 | + } |
|
| 281 | + } |
|
| 282 | + } |
|
| 283 | + |
|
| 284 | + return true; |
|
| 285 | + } catch (HintException $hintException) { |
|
| 286 | + $this->logger->error('Failed to send share by mail.', [ |
|
| 287 | + 'app' => 'sharebymail', |
|
| 288 | + 'exception' => $hintException, |
|
| 289 | + ]); |
|
| 290 | + $this->removeShareFromTable((int)$shareId); |
|
| 291 | + throw $hintException; |
|
| 292 | + } catch (\Exception $e) { |
|
| 293 | + $this->logger->error('Failed to send share by mail.', [ |
|
| 294 | + 'app' => 'sharebymail', |
|
| 295 | + 'exception' => $e, |
|
| 296 | + ]); |
|
| 297 | + $this->removeShareFromTable((int)$shareId); |
|
| 298 | + throw new HintException( |
|
| 299 | + 'Failed to send share by mail', |
|
| 300 | + $this->l->t('Failed to send share by email'), |
|
| 301 | + 0, |
|
| 302 | + $e, |
|
| 303 | + ); |
|
| 304 | + } |
|
| 305 | + return false; |
|
| 306 | + } |
|
| 307 | + |
|
| 308 | + /** |
|
| 309 | + * @param IShare $share The share to send the email for |
|
| 310 | + * @param array $emails The email addresses to send the email to |
|
| 311 | + */ |
|
| 312 | + protected function sendEmail(IShare $share, array $emails): void { |
|
| 313 | + $link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', [ |
|
| 314 | + 'token' => $share->getToken() |
|
| 315 | + ]); |
|
| 316 | + |
|
| 317 | + $expiration = $share->getExpirationDate(); |
|
| 318 | + $filename = $share->getNode()->getName(); |
|
| 319 | + $initiator = $share->getSharedBy(); |
|
| 320 | + $note = $share->getNote(); |
|
| 321 | + $shareWith = $share->getSharedWith(); |
|
| 322 | + |
|
| 323 | + $initiatorUser = $this->userManager->get($initiator); |
|
| 324 | + $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
| 325 | + $message = $this->mailer->createMessage(); |
|
| 326 | + |
|
| 327 | + $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientNotification', [ |
|
| 328 | + 'filename' => $filename, |
|
| 329 | + 'link' => $link, |
|
| 330 | + 'initiator' => $initiatorDisplayName, |
|
| 331 | + 'expiration' => $expiration, |
|
| 332 | + 'shareWith' => $shareWith, |
|
| 333 | + 'note' => $note |
|
| 334 | + ]); |
|
| 335 | + |
|
| 336 | + $emailTemplate->setSubject($this->l->t('%1$s shared %2$s with you', [$initiatorDisplayName, $filename])); |
|
| 337 | + $emailTemplate->addHeader(); |
|
| 338 | + $emailTemplate->addHeading($this->l->t('%1$s shared %2$s with you', [$initiatorDisplayName, $filename]), false); |
|
| 339 | + |
|
| 340 | + if ($note !== '') { |
|
| 341 | + $emailTemplate->addBodyListItem( |
|
| 342 | + htmlspecialchars($note), |
|
| 343 | + $this->l->t('Note:'), |
|
| 344 | + $this->getAbsoluteImagePath('caldav/description.png'), |
|
| 345 | + $note |
|
| 346 | + ); |
|
| 347 | + } |
|
| 348 | + |
|
| 349 | + if ($expiration !== null) { |
|
| 350 | + $dateString = (string)$this->l->l('date', $expiration, ['width' => 'medium']); |
|
| 351 | + $emailTemplate->addBodyListItem( |
|
| 352 | + $this->l->t('This share is valid until %s at midnight', [$dateString]), |
|
| 353 | + $this->l->t('Expiration:'), |
|
| 354 | + $this->getAbsoluteImagePath('caldav/time.png'), |
|
| 355 | + ); |
|
| 356 | + } |
|
| 357 | + |
|
| 358 | + $emailTemplate->addBodyButton( |
|
| 359 | + $this->l->t('Open %s', [$filename]), |
|
| 360 | + $link |
|
| 361 | + ); |
|
| 362 | + |
|
| 363 | + // If multiple recipients are given, we send the mail to all of them |
|
| 364 | + if (count($emails) > 1) { |
|
| 365 | + // We do not want to expose the email addresses of the other recipients |
|
| 366 | + $message->setBcc($emails); |
|
| 367 | + } else { |
|
| 368 | + $message->setTo($emails); |
|
| 369 | + } |
|
| 370 | + |
|
| 371 | + // The "From" contains the sharers name |
|
| 372 | + $instanceName = $this->defaults->getName(); |
|
| 373 | + $senderName = $instanceName; |
|
| 374 | + if ($this->settingsManager->replyToInitiator()) { |
|
| 375 | + $senderName = $this->l->t( |
|
| 376 | + '%1$s via %2$s', |
|
| 377 | + [ |
|
| 378 | + $initiatorDisplayName, |
|
| 379 | + $instanceName |
|
| 380 | + ] |
|
| 381 | + ); |
|
| 382 | + } |
|
| 383 | + $message->setFrom([Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
| 384 | + |
|
| 385 | + // The "Reply-To" is set to the sharer if an mail address is configured |
|
| 386 | + // also the default footer contains a "Do not reply" which needs to be adjusted. |
|
| 387 | + if ($initiatorUser && $this->settingsManager->replyToInitiator()) { |
|
| 388 | + $initiatorEmail = $initiatorUser->getEMailAddress(); |
|
| 389 | + if ($initiatorEmail !== null) { |
|
| 390 | + $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]); |
|
| 391 | + $emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')); |
|
| 392 | + } else { |
|
| 393 | + $emailTemplate->addFooter(); |
|
| 394 | + } |
|
| 395 | + } else { |
|
| 396 | + $emailTemplate->addFooter(); |
|
| 397 | + } |
|
| 398 | + |
|
| 399 | + $message->useTemplate($emailTemplate); |
|
| 400 | + $failedRecipients = $this->mailer->send($message); |
|
| 401 | + if (!empty($failedRecipients)) { |
|
| 402 | + $this->logger->error('Share notification mail could not be sent to: ' . implode(', ', $failedRecipients)); |
|
| 403 | + return; |
|
| 404 | + } |
|
| 405 | + } |
|
| 406 | + |
|
| 407 | + /** |
|
| 408 | + * Send password to recipient of a mail share |
|
| 409 | + * Will return false if |
|
| 410 | + * 1. the password is empty |
|
| 411 | + * 2. the setting to send the password by mail is disabled |
|
| 412 | + * 3. the share is set to send the password by talk |
|
| 413 | + * |
|
| 414 | + * @param IShare $share |
|
| 415 | + * @param string $password |
|
| 416 | + * @param array $emails |
|
| 417 | + * @return bool |
|
| 418 | + */ |
|
| 419 | + protected function sendPassword(IShare $share, string $password, array $emails): bool { |
|
| 420 | + $filename = $share->getNode()->getName(); |
|
| 421 | + $initiator = $share->getSharedBy(); |
|
| 422 | + $shareWith = $share->getSharedWith(); |
|
| 423 | + |
|
| 424 | + if ($password === '' || $this->settingsManager->sendPasswordByMail() === false || $share->getSendPasswordByTalk()) { |
|
| 425 | + return false; |
|
| 426 | + } |
|
| 427 | + |
|
| 428 | + $initiatorUser = $this->userManager->get($initiator); |
|
| 429 | + $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
| 430 | + $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
| 431 | + |
|
| 432 | + $plainBodyPart = $this->l->t('%1$s shared %2$s with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]); |
|
| 433 | + $htmlBodyPart = $this->l->t('%1$s shared %2$s with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]); |
|
| 434 | + |
|
| 435 | + $message = $this->mailer->createMessage(); |
|
| 436 | + |
|
| 437 | + $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientPasswordNotification', [ |
|
| 438 | + 'filename' => $filename, |
|
| 439 | + 'password' => $password, |
|
| 440 | + 'initiator' => $initiatorDisplayName, |
|
| 441 | + 'initiatorEmail' => $initiatorEmailAddress, |
|
| 442 | + 'shareWith' => $shareWith, |
|
| 443 | + ]); |
|
| 444 | + |
|
| 445 | + $emailTemplate->setSubject($this->l->t('Password to access %1$s shared to you by %2$s', [$filename, $initiatorDisplayName])); |
|
| 446 | + $emailTemplate->addHeader(); |
|
| 447 | + $emailTemplate->addHeading($this->l->t('Password to access %s', [$filename]), false); |
|
| 448 | + $emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart); |
|
| 449 | + $emailTemplate->addBodyText($this->l->t('It is protected with the following password:')); |
|
| 450 | + $emailTemplate->addBodyText($password); |
|
| 451 | + |
|
| 452 | + if ($this->config->getSystemValue('sharing.enable_mail_link_password_expiration', false) === true) { |
|
| 453 | + $expirationTime = new \DateTime(); |
|
| 454 | + $expirationInterval = $this->config->getSystemValue('sharing.mail_link_password_expiration_interval', 3600); |
|
| 455 | + $expirationTime = $expirationTime->add(new \DateInterval('PT' . $expirationInterval . 'S')); |
|
| 456 | + $emailTemplate->addBodyText($this->l->t('This password will expire at %s', [$expirationTime->format('r')])); |
|
| 457 | + } |
|
| 458 | + |
|
| 459 | + // If multiple recipients are given, we send the mail to all of them |
|
| 460 | + if (count($emails) > 1) { |
|
| 461 | + // We do not want to expose the email addresses of the other recipients |
|
| 462 | + $message->setBcc($emails); |
|
| 463 | + } else { |
|
| 464 | + $message->setTo($emails); |
|
| 465 | + } |
|
| 466 | + |
|
| 467 | + // The "From" contains the sharers name |
|
| 468 | + $instanceName = $this->defaults->getName(); |
|
| 469 | + $senderName = $instanceName; |
|
| 470 | + if ($this->settingsManager->replyToInitiator()) { |
|
| 471 | + $senderName = $this->l->t( |
|
| 472 | + '%1$s via %2$s', |
|
| 473 | + [ |
|
| 474 | + $initiatorDisplayName, |
|
| 475 | + $instanceName |
|
| 476 | + ] |
|
| 477 | + ); |
|
| 478 | + } |
|
| 479 | + $message->setFrom([Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
| 480 | + |
|
| 481 | + // The "Reply-To" is set to the sharer if an mail address is configured |
|
| 482 | + // also the default footer contains a "Do not reply" which needs to be adjusted. |
|
| 483 | + if ($initiatorUser && $this->settingsManager->replyToInitiator()) { |
|
| 484 | + $initiatorEmail = $initiatorUser->getEMailAddress(); |
|
| 485 | + if ($initiatorEmail !== null) { |
|
| 486 | + $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]); |
|
| 487 | + $emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')); |
|
| 488 | + } else { |
|
| 489 | + $emailTemplate->addFooter(); |
|
| 490 | + } |
|
| 491 | + } else { |
|
| 492 | + $emailTemplate->addFooter(); |
|
| 493 | + } |
|
| 494 | + |
|
| 495 | + $message->useTemplate($emailTemplate); |
|
| 496 | + $failedRecipients = $this->mailer->send($message); |
|
| 497 | + if (!empty($failedRecipients)) { |
|
| 498 | + $this->logger->error('Share password mail could not be sent to: ' . implode(', ', $failedRecipients)); |
|
| 499 | + return false; |
|
| 500 | + } |
|
| 501 | + |
|
| 502 | + $this->createPasswordSendActivity($share, $shareWith, false); |
|
| 503 | + return true; |
|
| 504 | + } |
|
| 505 | + |
|
| 506 | + protected function sendNote(IShare $share): void { |
|
| 507 | + $recipient = $share->getSharedWith(); |
|
| 508 | + |
|
| 509 | + |
|
| 510 | + $filename = $share->getNode()->getName(); |
|
| 511 | + $initiator = $share->getSharedBy(); |
|
| 512 | + $note = $share->getNote(); |
|
| 513 | + |
|
| 514 | + $initiatorUser = $this->userManager->get($initiator); |
|
| 515 | + $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
| 516 | + $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
| 517 | + |
|
| 518 | + $plainHeading = $this->l->t('%1$s shared %2$s with you and wants to add:', [$initiatorDisplayName, $filename]); |
|
| 519 | + $htmlHeading = $this->l->t('%1$s shared %2$s with you and wants to add', [$initiatorDisplayName, $filename]); |
|
| 520 | + |
|
| 521 | + $message = $this->mailer->createMessage(); |
|
| 522 | + |
|
| 523 | + $emailTemplate = $this->mailer->createEMailTemplate('shareByMail.sendNote'); |
|
| 524 | + |
|
| 525 | + $emailTemplate->setSubject($this->l->t('%s added a note to a file shared with you', [$initiatorDisplayName])); |
|
| 526 | + $emailTemplate->addHeader(); |
|
| 527 | + $emailTemplate->addHeading(htmlspecialchars($htmlHeading), $plainHeading); |
|
| 528 | + $emailTemplate->addBodyText(htmlspecialchars($note), $note); |
|
| 529 | + |
|
| 530 | + $link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', |
|
| 531 | + ['token' => $share->getToken()]); |
|
| 532 | + $emailTemplate->addBodyButton( |
|
| 533 | + $this->l->t('Open %s', [$filename]), |
|
| 534 | + $link |
|
| 535 | + ); |
|
| 536 | + |
|
| 537 | + // The "From" contains the sharers name |
|
| 538 | + $instanceName = $this->defaults->getName(); |
|
| 539 | + $senderName = $instanceName; |
|
| 540 | + if ($this->settingsManager->replyToInitiator()) { |
|
| 541 | + $senderName = $this->l->t( |
|
| 542 | + '%1$s via %2$s', |
|
| 543 | + [ |
|
| 544 | + $initiatorDisplayName, |
|
| 545 | + $instanceName |
|
| 546 | + ] |
|
| 547 | + ); |
|
| 548 | + } |
|
| 549 | + $message->setFrom([Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
| 550 | + if ($this->settingsManager->replyToInitiator() && $initiatorEmailAddress !== null) { |
|
| 551 | + $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]); |
|
| 552 | + $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan()); |
|
| 553 | + } else { |
|
| 554 | + $emailTemplate->addFooter(); |
|
| 555 | + } |
|
| 556 | + |
|
| 557 | + $message->setTo([$recipient]); |
|
| 558 | + $message->useTemplate($emailTemplate); |
|
| 559 | + $this->mailer->send($message); |
|
| 560 | + } |
|
| 561 | + |
|
| 562 | + /** |
|
| 563 | + * send auto generated password to the owner. This happens if the admin enforces |
|
| 564 | + * a password for mail shares and forbid to send the password by mail to the recipient |
|
| 565 | + * |
|
| 566 | + * @throws \Exception |
|
| 567 | + */ |
|
| 568 | + protected function sendPasswordToOwner(IShare $share, string $password): bool { |
|
| 569 | + $filename = $share->getNode()->getName(); |
|
| 570 | + $initiator = $this->userManager->get($share->getSharedBy()); |
|
| 571 | + $initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null; |
|
| 572 | + $initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy(); |
|
| 573 | + $shareWith = implode(', ', $this->getSharedWithEmails($share)); |
|
| 574 | + |
|
| 575 | + if ($initiatorEMailAddress === null) { |
|
| 576 | + throw new \Exception( |
|
| 577 | + $this->l->t('We cannot send you the auto-generated password. Please set a valid email address in your personal settings and try again.') |
|
| 578 | + ); |
|
| 579 | + } |
|
| 580 | + |
|
| 581 | + $bodyPart = $this->l->t('You just shared %1$s with %2$s. The share was already sent to the recipient. Due to the security policies defined by the administrator of %3$s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient.', [$filename, $shareWith, $this->defaults->getName()]); |
|
| 582 | + |
|
| 583 | + $message = $this->mailer->createMessage(); |
|
| 584 | + $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.OwnerPasswordNotification', [ |
|
| 585 | + 'filename' => $filename, |
|
| 586 | + 'password' => $password, |
|
| 587 | + 'initiator' => $initiatorDisplayName, |
|
| 588 | + 'initiatorEmail' => $initiatorEMailAddress, |
|
| 589 | + 'shareWith' => $shareWith, |
|
| 590 | + ]); |
|
| 591 | + |
|
| 592 | + $emailTemplate->setSubject($this->l->t('Password to access %1$s shared by you with %2$s', [$filename, $shareWith])); |
|
| 593 | + $emailTemplate->addHeader(); |
|
| 594 | + $emailTemplate->addHeading($this->l->t('Password to access %s', [$filename]), false); |
|
| 595 | + $emailTemplate->addBodyText($bodyPart); |
|
| 596 | + $emailTemplate->addBodyText($this->l->t('This is the password:')); |
|
| 597 | + $emailTemplate->addBodyText($password); |
|
| 598 | + |
|
| 599 | + if ($this->config->getSystemValue('sharing.enable_mail_link_password_expiration', false) === true) { |
|
| 600 | + $expirationTime = new \DateTime(); |
|
| 601 | + $expirationInterval = $this->config->getSystemValue('sharing.mail_link_password_expiration_interval', 3600); |
|
| 602 | + $expirationTime = $expirationTime->add(new \DateInterval('PT' . $expirationInterval . 'S')); |
|
| 603 | + $emailTemplate->addBodyText($this->l->t('This password will expire at %s', [$expirationTime->format('r')])); |
|
| 604 | + } |
|
| 605 | + |
|
| 606 | + $emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.')); |
|
| 607 | + |
|
| 608 | + $emailTemplate->addFooter(); |
|
| 609 | + |
|
| 610 | + $instanceName = $this->defaults->getName(); |
|
| 611 | + $senderName = $this->l->t( |
|
| 612 | + '%1$s via %2$s', |
|
| 613 | + [ |
|
| 614 | + $initiatorDisplayName, |
|
| 615 | + $instanceName |
|
| 616 | + ] |
|
| 617 | + ); |
|
| 618 | + $message->setFrom([Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
| 619 | + $message->setTo([$initiatorEMailAddress => $initiatorDisplayName]); |
|
| 620 | + $message->useTemplate($emailTemplate); |
|
| 621 | + $this->mailer->send($message); |
|
| 622 | + |
|
| 623 | + $this->createPasswordSendActivity($share, $shareWith, true); |
|
| 624 | + |
|
| 625 | + return true; |
|
| 626 | + } |
|
| 627 | + |
|
| 628 | + private function getAbsoluteImagePath(string $path):string { |
|
| 629 | + return $this->urlGenerator->getAbsoluteURL( |
|
| 630 | + $this->urlGenerator->imagePath('core', $path) |
|
| 631 | + ); |
|
| 632 | + } |
|
| 633 | + |
|
| 634 | + /** |
|
| 635 | + * generate share token |
|
| 636 | + */ |
|
| 637 | + protected function generateToken(int $size = 15): string { |
|
| 638 | + $token = $this->secureRandom->generate($size, ISecureRandom::CHAR_HUMAN_READABLE); |
|
| 639 | + return $token; |
|
| 640 | + } |
|
| 641 | + |
|
| 642 | + public function getChildren(IShare $parent): array { |
|
| 643 | + $children = []; |
|
| 644 | + |
|
| 645 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 646 | + $qb->select('*') |
|
| 647 | + ->from('share') |
|
| 648 | + ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) |
|
| 649 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL))) |
|
| 650 | + ->orderBy('id'); |
|
| 651 | + |
|
| 652 | + $cursor = $qb->executeQuery(); |
|
| 653 | + while ($data = $cursor->fetch()) { |
|
| 654 | + $children[] = $this->createShareObject($data); |
|
| 655 | + } |
|
| 656 | + $cursor->closeCursor(); |
|
| 657 | + |
|
| 658 | + return $children; |
|
| 659 | + } |
|
| 660 | + |
|
| 661 | + /** |
|
| 662 | + * Add share to the database and return the ID |
|
| 663 | + */ |
|
| 664 | + protected function addShareToDB( |
|
| 665 | + ?int $itemSource, |
|
| 666 | + ?string $itemType, |
|
| 667 | + ?string $shareWith, |
|
| 668 | + ?string $sharedBy, |
|
| 669 | + ?string $uidOwner, |
|
| 670 | + ?int $permissions, |
|
| 671 | + ?string $token, |
|
| 672 | + ?string $password, |
|
| 673 | + ?\DateTimeInterface $passwordExpirationTime, |
|
| 674 | + ?bool $sendPasswordByTalk, |
|
| 675 | + ?bool $hideDownload, |
|
| 676 | + ?string $label, |
|
| 677 | + ?\DateTimeInterface $expirationTime, |
|
| 678 | + ?string $note = '', |
|
| 679 | + ?IAttributes $attributes = null, |
|
| 680 | + ?bool $mailSend = true, |
|
| 681 | + ): int { |
|
| 682 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 683 | + $qb->insert('share') |
|
| 684 | + ->setValue('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL)) |
|
| 685 | + ->setValue('item_type', $qb->createNamedParameter($itemType)) |
|
| 686 | + ->setValue('item_source', $qb->createNamedParameter($itemSource)) |
|
| 687 | + ->setValue('file_source', $qb->createNamedParameter($itemSource)) |
|
| 688 | + ->setValue('share_with', $qb->createNamedParameter($shareWith)) |
|
| 689 | + ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) |
|
| 690 | + ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) |
|
| 691 | + ->setValue('permissions', $qb->createNamedParameter($permissions)) |
|
| 692 | + ->setValue('token', $qb->createNamedParameter($token)) |
|
| 693 | + ->setValue('password', $qb->createNamedParameter($password)) |
|
| 694 | + ->setValue('password_expiration_time', $qb->createNamedParameter($passwordExpirationTime, IQueryBuilder::PARAM_DATETIME_MUTABLE)) |
|
| 695 | + ->setValue('password_by_talk', $qb->createNamedParameter($sendPasswordByTalk, IQueryBuilder::PARAM_BOOL)) |
|
| 696 | + ->setValue('stime', $qb->createNamedParameter(time())) |
|
| 697 | + ->setValue('hide_download', $qb->createNamedParameter((int)$hideDownload, IQueryBuilder::PARAM_INT)) |
|
| 698 | + ->setValue('label', $qb->createNamedParameter($label)) |
|
| 699 | + ->setValue('note', $qb->createNamedParameter($note)) |
|
| 700 | + ->setValue('mail_send', $qb->createNamedParameter((int)$mailSend, IQueryBuilder::PARAM_INT)); |
|
| 701 | + |
|
| 702 | + // set share attributes |
|
| 703 | + $shareAttributes = $this->formatShareAttributes($attributes); |
|
| 704 | + |
|
| 705 | + $qb->setValue('attributes', $qb->createNamedParameter($shareAttributes)); |
|
| 706 | + if ($expirationTime !== null) { |
|
| 707 | + $qb->setValue('expiration', $qb->createNamedParameter($expirationTime, IQueryBuilder::PARAM_DATETIME_MUTABLE)); |
|
| 708 | + } |
|
| 709 | + |
|
| 710 | + $qb->executeStatement(); |
|
| 711 | + return $qb->getLastInsertId(); |
|
| 712 | + } |
|
| 713 | + |
|
| 714 | + /** |
|
| 715 | + * Update a share |
|
| 716 | + */ |
|
| 717 | + public function update(IShare $share, ?string $plainTextPassword = null): IShare { |
|
| 718 | + $originalShare = $this->getShareById($share->getId()); |
|
| 719 | + |
|
| 720 | + // a real password was given |
|
| 721 | + $validPassword = $plainTextPassword !== null && $plainTextPassword !== ''; |
|
| 722 | + |
|
| 723 | + if ($validPassword && ($originalShare->getPassword() !== $share->getPassword() |
|
| 724 | + || ($originalShare->getSendPasswordByTalk() && !$share->getSendPasswordByTalk()))) { |
|
| 725 | + $emails = $this->getSharedWithEmails($share); |
|
| 726 | + $validEmails = array_filter($emails, function ($email) { |
|
| 727 | + return $this->emailValidator->isValid($email); |
|
| 728 | + }); |
|
| 729 | + $this->sendPassword($share, $plainTextPassword, $validEmails); |
|
| 730 | + } |
|
| 731 | + |
|
| 732 | + $shareAttributes = $this->formatShareAttributes($share->getAttributes()); |
|
| 733 | + |
|
| 734 | + /* |
|
| 735 | 735 | * We allow updating mail shares |
| 736 | 736 | */ |
| 737 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 738 | - $qb->update('share') |
|
| 739 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
| 740 | - ->set('item_source', $qb->createNamedParameter($share->getNodeId())) |
|
| 741 | - ->set('file_source', $qb->createNamedParameter($share->getNodeId())) |
|
| 742 | - ->set('share_with', $qb->createNamedParameter($share->getSharedWith())) |
|
| 743 | - ->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
|
| 744 | - ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
|
| 745 | - ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
|
| 746 | - ->set('password', $qb->createNamedParameter($share->getPassword())) |
|
| 747 | - ->set('password_expiration_time', $qb->createNamedParameter($share->getPasswordExpirationTime(), IQueryBuilder::PARAM_DATETIME_MUTABLE)) |
|
| 748 | - ->set('label', $qb->createNamedParameter($share->getLabel())) |
|
| 749 | - ->set('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL)) |
|
| 750 | - ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATETIME_MUTABLE)) |
|
| 751 | - ->set('note', $qb->createNamedParameter($share->getNote())) |
|
| 752 | - ->set('hide_download', $qb->createNamedParameter((int)$share->getHideDownload(), IQueryBuilder::PARAM_INT)) |
|
| 753 | - ->set('attributes', $qb->createNamedParameter($shareAttributes)) |
|
| 754 | - ->set('mail_send', $qb->createNamedParameter((int)$share->getMailSend(), IQueryBuilder::PARAM_INT)) |
|
| 755 | - ->set('reminder_sent', $qb->createNamedParameter($share->getReminderSent(), IQueryBuilder::PARAM_BOOL)) |
|
| 756 | - ->executeStatement(); |
|
| 757 | - |
|
| 758 | - if ($originalShare->getNote() !== $share->getNote() && $share->getNote() !== '') { |
|
| 759 | - $this->sendNote($share); |
|
| 760 | - } |
|
| 761 | - |
|
| 762 | - return $share; |
|
| 763 | - } |
|
| 764 | - |
|
| 765 | - /** |
|
| 766 | - * @inheritdoc |
|
| 767 | - */ |
|
| 768 | - public function move(IShare $share, $recipient): IShare { |
|
| 769 | - /** |
|
| 770 | - * nothing to do here, mail shares are only outgoing shares |
|
| 771 | - */ |
|
| 772 | - return $share; |
|
| 773 | - } |
|
| 774 | - |
|
| 775 | - /** |
|
| 776 | - * Delete a share (owner unShares the file) |
|
| 777 | - * |
|
| 778 | - * @param IShare $share |
|
| 779 | - */ |
|
| 780 | - public function delete(IShare $share): void { |
|
| 781 | - try { |
|
| 782 | - $this->createShareActivity($share, 'unshare'); |
|
| 783 | - } catch (\Exception $e) { |
|
| 784 | - } |
|
| 785 | - |
|
| 786 | - $this->removeShareFromTable((int)$share->getId()); |
|
| 787 | - } |
|
| 788 | - |
|
| 789 | - /** |
|
| 790 | - * @inheritdoc |
|
| 791 | - */ |
|
| 792 | - public function deleteFromSelf(IShare $share, $recipient): void { |
|
| 793 | - // nothing to do here, mail shares are only outgoing shares |
|
| 794 | - } |
|
| 795 | - |
|
| 796 | - public function restore(IShare $share, string $recipient): IShare { |
|
| 797 | - throw new GenericShareException('not implemented'); |
|
| 798 | - } |
|
| 799 | - |
|
| 800 | - /** |
|
| 801 | - * @inheritdoc |
|
| 802 | - */ |
|
| 803 | - public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset): array { |
|
| 804 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 805 | - $qb->select('*') |
|
| 806 | - ->from('share'); |
|
| 807 | - |
|
| 808 | - $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL))); |
|
| 809 | - |
|
| 810 | - /** |
|
| 811 | - * Reshares for this user are shares where they are the owner. |
|
| 812 | - */ |
|
| 813 | - if ($reshares === false) { |
|
| 814 | - //Special case for old shares created via the web UI |
|
| 815 | - $or1 = $qb->expr()->andX( |
|
| 816 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 817 | - $qb->expr()->isNull('uid_initiator') |
|
| 818 | - ); |
|
| 819 | - |
|
| 820 | - $qb->andWhere( |
|
| 821 | - $qb->expr()->orX( |
|
| 822 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), |
|
| 823 | - $or1 |
|
| 824 | - ) |
|
| 825 | - ); |
|
| 826 | - } elseif ($node === null) { |
|
| 827 | - $qb->andWhere( |
|
| 828 | - $qb->expr()->orX( |
|
| 829 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 830 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
| 831 | - ) |
|
| 832 | - ); |
|
| 833 | - } |
|
| 834 | - |
|
| 835 | - if ($node !== null) { |
|
| 836 | - $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
| 837 | - } |
|
| 838 | - |
|
| 839 | - if ($limit !== -1) { |
|
| 840 | - $qb->setMaxResults($limit); |
|
| 841 | - } |
|
| 842 | - |
|
| 843 | - $qb->setFirstResult($offset); |
|
| 844 | - $qb->orderBy('id'); |
|
| 845 | - |
|
| 846 | - $cursor = $qb->executeQuery(); |
|
| 847 | - $shares = []; |
|
| 848 | - while ($data = $cursor->fetch()) { |
|
| 849 | - $shares[] = $this->createShareObject($data); |
|
| 850 | - } |
|
| 851 | - $cursor->closeCursor(); |
|
| 852 | - |
|
| 853 | - return $shares; |
|
| 854 | - } |
|
| 855 | - |
|
| 856 | - /** |
|
| 857 | - * @inheritdoc |
|
| 858 | - */ |
|
| 859 | - public function getShareById($id, $recipientId = null): IShare { |
|
| 860 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 861 | - |
|
| 862 | - $qb->select('*') |
|
| 863 | - ->from('share') |
|
| 864 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) |
|
| 865 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL))); |
|
| 866 | - |
|
| 867 | - $cursor = $qb->executeQuery(); |
|
| 868 | - $data = $cursor->fetch(); |
|
| 869 | - $cursor->closeCursor(); |
|
| 870 | - |
|
| 871 | - if ($data === false) { |
|
| 872 | - throw new ShareNotFound(); |
|
| 873 | - } |
|
| 874 | - |
|
| 875 | - try { |
|
| 876 | - $share = $this->createShareObject($data); |
|
| 877 | - } catch (InvalidShare $e) { |
|
| 878 | - throw new ShareNotFound(); |
|
| 879 | - } |
|
| 880 | - |
|
| 881 | - return $share; |
|
| 882 | - } |
|
| 883 | - |
|
| 884 | - /** |
|
| 885 | - * Get shares for a given path |
|
| 886 | - * |
|
| 887 | - * @return IShare[] |
|
| 888 | - */ |
|
| 889 | - public function getSharesByPath(Node $path): array { |
|
| 890 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 891 | - |
|
| 892 | - $cursor = $qb->select('*') |
|
| 893 | - ->from('share') |
|
| 894 | - ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) |
|
| 895 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL))) |
|
| 896 | - ->executeQuery(); |
|
| 897 | - |
|
| 898 | - $shares = []; |
|
| 899 | - while ($data = $cursor->fetch()) { |
|
| 900 | - $shares[] = $this->createShareObject($data); |
|
| 901 | - } |
|
| 902 | - $cursor->closeCursor(); |
|
| 903 | - |
|
| 904 | - return $shares; |
|
| 905 | - } |
|
| 906 | - |
|
| 907 | - /** |
|
| 908 | - * @inheritdoc |
|
| 909 | - */ |
|
| 910 | - public function getSharedWith($userId, $shareType, $node, $limit, $offset): array { |
|
| 911 | - /** @var IShare[] $shares */ |
|
| 912 | - $shares = []; |
|
| 913 | - |
|
| 914 | - //Get shares directly with this user |
|
| 915 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 916 | - $qb->select('*') |
|
| 917 | - ->from('share'); |
|
| 918 | - |
|
| 919 | - // Order by id |
|
| 920 | - $qb->orderBy('id'); |
|
| 921 | - |
|
| 922 | - // Set limit and offset |
|
| 923 | - if ($limit !== -1) { |
|
| 924 | - $qb->setMaxResults($limit); |
|
| 925 | - } |
|
| 926 | - $qb->setFirstResult($offset); |
|
| 927 | - |
|
| 928 | - $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL))); |
|
| 929 | - $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); |
|
| 930 | - |
|
| 931 | - // Filter by node if provided |
|
| 932 | - if ($node !== null) { |
|
| 933 | - $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
| 934 | - } |
|
| 935 | - |
|
| 936 | - $cursor = $qb->executeQuery(); |
|
| 937 | - |
|
| 938 | - while ($data = $cursor->fetch()) { |
|
| 939 | - $shares[] = $this->createShareObject($data); |
|
| 940 | - } |
|
| 941 | - $cursor->closeCursor(); |
|
| 942 | - |
|
| 943 | - |
|
| 944 | - return $shares; |
|
| 945 | - } |
|
| 946 | - |
|
| 947 | - /** |
|
| 948 | - * Get a share by token |
|
| 949 | - * |
|
| 950 | - * @throws ShareNotFound |
|
| 951 | - */ |
|
| 952 | - public function getShareByToken($token): IShare { |
|
| 953 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 954 | - |
|
| 955 | - $cursor = $qb->select('*') |
|
| 956 | - ->from('share') |
|
| 957 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL))) |
|
| 958 | - ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) |
|
| 959 | - ->executeQuery(); |
|
| 960 | - |
|
| 961 | - $data = $cursor->fetch(); |
|
| 962 | - |
|
| 963 | - if ($data === false) { |
|
| 964 | - throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
| 965 | - } |
|
| 966 | - |
|
| 967 | - try { |
|
| 968 | - $share = $this->createShareObject($data); |
|
| 969 | - } catch (InvalidShare $e) { |
|
| 970 | - throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
| 971 | - } |
|
| 972 | - |
|
| 973 | - return $share; |
|
| 974 | - } |
|
| 975 | - |
|
| 976 | - /** |
|
| 977 | - * remove share from table |
|
| 978 | - */ |
|
| 979 | - protected function removeShareFromTable(int $shareId): void { |
|
| 980 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 981 | - $qb->delete('share') |
|
| 982 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))); |
|
| 983 | - $qb->executeStatement(); |
|
| 984 | - } |
|
| 985 | - |
|
| 986 | - /** |
|
| 987 | - * Create a share object from a database row |
|
| 988 | - * |
|
| 989 | - * @throws InvalidShare |
|
| 990 | - * @throws ShareNotFound |
|
| 991 | - */ |
|
| 992 | - protected function createShareObject(array $data): IShare { |
|
| 993 | - $share = new Share($this->rootFolder, $this->userManager); |
|
| 994 | - $share->setId((int)$data['id']) |
|
| 995 | - ->setShareType((int)$data['share_type']) |
|
| 996 | - ->setPermissions((int)$data['permissions']) |
|
| 997 | - ->setTarget($data['file_target']) |
|
| 998 | - ->setMailSend((bool)$data['mail_send']) |
|
| 999 | - ->setNote($data['note']) |
|
| 1000 | - ->setToken($data['token']); |
|
| 1001 | - |
|
| 1002 | - $shareTime = new \DateTime(); |
|
| 1003 | - $shareTime->setTimestamp((int)$data['stime']); |
|
| 1004 | - $share->setShareTime($shareTime); |
|
| 1005 | - $share->setSharedWith($data['share_with'] ?? ''); |
|
| 1006 | - $share->setPassword($data['password']); |
|
| 1007 | - $passwordExpirationTime = \DateTime::createFromFormat('Y-m-d H:i:s', $data['password_expiration_time'] ?? ''); |
|
| 1008 | - $share->setPasswordExpirationTime($passwordExpirationTime !== false ? $passwordExpirationTime : null); |
|
| 1009 | - $share->setLabel($data['label'] ?? ''); |
|
| 1010 | - $share->setSendPasswordByTalk((bool)$data['password_by_talk']); |
|
| 1011 | - $share->setHideDownload((bool)$data['hide_download']); |
|
| 1012 | - $share->setReminderSent((bool)$data['reminder_sent']); |
|
| 1013 | - |
|
| 1014 | - if ($data['uid_initiator'] !== null) { |
|
| 1015 | - $share->setShareOwner($data['uid_owner']); |
|
| 1016 | - $share->setSharedBy($data['uid_initiator']); |
|
| 1017 | - } else { |
|
| 1018 | - //OLD SHARE |
|
| 1019 | - $share->setSharedBy($data['uid_owner']); |
|
| 1020 | - $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); |
|
| 1021 | - |
|
| 1022 | - $owner = $path->getOwner(); |
|
| 1023 | - $share->setShareOwner($owner->getUID()); |
|
| 1024 | - } |
|
| 1025 | - |
|
| 1026 | - if ($data['expiration'] !== null) { |
|
| 1027 | - $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']); |
|
| 1028 | - if ($expiration !== false) { |
|
| 1029 | - $share->setExpirationDate($expiration); |
|
| 1030 | - } |
|
| 1031 | - } |
|
| 1032 | - |
|
| 1033 | - $share = $this->updateShareAttributes($share, $data['attributes']); |
|
| 1034 | - |
|
| 1035 | - $share->setNodeId((int)$data['file_source']); |
|
| 1036 | - $share->setNodeType($data['item_type']); |
|
| 1037 | - |
|
| 1038 | - $share->setProviderId($this->identifier()); |
|
| 1039 | - |
|
| 1040 | - return $share; |
|
| 1041 | - } |
|
| 1042 | - |
|
| 1043 | - /** |
|
| 1044 | - * Get the node with file $id for $user |
|
| 1045 | - * |
|
| 1046 | - * @throws InvalidShare |
|
| 1047 | - */ |
|
| 1048 | - private function getNode(string $userId, int $id): Node { |
|
| 1049 | - try { |
|
| 1050 | - $userFolder = $this->rootFolder->getUserFolder($userId); |
|
| 1051 | - } catch (NoUserException $e) { |
|
| 1052 | - throw new InvalidShare(); |
|
| 1053 | - } |
|
| 1054 | - |
|
| 1055 | - $nodes = $userFolder->getById($id); |
|
| 1056 | - |
|
| 1057 | - if (empty($nodes)) { |
|
| 1058 | - throw new InvalidShare(); |
|
| 1059 | - } |
|
| 1060 | - |
|
| 1061 | - return $nodes[0]; |
|
| 1062 | - } |
|
| 1063 | - |
|
| 1064 | - /** |
|
| 1065 | - * A user is deleted from the system |
|
| 1066 | - * So clean up the relevant shares. |
|
| 1067 | - */ |
|
| 1068 | - public function userDeleted($uid, $shareType): void { |
|
| 1069 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1070 | - |
|
| 1071 | - $qb->delete('share') |
|
| 1072 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL))) |
|
| 1073 | - ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) |
|
| 1074 | - ->executeStatement(); |
|
| 1075 | - } |
|
| 1076 | - |
|
| 1077 | - /** |
|
| 1078 | - * This provider does not support group shares |
|
| 1079 | - */ |
|
| 1080 | - public function groupDeleted($gid): void { |
|
| 1081 | - } |
|
| 1082 | - |
|
| 1083 | - /** |
|
| 1084 | - * This provider does not support group shares |
|
| 1085 | - */ |
|
| 1086 | - public function userDeletedFromGroup($uid, $gid): void { |
|
| 1087 | - } |
|
| 1088 | - |
|
| 1089 | - /** |
|
| 1090 | - * get database row of a give share |
|
| 1091 | - * |
|
| 1092 | - * @throws ShareNotFound |
|
| 1093 | - */ |
|
| 1094 | - protected function getRawShare(int $id): array { |
|
| 1095 | - // Now fetch the inserted share and create a complete share object |
|
| 1096 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1097 | - $qb->select('*') |
|
| 1098 | - ->from('share') |
|
| 1099 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
| 1100 | - |
|
| 1101 | - $cursor = $qb->executeQuery(); |
|
| 1102 | - $data = $cursor->fetch(); |
|
| 1103 | - $cursor->closeCursor(); |
|
| 1104 | - |
|
| 1105 | - if ($data === false) { |
|
| 1106 | - throw new ShareNotFound; |
|
| 1107 | - } |
|
| 1108 | - |
|
| 1109 | - return $data; |
|
| 1110 | - } |
|
| 1111 | - |
|
| 1112 | - public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true): array { |
|
| 1113 | - return $this->getSharesInFolderInternal($userId, $node, $reshares); |
|
| 1114 | - } |
|
| 1115 | - |
|
| 1116 | - public function getAllSharesInFolder(Folder $node): array { |
|
| 1117 | - return $this->getSharesInFolderInternal(null, $node, null); |
|
| 1118 | - } |
|
| 1119 | - |
|
| 1120 | - /** |
|
| 1121 | - * @return array<int, list<IShare>> |
|
| 1122 | - */ |
|
| 1123 | - private function getSharesInFolderInternal(?string $userId, Folder $node, ?bool $reshares): array { |
|
| 1124 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1125 | - $qb->select('*') |
|
| 1126 | - ->from('share', 's') |
|
| 1127 | - ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))) |
|
| 1128 | - ->andWhere( |
|
| 1129 | - $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL)) |
|
| 1130 | - ); |
|
| 1131 | - |
|
| 1132 | - if ($userId !== null) { |
|
| 1133 | - /** |
|
| 1134 | - * Reshares for this user are shares where they are the owner. |
|
| 1135 | - */ |
|
| 1136 | - if ($reshares !== true) { |
|
| 1137 | - $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); |
|
| 1138 | - } else { |
|
| 1139 | - $qb->andWhere( |
|
| 1140 | - $qb->expr()->orX( |
|
| 1141 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 1142 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
| 1143 | - ) |
|
| 1144 | - ); |
|
| 1145 | - } |
|
| 1146 | - } |
|
| 1147 | - |
|
| 1148 | - $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')); |
|
| 1149 | - |
|
| 1150 | - $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); |
|
| 1151 | - |
|
| 1152 | - $qb->orderBy('id'); |
|
| 1153 | - |
|
| 1154 | - $cursor = $qb->executeQuery(); |
|
| 1155 | - $shares = []; |
|
| 1156 | - while ($data = $cursor->fetch()) { |
|
| 1157 | - $shares[$data['fileid']][] = $this->createShareObject($data); |
|
| 1158 | - } |
|
| 1159 | - $cursor->closeCursor(); |
|
| 1160 | - |
|
| 1161 | - return $shares; |
|
| 1162 | - } |
|
| 1163 | - |
|
| 1164 | - /** |
|
| 1165 | - * @inheritdoc |
|
| 1166 | - */ |
|
| 1167 | - public function getAccessList($nodes, $currentAccess): array { |
|
| 1168 | - $ids = []; |
|
| 1169 | - foreach ($nodes as $node) { |
|
| 1170 | - $ids[] = $node->getId(); |
|
| 1171 | - } |
|
| 1172 | - |
|
| 1173 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1174 | - $qb->select('share_with', 'file_source', 'token') |
|
| 1175 | - ->from('share') |
|
| 1176 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL))) |
|
| 1177 | - ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) |
|
| 1178 | - ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))); |
|
| 1179 | - $cursor = $qb->executeQuery(); |
|
| 1180 | - |
|
| 1181 | - $public = false; |
|
| 1182 | - $mail = []; |
|
| 1183 | - while ($row = $cursor->fetch()) { |
|
| 1184 | - $public = true; |
|
| 1185 | - if ($currentAccess === false) { |
|
| 1186 | - $mail[] = $row['share_with']; |
|
| 1187 | - } else { |
|
| 1188 | - $mail[$row['share_with']] = [ |
|
| 1189 | - 'node_id' => $row['file_source'], |
|
| 1190 | - 'token' => $row['token'] |
|
| 1191 | - ]; |
|
| 1192 | - } |
|
| 1193 | - } |
|
| 1194 | - $cursor->closeCursor(); |
|
| 1195 | - |
|
| 1196 | - return ['public' => $public, 'mail' => $mail]; |
|
| 1197 | - } |
|
| 1198 | - |
|
| 1199 | - public function getAllShares(): iterable { |
|
| 1200 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1201 | - |
|
| 1202 | - $qb->select('*') |
|
| 1203 | - ->from('share') |
|
| 1204 | - ->where( |
|
| 1205 | - $qb->expr()->orX( |
|
| 1206 | - $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL)) |
|
| 1207 | - ) |
|
| 1208 | - ); |
|
| 1209 | - |
|
| 1210 | - $cursor = $qb->executeQuery(); |
|
| 1211 | - while ($data = $cursor->fetch()) { |
|
| 1212 | - try { |
|
| 1213 | - $share = $this->createShareObject($data); |
|
| 1214 | - } catch (InvalidShare $e) { |
|
| 1215 | - continue; |
|
| 1216 | - } catch (ShareNotFound $e) { |
|
| 1217 | - continue; |
|
| 1218 | - } |
|
| 1219 | - |
|
| 1220 | - yield $share; |
|
| 1221 | - } |
|
| 1222 | - $cursor->closeCursor(); |
|
| 1223 | - } |
|
| 1224 | - |
|
| 1225 | - /** |
|
| 1226 | - * Extract the emails from the share |
|
| 1227 | - * It can be a single email, from the share_with field |
|
| 1228 | - * or a list of emails from the emails attributes field. |
|
| 1229 | - * @param IShare $share |
|
| 1230 | - * @return string[] |
|
| 1231 | - */ |
|
| 1232 | - protected function getSharedWithEmails(IShare $share): array { |
|
| 1233 | - $attributes = $share->getAttributes(); |
|
| 1234 | - |
|
| 1235 | - if ($attributes === null) { |
|
| 1236 | - return [$share->getSharedWith()]; |
|
| 1237 | - } |
|
| 1238 | - |
|
| 1239 | - $emails = $attributes->getAttribute('shareWith', 'emails'); |
|
| 1240 | - if (isset($emails) && is_array($emails) && !empty($emails)) { |
|
| 1241 | - return $emails; |
|
| 1242 | - } |
|
| 1243 | - return [$share->getSharedWith()]; |
|
| 1244 | - } |
|
| 737 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 738 | + $qb->update('share') |
|
| 739 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
| 740 | + ->set('item_source', $qb->createNamedParameter($share->getNodeId())) |
|
| 741 | + ->set('file_source', $qb->createNamedParameter($share->getNodeId())) |
|
| 742 | + ->set('share_with', $qb->createNamedParameter($share->getSharedWith())) |
|
| 743 | + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
|
| 744 | + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
|
| 745 | + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
|
| 746 | + ->set('password', $qb->createNamedParameter($share->getPassword())) |
|
| 747 | + ->set('password_expiration_time', $qb->createNamedParameter($share->getPasswordExpirationTime(), IQueryBuilder::PARAM_DATETIME_MUTABLE)) |
|
| 748 | + ->set('label', $qb->createNamedParameter($share->getLabel())) |
|
| 749 | + ->set('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL)) |
|
| 750 | + ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATETIME_MUTABLE)) |
|
| 751 | + ->set('note', $qb->createNamedParameter($share->getNote())) |
|
| 752 | + ->set('hide_download', $qb->createNamedParameter((int)$share->getHideDownload(), IQueryBuilder::PARAM_INT)) |
|
| 753 | + ->set('attributes', $qb->createNamedParameter($shareAttributes)) |
|
| 754 | + ->set('mail_send', $qb->createNamedParameter((int)$share->getMailSend(), IQueryBuilder::PARAM_INT)) |
|
| 755 | + ->set('reminder_sent', $qb->createNamedParameter($share->getReminderSent(), IQueryBuilder::PARAM_BOOL)) |
|
| 756 | + ->executeStatement(); |
|
| 757 | + |
|
| 758 | + if ($originalShare->getNote() !== $share->getNote() && $share->getNote() !== '') { |
|
| 759 | + $this->sendNote($share); |
|
| 760 | + } |
|
| 761 | + |
|
| 762 | + return $share; |
|
| 763 | + } |
|
| 764 | + |
|
| 765 | + /** |
|
| 766 | + * @inheritdoc |
|
| 767 | + */ |
|
| 768 | + public function move(IShare $share, $recipient): IShare { |
|
| 769 | + /** |
|
| 770 | + * nothing to do here, mail shares are only outgoing shares |
|
| 771 | + */ |
|
| 772 | + return $share; |
|
| 773 | + } |
|
| 774 | + |
|
| 775 | + /** |
|
| 776 | + * Delete a share (owner unShares the file) |
|
| 777 | + * |
|
| 778 | + * @param IShare $share |
|
| 779 | + */ |
|
| 780 | + public function delete(IShare $share): void { |
|
| 781 | + try { |
|
| 782 | + $this->createShareActivity($share, 'unshare'); |
|
| 783 | + } catch (\Exception $e) { |
|
| 784 | + } |
|
| 785 | + |
|
| 786 | + $this->removeShareFromTable((int)$share->getId()); |
|
| 787 | + } |
|
| 788 | + |
|
| 789 | + /** |
|
| 790 | + * @inheritdoc |
|
| 791 | + */ |
|
| 792 | + public function deleteFromSelf(IShare $share, $recipient): void { |
|
| 793 | + // nothing to do here, mail shares are only outgoing shares |
|
| 794 | + } |
|
| 795 | + |
|
| 796 | + public function restore(IShare $share, string $recipient): IShare { |
|
| 797 | + throw new GenericShareException('not implemented'); |
|
| 798 | + } |
|
| 799 | + |
|
| 800 | + /** |
|
| 801 | + * @inheritdoc |
|
| 802 | + */ |
|
| 803 | + public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset): array { |
|
| 804 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 805 | + $qb->select('*') |
|
| 806 | + ->from('share'); |
|
| 807 | + |
|
| 808 | + $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL))); |
|
| 809 | + |
|
| 810 | + /** |
|
| 811 | + * Reshares for this user are shares where they are the owner. |
|
| 812 | + */ |
|
| 813 | + if ($reshares === false) { |
|
| 814 | + //Special case for old shares created via the web UI |
|
| 815 | + $or1 = $qb->expr()->andX( |
|
| 816 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 817 | + $qb->expr()->isNull('uid_initiator') |
|
| 818 | + ); |
|
| 819 | + |
|
| 820 | + $qb->andWhere( |
|
| 821 | + $qb->expr()->orX( |
|
| 822 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), |
|
| 823 | + $or1 |
|
| 824 | + ) |
|
| 825 | + ); |
|
| 826 | + } elseif ($node === null) { |
|
| 827 | + $qb->andWhere( |
|
| 828 | + $qb->expr()->orX( |
|
| 829 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 830 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
| 831 | + ) |
|
| 832 | + ); |
|
| 833 | + } |
|
| 834 | + |
|
| 835 | + if ($node !== null) { |
|
| 836 | + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
| 837 | + } |
|
| 838 | + |
|
| 839 | + if ($limit !== -1) { |
|
| 840 | + $qb->setMaxResults($limit); |
|
| 841 | + } |
|
| 842 | + |
|
| 843 | + $qb->setFirstResult($offset); |
|
| 844 | + $qb->orderBy('id'); |
|
| 845 | + |
|
| 846 | + $cursor = $qb->executeQuery(); |
|
| 847 | + $shares = []; |
|
| 848 | + while ($data = $cursor->fetch()) { |
|
| 849 | + $shares[] = $this->createShareObject($data); |
|
| 850 | + } |
|
| 851 | + $cursor->closeCursor(); |
|
| 852 | + |
|
| 853 | + return $shares; |
|
| 854 | + } |
|
| 855 | + |
|
| 856 | + /** |
|
| 857 | + * @inheritdoc |
|
| 858 | + */ |
|
| 859 | + public function getShareById($id, $recipientId = null): IShare { |
|
| 860 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 861 | + |
|
| 862 | + $qb->select('*') |
|
| 863 | + ->from('share') |
|
| 864 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) |
|
| 865 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL))); |
|
| 866 | + |
|
| 867 | + $cursor = $qb->executeQuery(); |
|
| 868 | + $data = $cursor->fetch(); |
|
| 869 | + $cursor->closeCursor(); |
|
| 870 | + |
|
| 871 | + if ($data === false) { |
|
| 872 | + throw new ShareNotFound(); |
|
| 873 | + } |
|
| 874 | + |
|
| 875 | + try { |
|
| 876 | + $share = $this->createShareObject($data); |
|
| 877 | + } catch (InvalidShare $e) { |
|
| 878 | + throw new ShareNotFound(); |
|
| 879 | + } |
|
| 880 | + |
|
| 881 | + return $share; |
|
| 882 | + } |
|
| 883 | + |
|
| 884 | + /** |
|
| 885 | + * Get shares for a given path |
|
| 886 | + * |
|
| 887 | + * @return IShare[] |
|
| 888 | + */ |
|
| 889 | + public function getSharesByPath(Node $path): array { |
|
| 890 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 891 | + |
|
| 892 | + $cursor = $qb->select('*') |
|
| 893 | + ->from('share') |
|
| 894 | + ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) |
|
| 895 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL))) |
|
| 896 | + ->executeQuery(); |
|
| 897 | + |
|
| 898 | + $shares = []; |
|
| 899 | + while ($data = $cursor->fetch()) { |
|
| 900 | + $shares[] = $this->createShareObject($data); |
|
| 901 | + } |
|
| 902 | + $cursor->closeCursor(); |
|
| 903 | + |
|
| 904 | + return $shares; |
|
| 905 | + } |
|
| 906 | + |
|
| 907 | + /** |
|
| 908 | + * @inheritdoc |
|
| 909 | + */ |
|
| 910 | + public function getSharedWith($userId, $shareType, $node, $limit, $offset): array { |
|
| 911 | + /** @var IShare[] $shares */ |
|
| 912 | + $shares = []; |
|
| 913 | + |
|
| 914 | + //Get shares directly with this user |
|
| 915 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 916 | + $qb->select('*') |
|
| 917 | + ->from('share'); |
|
| 918 | + |
|
| 919 | + // Order by id |
|
| 920 | + $qb->orderBy('id'); |
|
| 921 | + |
|
| 922 | + // Set limit and offset |
|
| 923 | + if ($limit !== -1) { |
|
| 924 | + $qb->setMaxResults($limit); |
|
| 925 | + } |
|
| 926 | + $qb->setFirstResult($offset); |
|
| 927 | + |
|
| 928 | + $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL))); |
|
| 929 | + $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); |
|
| 930 | + |
|
| 931 | + // Filter by node if provided |
|
| 932 | + if ($node !== null) { |
|
| 933 | + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
| 934 | + } |
|
| 935 | + |
|
| 936 | + $cursor = $qb->executeQuery(); |
|
| 937 | + |
|
| 938 | + while ($data = $cursor->fetch()) { |
|
| 939 | + $shares[] = $this->createShareObject($data); |
|
| 940 | + } |
|
| 941 | + $cursor->closeCursor(); |
|
| 942 | + |
|
| 943 | + |
|
| 944 | + return $shares; |
|
| 945 | + } |
|
| 946 | + |
|
| 947 | + /** |
|
| 948 | + * Get a share by token |
|
| 949 | + * |
|
| 950 | + * @throws ShareNotFound |
|
| 951 | + */ |
|
| 952 | + public function getShareByToken($token): IShare { |
|
| 953 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 954 | + |
|
| 955 | + $cursor = $qb->select('*') |
|
| 956 | + ->from('share') |
|
| 957 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL))) |
|
| 958 | + ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) |
|
| 959 | + ->executeQuery(); |
|
| 960 | + |
|
| 961 | + $data = $cursor->fetch(); |
|
| 962 | + |
|
| 963 | + if ($data === false) { |
|
| 964 | + throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
| 965 | + } |
|
| 966 | + |
|
| 967 | + try { |
|
| 968 | + $share = $this->createShareObject($data); |
|
| 969 | + } catch (InvalidShare $e) { |
|
| 970 | + throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
| 971 | + } |
|
| 972 | + |
|
| 973 | + return $share; |
|
| 974 | + } |
|
| 975 | + |
|
| 976 | + /** |
|
| 977 | + * remove share from table |
|
| 978 | + */ |
|
| 979 | + protected function removeShareFromTable(int $shareId): void { |
|
| 980 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 981 | + $qb->delete('share') |
|
| 982 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))); |
|
| 983 | + $qb->executeStatement(); |
|
| 984 | + } |
|
| 985 | + |
|
| 986 | + /** |
|
| 987 | + * Create a share object from a database row |
|
| 988 | + * |
|
| 989 | + * @throws InvalidShare |
|
| 990 | + * @throws ShareNotFound |
|
| 991 | + */ |
|
| 992 | + protected function createShareObject(array $data): IShare { |
|
| 993 | + $share = new Share($this->rootFolder, $this->userManager); |
|
| 994 | + $share->setId((int)$data['id']) |
|
| 995 | + ->setShareType((int)$data['share_type']) |
|
| 996 | + ->setPermissions((int)$data['permissions']) |
|
| 997 | + ->setTarget($data['file_target']) |
|
| 998 | + ->setMailSend((bool)$data['mail_send']) |
|
| 999 | + ->setNote($data['note']) |
|
| 1000 | + ->setToken($data['token']); |
|
| 1001 | + |
|
| 1002 | + $shareTime = new \DateTime(); |
|
| 1003 | + $shareTime->setTimestamp((int)$data['stime']); |
|
| 1004 | + $share->setShareTime($shareTime); |
|
| 1005 | + $share->setSharedWith($data['share_with'] ?? ''); |
|
| 1006 | + $share->setPassword($data['password']); |
|
| 1007 | + $passwordExpirationTime = \DateTime::createFromFormat('Y-m-d H:i:s', $data['password_expiration_time'] ?? ''); |
|
| 1008 | + $share->setPasswordExpirationTime($passwordExpirationTime !== false ? $passwordExpirationTime : null); |
|
| 1009 | + $share->setLabel($data['label'] ?? ''); |
|
| 1010 | + $share->setSendPasswordByTalk((bool)$data['password_by_talk']); |
|
| 1011 | + $share->setHideDownload((bool)$data['hide_download']); |
|
| 1012 | + $share->setReminderSent((bool)$data['reminder_sent']); |
|
| 1013 | + |
|
| 1014 | + if ($data['uid_initiator'] !== null) { |
|
| 1015 | + $share->setShareOwner($data['uid_owner']); |
|
| 1016 | + $share->setSharedBy($data['uid_initiator']); |
|
| 1017 | + } else { |
|
| 1018 | + //OLD SHARE |
|
| 1019 | + $share->setSharedBy($data['uid_owner']); |
|
| 1020 | + $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); |
|
| 1021 | + |
|
| 1022 | + $owner = $path->getOwner(); |
|
| 1023 | + $share->setShareOwner($owner->getUID()); |
|
| 1024 | + } |
|
| 1025 | + |
|
| 1026 | + if ($data['expiration'] !== null) { |
|
| 1027 | + $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']); |
|
| 1028 | + if ($expiration !== false) { |
|
| 1029 | + $share->setExpirationDate($expiration); |
|
| 1030 | + } |
|
| 1031 | + } |
|
| 1032 | + |
|
| 1033 | + $share = $this->updateShareAttributes($share, $data['attributes']); |
|
| 1034 | + |
|
| 1035 | + $share->setNodeId((int)$data['file_source']); |
|
| 1036 | + $share->setNodeType($data['item_type']); |
|
| 1037 | + |
|
| 1038 | + $share->setProviderId($this->identifier()); |
|
| 1039 | + |
|
| 1040 | + return $share; |
|
| 1041 | + } |
|
| 1042 | + |
|
| 1043 | + /** |
|
| 1044 | + * Get the node with file $id for $user |
|
| 1045 | + * |
|
| 1046 | + * @throws InvalidShare |
|
| 1047 | + */ |
|
| 1048 | + private function getNode(string $userId, int $id): Node { |
|
| 1049 | + try { |
|
| 1050 | + $userFolder = $this->rootFolder->getUserFolder($userId); |
|
| 1051 | + } catch (NoUserException $e) { |
|
| 1052 | + throw new InvalidShare(); |
|
| 1053 | + } |
|
| 1054 | + |
|
| 1055 | + $nodes = $userFolder->getById($id); |
|
| 1056 | + |
|
| 1057 | + if (empty($nodes)) { |
|
| 1058 | + throw new InvalidShare(); |
|
| 1059 | + } |
|
| 1060 | + |
|
| 1061 | + return $nodes[0]; |
|
| 1062 | + } |
|
| 1063 | + |
|
| 1064 | + /** |
|
| 1065 | + * A user is deleted from the system |
|
| 1066 | + * So clean up the relevant shares. |
|
| 1067 | + */ |
|
| 1068 | + public function userDeleted($uid, $shareType): void { |
|
| 1069 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1070 | + |
|
| 1071 | + $qb->delete('share') |
|
| 1072 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL))) |
|
| 1073 | + ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) |
|
| 1074 | + ->executeStatement(); |
|
| 1075 | + } |
|
| 1076 | + |
|
| 1077 | + /** |
|
| 1078 | + * This provider does not support group shares |
|
| 1079 | + */ |
|
| 1080 | + public function groupDeleted($gid): void { |
|
| 1081 | + } |
|
| 1082 | + |
|
| 1083 | + /** |
|
| 1084 | + * This provider does not support group shares |
|
| 1085 | + */ |
|
| 1086 | + public function userDeletedFromGroup($uid, $gid): void { |
|
| 1087 | + } |
|
| 1088 | + |
|
| 1089 | + /** |
|
| 1090 | + * get database row of a give share |
|
| 1091 | + * |
|
| 1092 | + * @throws ShareNotFound |
|
| 1093 | + */ |
|
| 1094 | + protected function getRawShare(int $id): array { |
|
| 1095 | + // Now fetch the inserted share and create a complete share object |
|
| 1096 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1097 | + $qb->select('*') |
|
| 1098 | + ->from('share') |
|
| 1099 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
| 1100 | + |
|
| 1101 | + $cursor = $qb->executeQuery(); |
|
| 1102 | + $data = $cursor->fetch(); |
|
| 1103 | + $cursor->closeCursor(); |
|
| 1104 | + |
|
| 1105 | + if ($data === false) { |
|
| 1106 | + throw new ShareNotFound; |
|
| 1107 | + } |
|
| 1108 | + |
|
| 1109 | + return $data; |
|
| 1110 | + } |
|
| 1111 | + |
|
| 1112 | + public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true): array { |
|
| 1113 | + return $this->getSharesInFolderInternal($userId, $node, $reshares); |
|
| 1114 | + } |
|
| 1115 | + |
|
| 1116 | + public function getAllSharesInFolder(Folder $node): array { |
|
| 1117 | + return $this->getSharesInFolderInternal(null, $node, null); |
|
| 1118 | + } |
|
| 1119 | + |
|
| 1120 | + /** |
|
| 1121 | + * @return array<int, list<IShare>> |
|
| 1122 | + */ |
|
| 1123 | + private function getSharesInFolderInternal(?string $userId, Folder $node, ?bool $reshares): array { |
|
| 1124 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1125 | + $qb->select('*') |
|
| 1126 | + ->from('share', 's') |
|
| 1127 | + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))) |
|
| 1128 | + ->andWhere( |
|
| 1129 | + $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL)) |
|
| 1130 | + ); |
|
| 1131 | + |
|
| 1132 | + if ($userId !== null) { |
|
| 1133 | + /** |
|
| 1134 | + * Reshares for this user are shares where they are the owner. |
|
| 1135 | + */ |
|
| 1136 | + if ($reshares !== true) { |
|
| 1137 | + $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); |
|
| 1138 | + } else { |
|
| 1139 | + $qb->andWhere( |
|
| 1140 | + $qb->expr()->orX( |
|
| 1141 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 1142 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
| 1143 | + ) |
|
| 1144 | + ); |
|
| 1145 | + } |
|
| 1146 | + } |
|
| 1147 | + |
|
| 1148 | + $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')); |
|
| 1149 | + |
|
| 1150 | + $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); |
|
| 1151 | + |
|
| 1152 | + $qb->orderBy('id'); |
|
| 1153 | + |
|
| 1154 | + $cursor = $qb->executeQuery(); |
|
| 1155 | + $shares = []; |
|
| 1156 | + while ($data = $cursor->fetch()) { |
|
| 1157 | + $shares[$data['fileid']][] = $this->createShareObject($data); |
|
| 1158 | + } |
|
| 1159 | + $cursor->closeCursor(); |
|
| 1160 | + |
|
| 1161 | + return $shares; |
|
| 1162 | + } |
|
| 1163 | + |
|
| 1164 | + /** |
|
| 1165 | + * @inheritdoc |
|
| 1166 | + */ |
|
| 1167 | + public function getAccessList($nodes, $currentAccess): array { |
|
| 1168 | + $ids = []; |
|
| 1169 | + foreach ($nodes as $node) { |
|
| 1170 | + $ids[] = $node->getId(); |
|
| 1171 | + } |
|
| 1172 | + |
|
| 1173 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1174 | + $qb->select('share_with', 'file_source', 'token') |
|
| 1175 | + ->from('share') |
|
| 1176 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL))) |
|
| 1177 | + ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) |
|
| 1178 | + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))); |
|
| 1179 | + $cursor = $qb->executeQuery(); |
|
| 1180 | + |
|
| 1181 | + $public = false; |
|
| 1182 | + $mail = []; |
|
| 1183 | + while ($row = $cursor->fetch()) { |
|
| 1184 | + $public = true; |
|
| 1185 | + if ($currentAccess === false) { |
|
| 1186 | + $mail[] = $row['share_with']; |
|
| 1187 | + } else { |
|
| 1188 | + $mail[$row['share_with']] = [ |
|
| 1189 | + 'node_id' => $row['file_source'], |
|
| 1190 | + 'token' => $row['token'] |
|
| 1191 | + ]; |
|
| 1192 | + } |
|
| 1193 | + } |
|
| 1194 | + $cursor->closeCursor(); |
|
| 1195 | + |
|
| 1196 | + return ['public' => $public, 'mail' => $mail]; |
|
| 1197 | + } |
|
| 1198 | + |
|
| 1199 | + public function getAllShares(): iterable { |
|
| 1200 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1201 | + |
|
| 1202 | + $qb->select('*') |
|
| 1203 | + ->from('share') |
|
| 1204 | + ->where( |
|
| 1205 | + $qb->expr()->orX( |
|
| 1206 | + $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL)) |
|
| 1207 | + ) |
|
| 1208 | + ); |
|
| 1209 | + |
|
| 1210 | + $cursor = $qb->executeQuery(); |
|
| 1211 | + while ($data = $cursor->fetch()) { |
|
| 1212 | + try { |
|
| 1213 | + $share = $this->createShareObject($data); |
|
| 1214 | + } catch (InvalidShare $e) { |
|
| 1215 | + continue; |
|
| 1216 | + } catch (ShareNotFound $e) { |
|
| 1217 | + continue; |
|
| 1218 | + } |
|
| 1219 | + |
|
| 1220 | + yield $share; |
|
| 1221 | + } |
|
| 1222 | + $cursor->closeCursor(); |
|
| 1223 | + } |
|
| 1224 | + |
|
| 1225 | + /** |
|
| 1226 | + * Extract the emails from the share |
|
| 1227 | + * It can be a single email, from the share_with field |
|
| 1228 | + * or a list of emails from the emails attributes field. |
|
| 1229 | + * @param IShare $share |
|
| 1230 | + * @return string[] |
|
| 1231 | + */ |
|
| 1232 | + protected function getSharedWithEmails(IShare $share): array { |
|
| 1233 | + $attributes = $share->getAttributes(); |
|
| 1234 | + |
|
| 1235 | + if ($attributes === null) { |
|
| 1236 | + return [$share->getSharedWith()]; |
|
| 1237 | + } |
|
| 1238 | + |
|
| 1239 | + $emails = $attributes->getAttribute('shareWith', 'emails'); |
|
| 1240 | + if (isset($emails) && is_array($emails) && !empty($emails)) { |
|
| 1241 | + return $emails; |
|
| 1242 | + } |
|
| 1243 | + return [$share->getSharedWith()]; |
|
| 1244 | + } |
|
| 1245 | 1245 | } |
@@ -158,7 +158,7 @@ discard block |
||
| 158 | 158 | [$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()], |
| 159 | 159 | $share->getSharedBy(), |
| 160 | 160 | $share->getNode()->getId(), |
| 161 | - (string)$userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 161 | + (string) $userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 162 | 162 | ); |
| 163 | 163 | |
| 164 | 164 | if ($share->getShareOwner() !== $share->getSharedBy()) { |
@@ -171,7 +171,7 @@ discard block |
||
| 171 | 171 | [$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()], |
| 172 | 172 | $share->getShareOwner(), |
| 173 | 173 | $fileId, |
| 174 | - (string)$ownerFolder->getRelativePath($ownerPath) |
|
| 174 | + (string) $ownerFolder->getRelativePath($ownerPath) |
|
| 175 | 175 | ); |
| 176 | 176 | } |
| 177 | 177 | } |
@@ -188,7 +188,7 @@ discard block |
||
| 188 | 188 | [$userFolder->getRelativePath($share->getNode()->getPath())], |
| 189 | 189 | $share->getSharedBy(), |
| 190 | 190 | $share->getNode()->getId(), |
| 191 | - (string)$userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 191 | + (string) $userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 192 | 192 | ); |
| 193 | 193 | } else { |
| 194 | 194 | $this->publishActivity( |
@@ -196,7 +196,7 @@ discard block |
||
| 196 | 196 | [$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith], |
| 197 | 197 | $share->getSharedBy(), |
| 198 | 198 | $share->getNode()->getId(), |
| 199 | - (string)$userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 199 | + (string) $userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 200 | 200 | ); |
| 201 | 201 | } |
| 202 | 202 | } |
@@ -247,15 +247,15 @@ discard block |
||
| 247 | 247 | $shareId = $share->getId(); |
| 248 | 248 | |
| 249 | 249 | $emails = $this->getSharedWithEmails($share); |
| 250 | - $validEmails = array_filter($emails, function (string $email) { |
|
| 250 | + $validEmails = array_filter($emails, function(string $email) { |
|
| 251 | 251 | return $this->emailValidator->isValid($email); |
| 252 | 252 | }); |
| 253 | 253 | |
| 254 | 254 | if (count($validEmails) === 0) { |
| 255 | - $this->removeShareFromTable((int)$shareId); |
|
| 256 | - $e = new HintException('Failed to send share by mail. Could not find a valid email address: ' . join(', ', $emails), |
|
| 255 | + $this->removeShareFromTable((int) $shareId); |
|
| 256 | + $e = new HintException('Failed to send share by mail. Could not find a valid email address: '.join(', ', $emails), |
|
| 257 | 257 | $this->l->t('Failed to send share by email. Got an invalid email address')); |
| 258 | - $this->logger->error('Failed to send share by mail. Could not find a valid email address ' . join(', ', $emails), [ |
|
| 258 | + $this->logger->error('Failed to send share by mail. Could not find a valid email address '.join(', ', $emails), [ |
|
| 259 | 259 | 'app' => 'sharebymail', |
| 260 | 260 | 'exception' => $e, |
| 261 | 261 | ]); |
@@ -287,14 +287,14 @@ discard block |
||
| 287 | 287 | 'app' => 'sharebymail', |
| 288 | 288 | 'exception' => $hintException, |
| 289 | 289 | ]); |
| 290 | - $this->removeShareFromTable((int)$shareId); |
|
| 290 | + $this->removeShareFromTable((int) $shareId); |
|
| 291 | 291 | throw $hintException; |
| 292 | 292 | } catch (\Exception $e) { |
| 293 | 293 | $this->logger->error('Failed to send share by mail.', [ |
| 294 | 294 | 'app' => 'sharebymail', |
| 295 | 295 | 'exception' => $e, |
| 296 | 296 | ]); |
| 297 | - $this->removeShareFromTable((int)$shareId); |
|
| 297 | + $this->removeShareFromTable((int) $shareId); |
|
| 298 | 298 | throw new HintException( |
| 299 | 299 | 'Failed to send share by mail', |
| 300 | 300 | $this->l->t('Failed to send share by email'), |
@@ -347,7 +347,7 @@ discard block |
||
| 347 | 347 | } |
| 348 | 348 | |
| 349 | 349 | if ($expiration !== null) { |
| 350 | - $dateString = (string)$this->l->l('date', $expiration, ['width' => 'medium']); |
|
| 350 | + $dateString = (string) $this->l->l('date', $expiration, ['width' => 'medium']); |
|
| 351 | 351 | $emailTemplate->addBodyListItem( |
| 352 | 352 | $this->l->t('This share is valid until %s at midnight', [$dateString]), |
| 353 | 353 | $this->l->t('Expiration:'), |
@@ -388,7 +388,7 @@ discard block |
||
| 388 | 388 | $initiatorEmail = $initiatorUser->getEMailAddress(); |
| 389 | 389 | if ($initiatorEmail !== null) { |
| 390 | 390 | $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]); |
| 391 | - $emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')); |
|
| 391 | + $emailTemplate->addFooter($instanceName.($this->defaults->getSlogan() !== '' ? ' - '.$this->defaults->getSlogan() : '')); |
|
| 392 | 392 | } else { |
| 393 | 393 | $emailTemplate->addFooter(); |
| 394 | 394 | } |
@@ -399,7 +399,7 @@ discard block |
||
| 399 | 399 | $message->useTemplate($emailTemplate); |
| 400 | 400 | $failedRecipients = $this->mailer->send($message); |
| 401 | 401 | if (!empty($failedRecipients)) { |
| 402 | - $this->logger->error('Share notification mail could not be sent to: ' . implode(', ', $failedRecipients)); |
|
| 402 | + $this->logger->error('Share notification mail could not be sent to: '.implode(', ', $failedRecipients)); |
|
| 403 | 403 | return; |
| 404 | 404 | } |
| 405 | 405 | } |
@@ -452,7 +452,7 @@ discard block |
||
| 452 | 452 | if ($this->config->getSystemValue('sharing.enable_mail_link_password_expiration', false) === true) { |
| 453 | 453 | $expirationTime = new \DateTime(); |
| 454 | 454 | $expirationInterval = $this->config->getSystemValue('sharing.mail_link_password_expiration_interval', 3600); |
| 455 | - $expirationTime = $expirationTime->add(new \DateInterval('PT' . $expirationInterval . 'S')); |
|
| 455 | + $expirationTime = $expirationTime->add(new \DateInterval('PT'.$expirationInterval.'S')); |
|
| 456 | 456 | $emailTemplate->addBodyText($this->l->t('This password will expire at %s', [$expirationTime->format('r')])); |
| 457 | 457 | } |
| 458 | 458 | |
@@ -484,7 +484,7 @@ discard block |
||
| 484 | 484 | $initiatorEmail = $initiatorUser->getEMailAddress(); |
| 485 | 485 | if ($initiatorEmail !== null) { |
| 486 | 486 | $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]); |
| 487 | - $emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')); |
|
| 487 | + $emailTemplate->addFooter($instanceName.($this->defaults->getSlogan() !== '' ? ' - '.$this->defaults->getSlogan() : '')); |
|
| 488 | 488 | } else { |
| 489 | 489 | $emailTemplate->addFooter(); |
| 490 | 490 | } |
@@ -495,7 +495,7 @@ discard block |
||
| 495 | 495 | $message->useTemplate($emailTemplate); |
| 496 | 496 | $failedRecipients = $this->mailer->send($message); |
| 497 | 497 | if (!empty($failedRecipients)) { |
| 498 | - $this->logger->error('Share password mail could not be sent to: ' . implode(', ', $failedRecipients)); |
|
| 498 | + $this->logger->error('Share password mail could not be sent to: '.implode(', ', $failedRecipients)); |
|
| 499 | 499 | return false; |
| 500 | 500 | } |
| 501 | 501 | |
@@ -549,7 +549,7 @@ discard block |
||
| 549 | 549 | $message->setFrom([Util::getDefaultEmailAddress($instanceName) => $senderName]); |
| 550 | 550 | if ($this->settingsManager->replyToInitiator() && $initiatorEmailAddress !== null) { |
| 551 | 551 | $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]); |
| 552 | - $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan()); |
|
| 552 | + $emailTemplate->addFooter($instanceName.' - '.$this->defaults->getSlogan()); |
|
| 553 | 553 | } else { |
| 554 | 554 | $emailTemplate->addFooter(); |
| 555 | 555 | } |
@@ -599,7 +599,7 @@ discard block |
||
| 599 | 599 | if ($this->config->getSystemValue('sharing.enable_mail_link_password_expiration', false) === true) { |
| 600 | 600 | $expirationTime = new \DateTime(); |
| 601 | 601 | $expirationInterval = $this->config->getSystemValue('sharing.mail_link_password_expiration_interval', 3600); |
| 602 | - $expirationTime = $expirationTime->add(new \DateInterval('PT' . $expirationInterval . 'S')); |
|
| 602 | + $expirationTime = $expirationTime->add(new \DateInterval('PT'.$expirationInterval.'S')); |
|
| 603 | 603 | $emailTemplate->addBodyText($this->l->t('This password will expire at %s', [$expirationTime->format('r')])); |
| 604 | 604 | } |
| 605 | 605 | |
@@ -694,10 +694,10 @@ discard block |
||
| 694 | 694 | ->setValue('password_expiration_time', $qb->createNamedParameter($passwordExpirationTime, IQueryBuilder::PARAM_DATETIME_MUTABLE)) |
| 695 | 695 | ->setValue('password_by_talk', $qb->createNamedParameter($sendPasswordByTalk, IQueryBuilder::PARAM_BOOL)) |
| 696 | 696 | ->setValue('stime', $qb->createNamedParameter(time())) |
| 697 | - ->setValue('hide_download', $qb->createNamedParameter((int)$hideDownload, IQueryBuilder::PARAM_INT)) |
|
| 697 | + ->setValue('hide_download', $qb->createNamedParameter((int) $hideDownload, IQueryBuilder::PARAM_INT)) |
|
| 698 | 698 | ->setValue('label', $qb->createNamedParameter($label)) |
| 699 | 699 | ->setValue('note', $qb->createNamedParameter($note)) |
| 700 | - ->setValue('mail_send', $qb->createNamedParameter((int)$mailSend, IQueryBuilder::PARAM_INT)); |
|
| 700 | + ->setValue('mail_send', $qb->createNamedParameter((int) $mailSend, IQueryBuilder::PARAM_INT)); |
|
| 701 | 701 | |
| 702 | 702 | // set share attributes |
| 703 | 703 | $shareAttributes = $this->formatShareAttributes($attributes); |
@@ -723,7 +723,7 @@ discard block |
||
| 723 | 723 | if ($validPassword && ($originalShare->getPassword() !== $share->getPassword() |
| 724 | 724 | || ($originalShare->getSendPasswordByTalk() && !$share->getSendPasswordByTalk()))) { |
| 725 | 725 | $emails = $this->getSharedWithEmails($share); |
| 726 | - $validEmails = array_filter($emails, function ($email) { |
|
| 726 | + $validEmails = array_filter($emails, function($email) { |
|
| 727 | 727 | return $this->emailValidator->isValid($email); |
| 728 | 728 | }); |
| 729 | 729 | $this->sendPassword($share, $plainTextPassword, $validEmails); |
@@ -749,9 +749,9 @@ discard block |
||
| 749 | 749 | ->set('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL)) |
| 750 | 750 | ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATETIME_MUTABLE)) |
| 751 | 751 | ->set('note', $qb->createNamedParameter($share->getNote())) |
| 752 | - ->set('hide_download', $qb->createNamedParameter((int)$share->getHideDownload(), IQueryBuilder::PARAM_INT)) |
|
| 752 | + ->set('hide_download', $qb->createNamedParameter((int) $share->getHideDownload(), IQueryBuilder::PARAM_INT)) |
|
| 753 | 753 | ->set('attributes', $qb->createNamedParameter($shareAttributes)) |
| 754 | - ->set('mail_send', $qb->createNamedParameter((int)$share->getMailSend(), IQueryBuilder::PARAM_INT)) |
|
| 754 | + ->set('mail_send', $qb->createNamedParameter((int) $share->getMailSend(), IQueryBuilder::PARAM_INT)) |
|
| 755 | 755 | ->set('reminder_sent', $qb->createNamedParameter($share->getReminderSent(), IQueryBuilder::PARAM_BOOL)) |
| 756 | 756 | ->executeStatement(); |
| 757 | 757 | |
@@ -783,7 +783,7 @@ discard block |
||
| 783 | 783 | } catch (\Exception $e) { |
| 784 | 784 | } |
| 785 | 785 | |
| 786 | - $this->removeShareFromTable((int)$share->getId()); |
|
| 786 | + $this->removeShareFromTable((int) $share->getId()); |
|
| 787 | 787 | } |
| 788 | 788 | |
| 789 | 789 | /** |
@@ -991,25 +991,25 @@ discard block |
||
| 991 | 991 | */ |
| 992 | 992 | protected function createShareObject(array $data): IShare { |
| 993 | 993 | $share = new Share($this->rootFolder, $this->userManager); |
| 994 | - $share->setId((int)$data['id']) |
|
| 995 | - ->setShareType((int)$data['share_type']) |
|
| 996 | - ->setPermissions((int)$data['permissions']) |
|
| 994 | + $share->setId((int) $data['id']) |
|
| 995 | + ->setShareType((int) $data['share_type']) |
|
| 996 | + ->setPermissions((int) $data['permissions']) |
|
| 997 | 997 | ->setTarget($data['file_target']) |
| 998 | - ->setMailSend((bool)$data['mail_send']) |
|
| 998 | + ->setMailSend((bool) $data['mail_send']) |
|
| 999 | 999 | ->setNote($data['note']) |
| 1000 | 1000 | ->setToken($data['token']); |
| 1001 | 1001 | |
| 1002 | 1002 | $shareTime = new \DateTime(); |
| 1003 | - $shareTime->setTimestamp((int)$data['stime']); |
|
| 1003 | + $shareTime->setTimestamp((int) $data['stime']); |
|
| 1004 | 1004 | $share->setShareTime($shareTime); |
| 1005 | 1005 | $share->setSharedWith($data['share_with'] ?? ''); |
| 1006 | 1006 | $share->setPassword($data['password']); |
| 1007 | 1007 | $passwordExpirationTime = \DateTime::createFromFormat('Y-m-d H:i:s', $data['password_expiration_time'] ?? ''); |
| 1008 | 1008 | $share->setPasswordExpirationTime($passwordExpirationTime !== false ? $passwordExpirationTime : null); |
| 1009 | 1009 | $share->setLabel($data['label'] ?? ''); |
| 1010 | - $share->setSendPasswordByTalk((bool)$data['password_by_talk']); |
|
| 1011 | - $share->setHideDownload((bool)$data['hide_download']); |
|
| 1012 | - $share->setReminderSent((bool)$data['reminder_sent']); |
|
| 1010 | + $share->setSendPasswordByTalk((bool) $data['password_by_talk']); |
|
| 1011 | + $share->setHideDownload((bool) $data['hide_download']); |
|
| 1012 | + $share->setReminderSent((bool) $data['reminder_sent']); |
|
| 1013 | 1013 | |
| 1014 | 1014 | if ($data['uid_initiator'] !== null) { |
| 1015 | 1015 | $share->setShareOwner($data['uid_owner']); |
@@ -1017,7 +1017,7 @@ discard block |
||
| 1017 | 1017 | } else { |
| 1018 | 1018 | //OLD SHARE |
| 1019 | 1019 | $share->setSharedBy($data['uid_owner']); |
| 1020 | - $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); |
|
| 1020 | + $path = $this->getNode($share->getSharedBy(), (int) $data['file_source']); |
|
| 1021 | 1021 | |
| 1022 | 1022 | $owner = $path->getOwner(); |
| 1023 | 1023 | $share->setShareOwner($owner->getUID()); |
@@ -1032,7 +1032,7 @@ discard block |
||
| 1032 | 1032 | |
| 1033 | 1033 | $share = $this->updateShareAttributes($share, $data['attributes']); |
| 1034 | 1034 | |
| 1035 | - $share->setNodeId((int)$data['file_source']); |
|
| 1035 | + $share->setNodeId((int) $data['file_source']); |
|
| 1036 | 1036 | $share->setNodeType($data['item_type']); |
| 1037 | 1037 | |
| 1038 | 1038 | $share->setProviderId($this->identifier()); |
@@ -50,1861 +50,1861 @@ |
||
| 50 | 50 | * @group DB |
| 51 | 51 | */ |
| 52 | 52 | class ShareByMailProviderTest extends TestCase { |
| 53 | - use EmailValidatorTrait; |
|
| 54 | - |
|
| 55 | - private IDBConnection $connection; |
|
| 56 | - |
|
| 57 | - private IL10N&MockObject $l; |
|
| 58 | - private IShare&MockObject $share; |
|
| 59 | - private IConfig&MockObject $config; |
|
| 60 | - private IMailer&MockObject $mailer; |
|
| 61 | - private IHasher&MockObject $hasher; |
|
| 62 | - private Defaults&MockObject $defaults; |
|
| 63 | - private IManager&MockObject $shareManager; |
|
| 64 | - private LoggerInterface&MockObject $logger; |
|
| 65 | - private IRootFolder&MockObject $rootFolder; |
|
| 66 | - private IUserManager&MockObject $userManager; |
|
| 67 | - private ISecureRandom&MockObject $secureRandom; |
|
| 68 | - private IURLGenerator&MockObject $urlGenerator; |
|
| 69 | - private SettingsManager&MockObject $settingsManager; |
|
| 70 | - private IActivityManager&MockObject $activityManager; |
|
| 71 | - private IEventDispatcher&MockObject $eventDispatcher; |
|
| 72 | - |
|
| 73 | - protected function setUp(): void { |
|
| 74 | - parent::setUp(); |
|
| 75 | - |
|
| 76 | - $this->connection = Server::get(IDBConnection::class); |
|
| 77 | - |
|
| 78 | - $this->l = $this->createMock(IL10N::class); |
|
| 79 | - $this->l->method('t') |
|
| 80 | - ->willReturnCallback(function ($text, $parameters = []) { |
|
| 81 | - return vsprintf($text, $parameters); |
|
| 82 | - }); |
|
| 83 | - $this->config = $this->createMock(IConfig::class); |
|
| 84 | - $this->logger = $this->createMock(LoggerInterface::class); |
|
| 85 | - $this->rootFolder = $this->createMock('OCP\Files\IRootFolder'); |
|
| 86 | - $this->userManager = $this->createMock(IUserManager::class); |
|
| 87 | - $this->secureRandom = $this->createMock('\OCP\Security\ISecureRandom'); |
|
| 88 | - $this->mailer = $this->createMock('\OCP\Mail\IMailer'); |
|
| 89 | - $this->urlGenerator = $this->createMock(IURLGenerator::class); |
|
| 90 | - $this->share = $this->createMock(IShare::class); |
|
| 91 | - $this->activityManager = $this->createMock('OCP\Activity\IManager'); |
|
| 92 | - $this->settingsManager = $this->createMock(SettingsManager::class); |
|
| 93 | - $this->defaults = $this->createMock(Defaults::class); |
|
| 94 | - $this->hasher = $this->createMock(IHasher::class); |
|
| 95 | - $this->eventDispatcher = $this->createMock(IEventDispatcher::class); |
|
| 96 | - $this->shareManager = $this->createMock(IManager::class); |
|
| 97 | - |
|
| 98 | - $this->userManager->expects($this->any())->method('userExists')->willReturn(true); |
|
| 99 | - $this->config->expects($this->any())->method('getAppValue')->with('core', 'enforce_strict_email_check')->willReturn('yes'); |
|
| 100 | - } |
|
| 101 | - |
|
| 102 | - /** |
|
| 103 | - * get instance of Mocked ShareByMailProvider |
|
| 104 | - * |
|
| 105 | - * @param array $mockedMethods internal methods which should be mocked |
|
| 106 | - * @return \PHPUnit\Framework\MockObject\MockObject | ShareByMailProvider |
|
| 107 | - */ |
|
| 108 | - private function getInstance(array $mockedMethods = []) { |
|
| 109 | - if (!empty($mockedMethods)) { |
|
| 110 | - return $this->getMockBuilder(ShareByMailProvider::class) |
|
| 111 | - ->setConstructorArgs([ |
|
| 112 | - $this->config, |
|
| 113 | - $this->connection, |
|
| 114 | - $this->secureRandom, |
|
| 115 | - $this->userManager, |
|
| 116 | - $this->rootFolder, |
|
| 117 | - $this->l, |
|
| 118 | - $this->logger, |
|
| 119 | - $this->mailer, |
|
| 120 | - $this->urlGenerator, |
|
| 121 | - $this->activityManager, |
|
| 122 | - $this->settingsManager, |
|
| 123 | - $this->defaults, |
|
| 124 | - $this->hasher, |
|
| 125 | - $this->eventDispatcher, |
|
| 126 | - $this->shareManager, |
|
| 127 | - $this->getEmailValidatorWithStrictEmailCheck(), |
|
| 128 | - ]) |
|
| 129 | - ->onlyMethods($mockedMethods) |
|
| 130 | - ->getMock(); |
|
| 131 | - } |
|
| 132 | - |
|
| 133 | - return new ShareByMailProvider( |
|
| 134 | - $this->config, |
|
| 135 | - $this->connection, |
|
| 136 | - $this->secureRandom, |
|
| 137 | - $this->userManager, |
|
| 138 | - $this->rootFolder, |
|
| 139 | - $this->l, |
|
| 140 | - $this->logger, |
|
| 141 | - $this->mailer, |
|
| 142 | - $this->urlGenerator, |
|
| 143 | - $this->activityManager, |
|
| 144 | - $this->settingsManager, |
|
| 145 | - $this->defaults, |
|
| 146 | - $this->hasher, |
|
| 147 | - $this->eventDispatcher, |
|
| 148 | - $this->shareManager, |
|
| 149 | - $this->getEmailValidatorWithStrictEmailCheck(), |
|
| 150 | - ); |
|
| 151 | - } |
|
| 152 | - |
|
| 153 | - protected function tearDown(): void { |
|
| 154 | - $this->connection |
|
| 155 | - ->getQueryBuilder() |
|
| 156 | - ->delete('share') |
|
| 157 | - ->executeStatement(); |
|
| 158 | - |
|
| 159 | - parent::tearDown(); |
|
| 160 | - } |
|
| 161 | - |
|
| 162 | - public function testCreate(): void { |
|
| 163 | - $expectedShare = $this->createMock(IShare::class); |
|
| 164 | - |
|
| 165 | - $share = $this->createMock(IShare::class); |
|
| 166 | - $share->expects($this->any())->method('getSharedWith')->willReturn('user1'); |
|
| 167 | - |
|
| 168 | - $node = $this->createMock(File::class); |
|
| 169 | - $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 170 | - |
|
| 171 | - $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', 'createShareActivity', 'sendEmail', 'sendPassword']); |
|
| 172 | - |
|
| 173 | - $instance->expects($this->once())->method('getSharedWith')->willReturn([]); |
|
| 174 | - $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); |
|
| 175 | - $instance->expects($this->once())->method('createShareActivity')->with($share); |
|
| 176 | - $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn(['rawShare']); |
|
| 177 | - $instance->expects($this->once())->method('createShareObject')->with(['rawShare'])->willReturn($expectedShare); |
|
| 178 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 179 | - |
|
| 180 | - // As share api link password is not enforced, the password will not be generated. |
|
| 181 | - $this->shareManager->expects($this->once())->method('shareApiLinkEnforcePassword')->willReturn(false); |
|
| 182 | - $this->settingsManager->expects($this->never())->method('sendPasswordByMail'); |
|
| 183 | - |
|
| 184 | - // Mail notification is triggered by the share manager. |
|
| 185 | - $instance->expects($this->never())->method('sendEmail'); |
|
| 186 | - $instance->expects($this->never())->method('sendPassword'); |
|
| 187 | - |
|
| 188 | - $this->assertSame($expectedShare, $instance->create($share)); |
|
| 189 | - } |
|
| 190 | - |
|
| 191 | - public function testCreateSendPasswordByMailWithoutEnforcedPasswordProtection(): void { |
|
| 192 | - $expectedShare = $this->createMock(IShare::class); |
|
| 193 | - |
|
| 194 | - $node = $this->createMock(File::class); |
|
| 195 | - $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 196 | - |
|
| 197 | - $share = $this->createMock(IShare::class); |
|
| 198 | - $share->expects($this->any())->method('getSharedWith')->willReturn('receiver@examplelölöl.com'); |
|
| 199 | - $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(false); |
|
| 200 | - $share->expects($this->any())->method('getSharedBy')->willReturn('owner'); |
|
| 201 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 202 | - $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 203 | - $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 204 | - $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 205 | - |
|
| 206 | - $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', 'createShareActivity', 'autoGeneratePassword', 'createPasswordSendActivity', 'sendEmail', 'sendPassword', 'sendPasswordToOwner']); |
|
| 207 | - $instance->expects($this->once())->method('getSharedWith')->willReturn([]); |
|
| 208 | - $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); |
|
| 209 | - $instance->expects($this->once())->method('createShareActivity')->with($share); |
|
| 210 | - $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn(['rawShare']); |
|
| 211 | - $instance->expects($this->once())->method('createShareObject')->with(['rawShare'])->willReturn($expectedShare); |
|
| 212 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 213 | - |
|
| 214 | - // The autogenerated password should not be mailed. |
|
| 215 | - $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(false); |
|
| 216 | - $this->settingsManager->expects($this->any())->method('sendPasswordByMail')->willReturn(true); |
|
| 217 | - $instance->expects($this->never())->method('autoGeneratePassword'); |
|
| 218 | - |
|
| 219 | - // No password is set and no password sent via talk is requested |
|
| 220 | - $instance->expects($this->once())->method('sendEmail')->with($share, ['receiver@examplelölöl.com']); |
|
| 221 | - $instance->expects($this->never())->method('sendPassword'); |
|
| 222 | - $instance->expects($this->never())->method('sendPasswordToOwner'); |
|
| 223 | - |
|
| 224 | - // The manager sends the mail notification. |
|
| 225 | - // For the sake of testing simplicity, we will handle it ourselves. |
|
| 226 | - $this->assertSame($expectedShare, $instance->create($share)); |
|
| 227 | - $instance->sendMailNotification($share); |
|
| 228 | - } |
|
| 229 | - |
|
| 230 | - public function testCreateSendPasswordByMailWithPasswordAndWithoutEnforcedPasswordProtectionWithPermanentPassword(): void { |
|
| 231 | - $expectedShare = $this->createMock(IShare::class); |
|
| 232 | - |
|
| 233 | - $node = $this->createMock(File::class); |
|
| 234 | - $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 235 | - |
|
| 236 | - $share = $this->createMock(IShare::class); |
|
| 237 | - $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 238 | - $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(false); |
|
| 239 | - $share->expects($this->any())->method('getSharedBy')->willReturn('owner'); |
|
| 240 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 241 | - $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 242 | - $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 243 | - $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 244 | - |
|
| 245 | - $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', 'createShareActivity', 'autoGeneratePassword', 'createPasswordSendActivity', 'sendEmail', 'sendPassword', 'sendPasswordToOwner']); |
|
| 246 | - |
|
| 247 | - $instance->expects($this->once())->method('getSharedWith')->willReturn([]); |
|
| 248 | - $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); |
|
| 249 | - $instance->expects($this->once())->method('createShareActivity')->with($share); |
|
| 250 | - $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn(['rawShare', 'password' => 'password']); |
|
| 251 | - $instance->expects($this->once())->method('createShareObject')->with(['rawShare', 'password' => 'password'])->willReturn($expectedShare); |
|
| 252 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 253 | - |
|
| 254 | - $share->expects($this->any())->method('getPassword')->willReturn('password'); |
|
| 255 | - $this->hasher->expects($this->once())->method('hash')->with('password')->willReturn('passwordHashed'); |
|
| 256 | - $share->expects($this->once())->method('setPassword')->with('passwordHashed'); |
|
| 257 | - |
|
| 258 | - // The given password (but not the autogenerated password) should not be |
|
| 259 | - // mailed to the receiver of the share because permanent passwords are not enforced. |
|
| 260 | - $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(false); |
|
| 261 | - $this->config->expects($this->once())->method('getSystemValue')->with('sharing.enable_mail_link_password_expiration')->willReturn(false); |
|
| 262 | - $instance->expects($this->never())->method('autoGeneratePassword'); |
|
| 263 | - |
|
| 264 | - // A password is set but no password sent via talk has been requested |
|
| 265 | - $instance->expects($this->once())->method('sendEmail')->with($share, ['[email protected]']); |
|
| 266 | - $instance->expects($this->once())->method('sendPassword')->with($share, 'password'); |
|
| 267 | - $instance->expects($this->never())->method('sendPasswordToOwner'); |
|
| 268 | - |
|
| 269 | - $this->assertSame($expectedShare, $instance->create($share)); |
|
| 270 | - $instance->sendMailNotification($share); |
|
| 271 | - } |
|
| 272 | - |
|
| 273 | - public function testCreateSendPasswordByMailWithPasswordAndWithoutEnforcedPasswordProtectionWithoutPermanentPassword(): void { |
|
| 274 | - $expectedShare = $this->createMock(IShare::class); |
|
| 275 | - |
|
| 276 | - $node = $this->createMock(File::class); |
|
| 277 | - $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 278 | - |
|
| 279 | - $share = $this->createMock(IShare::class); |
|
| 280 | - $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 281 | - $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(false); |
|
| 282 | - $share->expects($this->any())->method('getSharedBy')->willReturn('owner'); |
|
| 283 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 284 | - $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 285 | - $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 286 | - $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 287 | - |
|
| 288 | - $instance = $this->getInstance([ |
|
| 289 | - 'getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', |
|
| 290 | - 'createShareActivity', 'autoGeneratePassword', 'createPasswordSendActivity', |
|
| 291 | - 'sendEmail', 'sendPassword', 'sendPasswordToOwner', |
|
| 292 | - ]); |
|
| 293 | - |
|
| 294 | - $instance->expects($this->once())->method('getSharedWith')->willReturn([]); |
|
| 295 | - $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); |
|
| 296 | - $instance->expects($this->once())->method('createShareActivity')->with($share); |
|
| 297 | - $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn(['rawShare', 'password' => 'password']); |
|
| 298 | - $instance->expects($this->once())->method('createShareObject')->with(['rawShare', 'password' => 'password'])->willReturn($expectedShare); |
|
| 299 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 300 | - |
|
| 301 | - $share->expects($this->any())->method('getPassword')->willReturn('password'); |
|
| 302 | - $this->hasher->expects($this->once())->method('hash')->with('password')->willReturn('passwordHashed'); |
|
| 303 | - $share->expects($this->once())->method('setPassword')->with('passwordHashed'); |
|
| 304 | - |
|
| 305 | - // No password is generated, so no emails need to be sent |
|
| 306 | - // aside from the main email notification. |
|
| 307 | - $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(false); |
|
| 308 | - $instance->expects($this->never())->method('autoGeneratePassword'); |
|
| 309 | - $this->config->expects($this->once())->method('getSystemValue') |
|
| 310 | - ->with('sharing.enable_mail_link_password_expiration') |
|
| 311 | - ->willReturn(true); |
|
| 312 | - |
|
| 313 | - // No password has been set and no password sent via talk has been requested, |
|
| 314 | - // but password has been enforced for the whole instance and will be generated. |
|
| 315 | - $instance->expects($this->once())->method('sendEmail')->with($share, ['[email protected]']); |
|
| 316 | - $instance->expects($this->never())->method('sendPassword'); |
|
| 317 | - $instance->expects($this->never())->method('sendPasswordToOwner'); |
|
| 318 | - |
|
| 319 | - $this->assertSame($expectedShare, $instance->create($share)); |
|
| 320 | - $instance->sendMailNotification($share); |
|
| 321 | - } |
|
| 322 | - |
|
| 323 | - public function testCreateSendPasswordByMailWithEnforcedPasswordProtectionWithPermanentPassword(): void { |
|
| 324 | - $expectedShare = $this->createMock(IShare::class); |
|
| 325 | - |
|
| 326 | - $node = $this->createMock(File::class); |
|
| 327 | - $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 328 | - |
|
| 329 | - $share = $this->createMock(IShare::class); |
|
| 330 | - $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 331 | - $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(false); |
|
| 332 | - $share->expects($this->any())->method('getSharedBy')->willReturn('owner'); |
|
| 333 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 334 | - $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 335 | - $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 336 | - $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 337 | - |
|
| 338 | - $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 339 | - ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 340 | - ->willReturn('https://example.com/file.txt'); |
|
| 341 | - |
|
| 342 | - $this->secureRandom->expects($this->once()) |
|
| 343 | - ->method('generate') |
|
| 344 | - ->with(8, ISecureRandom::CHAR_HUMAN_READABLE) |
|
| 345 | - ->willReturn('autogeneratedPassword'); |
|
| 346 | - $this->eventDispatcher->expects($this->once()) |
|
| 347 | - ->method('dispatchTyped') |
|
| 348 | - ->with(new GenerateSecurePasswordEvent(PasswordContext::SHARING)); |
|
| 349 | - |
|
| 350 | - $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', 'createShareActivity', 'createPasswordSendActivity', 'sendPasswordToOwner']); |
|
| 351 | - |
|
| 352 | - $instance->expects($this->once())->method('getSharedWith')->willReturn([]); |
|
| 353 | - $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); |
|
| 354 | - $instance->expects($this->once())->method('createShareActivity')->with($share); |
|
| 355 | - $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn(['rawShare', 'password' => 'autogeneratedPassword']); |
|
| 356 | - $instance->expects($this->once())->method('createShareObject')->with(['rawShare', 'password' => 'autogeneratedPassword'])->willReturn($expectedShare); |
|
| 357 | - |
|
| 358 | - // Initially not set, but will be set by the autoGeneratePassword method. |
|
| 359 | - $share->expects($this->exactly(3))->method('getPassword')->willReturnOnConsecutiveCalls(null, 'autogeneratedPassword', 'autogeneratedPassword'); |
|
| 360 | - $this->hasher->expects($this->once())->method('hash')->with('autogeneratedPassword')->willReturn('autogeneratedPasswordHashed'); |
|
| 361 | - $share->expects($this->once())->method('setPassword')->with('autogeneratedPasswordHashed'); |
|
| 362 | - |
|
| 363 | - // The autogenerated password should be mailed to the receiver of the share because permanent passwords are enforced. |
|
| 364 | - $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(true); |
|
| 365 | - $this->config->expects($this->any())->method('getSystemValue')->with('sharing.enable_mail_link_password_expiration')->willReturn(false); |
|
| 366 | - $this->settingsManager->expects($this->any())->method('sendPasswordByMail')->willReturn(true); |
|
| 367 | - |
|
| 368 | - $message = $this->createMock(IMessage::class); |
|
| 369 | - $message->expects($this->exactly(2))->method('setTo')->with(['[email protected]']); |
|
| 370 | - $this->mailer->expects($this->exactly(2))->method('createMessage')->willReturn($message); |
|
| 371 | - $calls = [ |
|
| 372 | - [ |
|
| 373 | - 'sharebymail.RecipientNotification', |
|
| 374 | - [ |
|
| 375 | - 'filename' => 'filename', |
|
| 376 | - 'link' => 'https://example.com/file.txt', |
|
| 377 | - 'initiator' => 'owner', |
|
| 378 | - 'expiration' => null, |
|
| 379 | - 'shareWith' => '[email protected]', |
|
| 380 | - 'note' => '', |
|
| 381 | - ], |
|
| 382 | - ], |
|
| 383 | - [ |
|
| 384 | - 'sharebymail.RecipientPasswordNotification', |
|
| 385 | - [ |
|
| 386 | - 'filename' => 'filename', |
|
| 387 | - 'password' => 'autogeneratedPassword', |
|
| 388 | - 'initiator' => 'owner', |
|
| 389 | - 'initiatorEmail' => null, |
|
| 390 | - 'shareWith' => '[email protected]', |
|
| 391 | - ], |
|
| 392 | - ], |
|
| 393 | - ]; |
|
| 394 | - $this->mailer->expects($this->exactly(2)) |
|
| 395 | - ->method('createEMailTemplate') |
|
| 396 | - ->willReturnCallback(function () use (&$calls) { |
|
| 397 | - $expected = array_shift($calls); |
|
| 398 | - $this->assertEquals($expected, func_get_args()); |
|
| 399 | - return $this->createMock(IEMailTemplate::class); |
|
| 400 | - }); |
|
| 401 | - |
|
| 402 | - // Main email notification is sent as well as the password |
|
| 403 | - // to the recipient because shareApiLinkEnforcePassword is enabled. |
|
| 404 | - $this->mailer->expects($this->exactly(2))->method('send'); |
|
| 405 | - $instance->expects($this->never())->method('sendPasswordToOwner'); |
|
| 406 | - |
|
| 407 | - $this->assertSame($expectedShare, $instance->create($share)); |
|
| 408 | - $instance->sendMailNotification($share); |
|
| 409 | - } |
|
| 410 | - |
|
| 411 | - public function testCreateSendPasswordByMailWithPasswordAndWithEnforcedPasswordProtectionWithPermanentPassword(): void { |
|
| 412 | - $expectedShare = $this->createMock(IShare::class); |
|
| 413 | - |
|
| 414 | - $node = $this->createMock(File::class); |
|
| 415 | - $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 416 | - |
|
| 417 | - $share = $this->createMock(IShare::class); |
|
| 418 | - $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 419 | - $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(false); |
|
| 420 | - $share->expects($this->any())->method('getSharedBy')->willReturn('owner'); |
|
| 421 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 422 | - $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 423 | - $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 424 | - $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 425 | - |
|
| 426 | - $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 427 | - ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 428 | - ->willReturn('https://example.com/file.txt'); |
|
| 429 | - |
|
| 430 | - $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', 'createShareActivity', 'autoGeneratePassword', 'createPasswordSendActivity', 'sendPasswordToOwner']); |
|
| 431 | - |
|
| 432 | - $instance->expects($this->once())->method('getSharedWith')->willReturn([]); |
|
| 433 | - $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); |
|
| 434 | - $instance->expects($this->once())->method('createShareActivity')->with($share); |
|
| 435 | - $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn(['rawShare', 'password' => 'password']); |
|
| 436 | - $instance->expects($this->once())->method('createShareObject')->with(['rawShare', 'password' => 'password'])->willReturn($expectedShare); |
|
| 437 | - |
|
| 438 | - $share->expects($this->exactly(3))->method('getPassword')->willReturn('password'); |
|
| 439 | - $this->hasher->expects($this->once())->method('hash')->with('password')->willReturn('passwordHashed'); |
|
| 440 | - $share->expects($this->once())->method('setPassword')->with('passwordHashed'); |
|
| 441 | - |
|
| 442 | - // The given password (but not the autogenerated password) should be |
|
| 443 | - // mailed to the receiver of the share. |
|
| 444 | - $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(true); |
|
| 445 | - $this->config->expects($this->any())->method('getSystemValue')->with('sharing.enable_mail_link_password_expiration')->willReturn(false); |
|
| 446 | - $this->settingsManager->expects($this->any())->method('sendPasswordByMail')->willReturn(true); |
|
| 447 | - $instance->expects($this->never())->method('autoGeneratePassword'); |
|
| 448 | - |
|
| 449 | - $message = $this->createMock(IMessage::class); |
|
| 450 | - $message->expects($this->exactly(2))->method('setTo')->with(['[email protected]']); |
|
| 451 | - $this->mailer->expects($this->exactly(2))->method('createMessage')->willReturn($message); |
|
| 452 | - |
|
| 453 | - $calls = [ |
|
| 454 | - [ |
|
| 455 | - 'sharebymail.RecipientNotification', |
|
| 456 | - [ |
|
| 457 | - 'filename' => 'filename', |
|
| 458 | - 'link' => 'https://example.com/file.txt', |
|
| 459 | - 'initiator' => 'owner', |
|
| 460 | - 'expiration' => null, |
|
| 461 | - 'shareWith' => '[email protected]', |
|
| 462 | - 'note' => '', |
|
| 463 | - ], |
|
| 464 | - ], |
|
| 465 | - [ |
|
| 466 | - 'sharebymail.RecipientPasswordNotification', |
|
| 467 | - [ |
|
| 468 | - 'filename' => 'filename', |
|
| 469 | - 'password' => 'password', |
|
| 470 | - 'initiator' => 'owner', |
|
| 471 | - 'initiatorEmail' => null, |
|
| 472 | - 'shareWith' => '[email protected]', |
|
| 473 | - ], |
|
| 474 | - ], |
|
| 475 | - ]; |
|
| 476 | - $this->mailer->expects($this->exactly(2)) |
|
| 477 | - ->method('createEMailTemplate') |
|
| 478 | - ->willReturnCallback(function () use (&$calls) { |
|
| 479 | - $expected = array_shift($calls); |
|
| 480 | - $this->assertEquals($expected, func_get_args()); |
|
| 481 | - return $this->createMock(IEMailTemplate::class); |
|
| 482 | - }); |
|
| 483 | - |
|
| 484 | - // Main email notification is sent as well as the password |
|
| 485 | - // to the recipient because the password is set. |
|
| 486 | - $this->mailer->expects($this->exactly(2))->method('send'); |
|
| 487 | - $instance->expects($this->never())->method('sendPasswordToOwner'); |
|
| 488 | - |
|
| 489 | - $this->assertSame($expectedShare, $instance->create($share)); |
|
| 490 | - $instance->sendMailNotification($share); |
|
| 491 | - } |
|
| 492 | - |
|
| 493 | - public function testCreateSendPasswordByTalkWithEnforcedPasswordProtectionWithPermanentPassword(): void { |
|
| 494 | - $expectedShare = $this->createMock(IShare::class); |
|
| 495 | - |
|
| 496 | - // The owner of the share. |
|
| 497 | - $owner = $this->createMock(IUser::class); |
|
| 498 | - $this->userManager->expects($this->any())->method('get')->with('owner')->willReturn($owner); |
|
| 499 | - $owner->expects($this->any())->method('getEMailAddress')->willReturn('[email protected]'); |
|
| 500 | - $owner->expects($this->any())->method('getDisplayName')->willReturn('owner'); |
|
| 501 | - |
|
| 502 | - $node = $this->createMock(File::class); |
|
| 503 | - $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 504 | - |
|
| 505 | - $share = $this->createMock(IShare::class); |
|
| 506 | - $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 507 | - $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(true); |
|
| 508 | - $share->expects($this->any())->method('getSharedBy')->willReturn('owner'); |
|
| 509 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 510 | - $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 511 | - $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 512 | - $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 513 | - |
|
| 514 | - $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 515 | - ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 516 | - ->willReturn('https://example.com/file.txt'); |
|
| 517 | - |
|
| 518 | - $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', 'createShareActivity', 'autoGeneratePassword', 'createPasswordSendActivity']); |
|
| 519 | - |
|
| 520 | - $instance->expects($this->once())->method('getSharedWith')->willReturn([]); |
|
| 521 | - $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); |
|
| 522 | - $instance->expects($this->once())->method('createShareActivity')->with($share); |
|
| 523 | - $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn(['rawShare', 'password' => 'autogeneratedPassword']); |
|
| 524 | - $instance->expects($this->once())->method('createShareObject')->with(['rawShare', 'password' => 'autogeneratedPassword'])->willReturn($expectedShare); |
|
| 525 | - |
|
| 526 | - $share->expects($this->exactly(4))->method('getPassword')->willReturnOnConsecutiveCalls(null, 'autogeneratedPassword', 'autogeneratedPassword', 'autogeneratedPassword'); |
|
| 527 | - $this->hasher->expects($this->once())->method('hash')->with('autogeneratedPassword')->willReturn('autogeneratedPasswordHashed'); |
|
| 528 | - $share->expects($this->once())->method('setPassword')->with('autogeneratedPasswordHashed'); |
|
| 529 | - |
|
| 530 | - // The autogenerated password should be mailed to the owner of the share. |
|
| 531 | - $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(true); |
|
| 532 | - $this->config->expects($this->any())->method('getSystemValue')->with('sharing.enable_mail_link_password_expiration')->willReturn(false); |
|
| 533 | - $this->settingsManager->expects($this->any())->method('sendPasswordByMail')->willReturn(true); |
|
| 534 | - $instance->expects($this->once())->method('autoGeneratePassword')->with($share)->willReturn('autogeneratedPassword'); |
|
| 535 | - |
|
| 536 | - $message = $this->createMock(IMessage::class); |
|
| 537 | - $setToCalls = [ |
|
| 538 | - [['[email protected]']], |
|
| 539 | - [['[email protected]' => 'owner']], |
|
| 540 | - ]; |
|
| 541 | - $message->expects($this->exactly(2)) |
|
| 542 | - ->method('setTo') |
|
| 543 | - ->willReturnCallback(function () use (&$setToCalls, $message) { |
|
| 544 | - $expected = array_shift($setToCalls); |
|
| 545 | - $this->assertEquals($expected, func_get_args()); |
|
| 546 | - return $message; |
|
| 547 | - }); |
|
| 548 | - $this->mailer->expects($this->exactly(2))->method('createMessage')->willReturn($message); |
|
| 549 | - |
|
| 550 | - $calls = [ |
|
| 551 | - [ |
|
| 552 | - 'sharebymail.RecipientNotification', |
|
| 553 | - [ |
|
| 554 | - 'filename' => 'filename', |
|
| 555 | - 'link' => 'https://example.com/file.txt', |
|
| 556 | - 'initiator' => 'owner', |
|
| 557 | - 'expiration' => null, |
|
| 558 | - 'shareWith' => '[email protected]', |
|
| 559 | - 'note' => '', |
|
| 560 | - ], |
|
| 561 | - ], |
|
| 562 | - [ |
|
| 563 | - 'sharebymail.OwnerPasswordNotification', |
|
| 564 | - [ |
|
| 565 | - 'filename' => 'filename', |
|
| 566 | - 'password' => 'autogeneratedPassword', |
|
| 567 | - 'initiator' => 'owner', |
|
| 568 | - 'initiatorEmail' => '[email protected]', |
|
| 569 | - 'shareWith' => '[email protected]', |
|
| 570 | - ], |
|
| 571 | - ], |
|
| 572 | - ]; |
|
| 573 | - $this->mailer->expects($this->exactly(2)) |
|
| 574 | - ->method('createEMailTemplate') |
|
| 575 | - ->willReturnCallback(function () use (&$calls) { |
|
| 576 | - $expected = array_shift($calls); |
|
| 577 | - $this->assertEquals($expected, func_get_args()); |
|
| 578 | - return $this->createMock(IEMailTemplate::class); |
|
| 579 | - }); |
|
| 580 | - |
|
| 581 | - // Main email notification is sent as well as the password to owner |
|
| 582 | - // because the password is set and SendPasswordByTalk is enabled. |
|
| 583 | - $this->mailer->expects($this->exactly(2))->method('send'); |
|
| 584 | - |
|
| 585 | - $this->assertSame($expectedShare, $instance->create($share)); |
|
| 586 | - $instance->sendMailNotification($share); |
|
| 587 | - } |
|
| 588 | - |
|
| 589 | - // If attributes is set to multiple emails, use them as BCC |
|
| 590 | - public function sendNotificationToMultipleEmails() { |
|
| 591 | - $expectedShare = $this->createMock(IShare::class); |
|
| 592 | - |
|
| 593 | - $node = $this->createMock(File::class); |
|
| 594 | - $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 595 | - |
|
| 596 | - $share = $this->createMock(IShare::class); |
|
| 597 | - $share->expects($this->any())->method('getSharedWith')->willReturn(''); |
|
| 598 | - $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(false); |
|
| 599 | - $share->expects($this->any())->method('getSharedBy')->willReturn('owner'); |
|
| 600 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 601 | - $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 602 | - $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 603 | - $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 604 | - |
|
| 605 | - $attributes = $this->createMock(IAttributes::class); |
|
| 606 | - $share->expects($this->any())->method('getAttributes')->willReturn($attributes); |
|
| 607 | - $attributes->expects($this->any())->method('getAttribute')->with('shareWith', 'emails')->willReturn([ |
|
| 608 | - '[email protected]', |
|
| 609 | - '[email protected]', |
|
| 610 | - '[email protected]', |
|
| 611 | - ]); |
|
| 612 | - |
|
| 613 | - $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', 'createShareActivity', 'autoGeneratePassword', 'createPasswordSendActivity', 'sendEmail', 'sendPassword', 'sendPasswordToOwner']); |
|
| 614 | - |
|
| 615 | - $instance->expects($this->once())->method('getSharedWith')->willReturn([]); |
|
| 616 | - $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); |
|
| 617 | - $instance->expects($this->once())->method('createShareActivity')->with($share); |
|
| 618 | - $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn(['rawShare', 'password' => 'password']); |
|
| 619 | - $instance->expects($this->once())->method('createShareObject')->with(['rawShare', 'password' => 'password'])->willReturn($expectedShare); |
|
| 620 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 621 | - |
|
| 622 | - $share->expects($this->any())->method('getPassword')->willReturn('password'); |
|
| 623 | - $this->hasher->expects($this->once())->method('hash')->with('password')->willReturn('passwordHashed'); |
|
| 624 | - $share->expects($this->once())->method('setPassword')->with('passwordHashed'); |
|
| 625 | - |
|
| 626 | - // The given password (but not the autogenerated password) should not be |
|
| 627 | - // mailed to the receiver of the share because permanent passwords are not enforced. |
|
| 628 | - $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(false); |
|
| 629 | - $this->config->expects($this->once())->method('getSystemValue')->with('sharing.enable_mail_link_password_expiration')->willReturn(false); |
|
| 630 | - $instance->expects($this->never())->method('autoGeneratePassword'); |
|
| 631 | - |
|
| 632 | - // A password is set but no password sent via talk has been requested |
|
| 633 | - $instance->expects($this->once())->method('sendEmail') |
|
| 634 | - ->with($share, ['[email protected]', '[email protected]', '[email protected]']); |
|
| 635 | - $instance->expects($this->once())->method('sendPassword')->with($share, 'password'); |
|
| 636 | - $instance->expects($this->never())->method('sendPasswordToOwner'); |
|
| 637 | - |
|
| 638 | - |
|
| 639 | - $message = $this->createMock(IMessage::class); |
|
| 640 | - $message->expects($this->never())->method('setTo'); |
|
| 641 | - $message->expects($this->exactly(2))->method('setBcc')->with(['[email protected]', '[email protected]', '[email protected]']); |
|
| 642 | - $this->mailer->expects($this->exactly(2))->method('createMessage')->willReturn($message); |
|
| 643 | - |
|
| 644 | - // Main email notification is sent as well as the password |
|
| 645 | - // to recipients because the password is set. |
|
| 646 | - $this->mailer->expects($this->exactly(2))->method('send'); |
|
| 647 | - |
|
| 648 | - $this->assertSame($expectedShare, $instance->create($share)); |
|
| 649 | - $instance->sendMailNotification($share); |
|
| 650 | - } |
|
| 651 | - |
|
| 652 | - public function testCreateFailed(): void { |
|
| 653 | - $this->expectException(\Exception::class); |
|
| 654 | - |
|
| 655 | - $this->share->expects($this->once())->method('getSharedWith')->willReturn('user1'); |
|
| 656 | - $node = $this->createMock('OCP\Files\Node'); |
|
| 657 | - $node->expects($this->any())->method('getName')->willReturn('fileName'); |
|
| 658 | - $this->share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 659 | - |
|
| 660 | - $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject']); |
|
| 661 | - |
|
| 662 | - $instance->expects($this->once())->method('getSharedWith')->willReturn(['found']); |
|
| 663 | - $instance->expects($this->never())->method('createMailShare'); |
|
| 664 | - $instance->expects($this->never())->method('getRawShare'); |
|
| 665 | - $instance->expects($this->never())->method('createShareObject'); |
|
| 666 | - |
|
| 667 | - $this->assertSame('shareObject', |
|
| 668 | - $instance->create($this->share) |
|
| 669 | - ); |
|
| 670 | - } |
|
| 671 | - |
|
| 672 | - public function testCreateMailShare(): void { |
|
| 673 | - $this->share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 674 | - $this->share->expects($this->once())->method('setToken')->with('token'); |
|
| 675 | - $this->share->expects($this->any())->method('getSharedBy')->willReturn('[email protected]'); |
|
| 676 | - $this->share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 677 | - $this->share->expects($this->any())->method('getNote')->willReturn('Check this!'); |
|
| 678 | - $this->share->expects($this->any())->method('getMailSend')->willReturn(true); |
|
| 679 | - |
|
| 680 | - $node = $this->createMock('OCP\Files\Node'); |
|
| 681 | - $node->expects($this->any())->method('getName')->willReturn('fileName'); |
|
| 682 | - $this->share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 683 | - |
|
| 684 | - $instance = $this->getInstance(['generateToken', 'addShareToDB', 'sendMailNotification']); |
|
| 685 | - |
|
| 686 | - $instance->expects($this->once())->method('generateToken')->willReturn('token'); |
|
| 687 | - $instance->expects($this->once())->method('addShareToDB')->willReturn(42); |
|
| 688 | - |
|
| 689 | - // The manager handle the mail sending |
|
| 690 | - $instance->expects($this->never())->method('sendMailNotification'); |
|
| 691 | - |
|
| 692 | - $this->assertSame(42, |
|
| 693 | - $this->invokePrivate($instance, 'createMailShare', [$this->share]) |
|
| 694 | - ); |
|
| 695 | - } |
|
| 696 | - |
|
| 697 | - public function testGenerateToken(): void { |
|
| 698 | - $instance = $this->getInstance(); |
|
| 699 | - |
|
| 700 | - $this->secureRandom->expects($this->once())->method('generate')->willReturn('token'); |
|
| 701 | - |
|
| 702 | - $this->assertSame('token', |
|
| 703 | - $this->invokePrivate($instance, 'generateToken') |
|
| 704 | - ); |
|
| 705 | - } |
|
| 706 | - |
|
| 707 | - public function testAddShareToDB(): void { |
|
| 708 | - $itemSource = 11; |
|
| 709 | - $itemType = 'file'; |
|
| 710 | - $shareWith = '[email protected]'; |
|
| 711 | - $sharedBy = 'user1'; |
|
| 712 | - $uidOwner = 'user2'; |
|
| 713 | - $permissions = 1; |
|
| 714 | - $token = 'token'; |
|
| 715 | - $password = 'password'; |
|
| 716 | - $sendPasswordByTalk = true; |
|
| 717 | - $hideDownload = true; |
|
| 718 | - $label = 'label'; |
|
| 719 | - $expiration = new \DateTime(); |
|
| 720 | - $passwordExpirationTime = new \DateTime(); |
|
| 721 | - |
|
| 722 | - |
|
| 723 | - $instance = $this->getInstance(); |
|
| 724 | - $id = $this->invokePrivate( |
|
| 725 | - $instance, |
|
| 726 | - 'addShareToDB', |
|
| 727 | - [ |
|
| 728 | - $itemSource, |
|
| 729 | - $itemType, |
|
| 730 | - $shareWith, |
|
| 731 | - $sharedBy, |
|
| 732 | - $uidOwner, |
|
| 733 | - $permissions, |
|
| 734 | - $token, |
|
| 735 | - $password, |
|
| 736 | - $passwordExpirationTime, |
|
| 737 | - $sendPasswordByTalk, |
|
| 738 | - $hideDownload, |
|
| 739 | - $label, |
|
| 740 | - $expiration |
|
| 741 | - ] |
|
| 742 | - ); |
|
| 743 | - |
|
| 744 | - $qb = $this->connection->getQueryBuilder(); |
|
| 745 | - $qb->select('*') |
|
| 746 | - ->from('share') |
|
| 747 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
| 748 | - |
|
| 749 | - $qResult = $qb->execute(); |
|
| 750 | - $result = $qResult->fetchAll(); |
|
| 751 | - $qResult->closeCursor(); |
|
| 752 | - |
|
| 753 | - $this->assertSame(1, count($result)); |
|
| 754 | - |
|
| 755 | - $this->assertSame($itemSource, (int)$result[0]['item_source']); |
|
| 756 | - $this->assertSame($itemType, $result[0]['item_type']); |
|
| 757 | - $this->assertSame($shareWith, $result[0]['share_with']); |
|
| 758 | - $this->assertSame($sharedBy, $result[0]['uid_initiator']); |
|
| 759 | - $this->assertSame($uidOwner, $result[0]['uid_owner']); |
|
| 760 | - $this->assertSame($permissions, (int)$result[0]['permissions']); |
|
| 761 | - $this->assertSame($token, $result[0]['token']); |
|
| 762 | - $this->assertSame($password, $result[0]['password']); |
|
| 763 | - $this->assertSame($passwordExpirationTime->getTimestamp(), \DateTime::createFromFormat('Y-m-d H:i:s', $result[0]['password_expiration_time'])->getTimestamp()); |
|
| 764 | - $this->assertSame($sendPasswordByTalk, (bool)$result[0]['password_by_talk']); |
|
| 765 | - $this->assertSame($hideDownload, (bool)$result[0]['hide_download']); |
|
| 766 | - $this->assertSame($label, $result[0]['label']); |
|
| 767 | - $this->assertSame($expiration->getTimestamp(), \DateTime::createFromFormat('Y-m-d H:i:s', $result[0]['expiration'])->getTimestamp()); |
|
| 768 | - } |
|
| 769 | - |
|
| 770 | - public function testUpdate(): void { |
|
| 771 | - $itemSource = 11; |
|
| 772 | - $itemType = 'file'; |
|
| 773 | - $shareWith = '[email protected]'; |
|
| 774 | - $sharedBy = 'user1'; |
|
| 775 | - $uidOwner = 'user2'; |
|
| 776 | - $permissions = 1; |
|
| 777 | - $token = 'token'; |
|
| 778 | - $note = 'personal note'; |
|
| 779 | - |
|
| 780 | - |
|
| 781 | - $instance = $this->getInstance(); |
|
| 782 | - |
|
| 783 | - $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $note); |
|
| 784 | - |
|
| 785 | - $this->share->expects($this->once())->method('getPermissions')->willReturn($permissions + 1); |
|
| 786 | - $this->share->expects($this->once())->method('getShareOwner')->willReturn($uidOwner); |
|
| 787 | - $this->share->expects($this->once())->method('getSharedBy')->willReturn($sharedBy); |
|
| 788 | - $this->share->expects($this->any())->method('getNote')->willReturn($note); |
|
| 789 | - $this->share->expects($this->atLeastOnce())->method('getId')->willReturn($id); |
|
| 790 | - $this->share->expects($this->atLeastOnce())->method('getNodeId')->willReturn($itemSource); |
|
| 791 | - $this->share->expects($this->once())->method('getSharedWith')->willReturn($shareWith); |
|
| 792 | - |
|
| 793 | - $this->assertSame($this->share, |
|
| 794 | - $instance->update($this->share) |
|
| 795 | - ); |
|
| 796 | - |
|
| 797 | - $qb = $this->connection->getQueryBuilder(); |
|
| 798 | - $qb->select('*') |
|
| 799 | - ->from('share') |
|
| 800 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
| 801 | - |
|
| 802 | - $qResult = $qb->execute(); |
|
| 803 | - $result = $qResult->fetchAll(); |
|
| 804 | - $qResult->closeCursor(); |
|
| 805 | - |
|
| 806 | - $this->assertSame(1, count($result)); |
|
| 807 | - |
|
| 808 | - $this->assertSame($itemSource, (int)$result[0]['item_source']); |
|
| 809 | - $this->assertSame($itemType, $result[0]['item_type']); |
|
| 810 | - $this->assertSame($shareWith, $result[0]['share_with']); |
|
| 811 | - $this->assertSame($sharedBy, $result[0]['uid_initiator']); |
|
| 812 | - $this->assertSame($uidOwner, $result[0]['uid_owner']); |
|
| 813 | - $this->assertSame($permissions + 1, (int)$result[0]['permissions']); |
|
| 814 | - $this->assertSame($token, $result[0]['token']); |
|
| 815 | - $this->assertSame($note, $result[0]['note']); |
|
| 816 | - } |
|
| 817 | - |
|
| 818 | - public static function dataUpdateSendPassword(): array { |
|
| 819 | - return [ |
|
| 820 | - ['password', 'hashed', 'hashed new', false, false, true], |
|
| 821 | - ['', 'hashed', 'hashed new', false, false, false], |
|
| 822 | - [null, 'hashed', 'hashed new', false, false, false], |
|
| 823 | - ['password', 'hashed', 'hashed', false, false, false], |
|
| 824 | - ['password', 'hashed', 'hashed new', false, true, false], |
|
| 825 | - ['password', 'hashed', 'hashed new', true, false, true], |
|
| 826 | - ['password', 'hashed', 'hashed', true, false, true], |
|
| 827 | - ]; |
|
| 828 | - } |
|
| 829 | - |
|
| 830 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataUpdateSendPassword')] |
|
| 831 | - public function testUpdateSendPassword(?string $plainTextPassword, string $originalPassword, string $newPassword, bool $originalSendPasswordByTalk, bool $newSendPasswordByTalk, bool $sendMail): void { |
|
| 832 | - $node = $this->createMock(File::class); |
|
| 833 | - $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 834 | - |
|
| 835 | - $this->settingsManager->method('sendPasswordByMail')->willReturn(true); |
|
| 836 | - |
|
| 837 | - $originalShare = $this->createMock(IShare::class); |
|
| 838 | - $originalShare->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 839 | - $originalShare->expects($this->any())->method('getNode')->willReturn($node); |
|
| 840 | - $originalShare->expects($this->any())->method('getId')->willReturn(42); |
|
| 841 | - $originalShare->expects($this->any())->method('getPassword')->willReturn($originalPassword); |
|
| 842 | - $originalShare->expects($this->any())->method('getSendPasswordByTalk')->willReturn($originalSendPasswordByTalk); |
|
| 843 | - |
|
| 844 | - $share = $this->createMock(IShare::class); |
|
| 845 | - $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 846 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 847 | - $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 848 | - $share->expects($this->any())->method('getPassword')->willReturn($newPassword); |
|
| 849 | - $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn($newSendPasswordByTalk); |
|
| 850 | - |
|
| 851 | - if ($sendMail) { |
|
| 852 | - $this->mailer->expects($this->once())->method('createEMailTemplate')->with('sharebymail.RecipientPasswordNotification', [ |
|
| 853 | - 'filename' => 'filename', |
|
| 854 | - 'password' => $plainTextPassword, |
|
| 855 | - 'initiator' => null, |
|
| 856 | - 'initiatorEmail' => null, |
|
| 857 | - 'shareWith' => '[email protected]', |
|
| 858 | - ]); |
|
| 859 | - $this->mailer->expects($this->once())->method('send'); |
|
| 860 | - } else { |
|
| 861 | - $this->mailer->expects($this->never())->method('send'); |
|
| 862 | - } |
|
| 863 | - |
|
| 864 | - $instance = $this->getInstance(['getShareById', 'createPasswordSendActivity']); |
|
| 865 | - $instance->expects($this->once())->method('getShareById')->willReturn($originalShare); |
|
| 866 | - |
|
| 867 | - $this->assertSame($share, |
|
| 868 | - $instance->update($share, $plainTextPassword) |
|
| 869 | - ); |
|
| 870 | - } |
|
| 871 | - |
|
| 872 | - public function testDelete(): void { |
|
| 873 | - $instance = $this->getInstance(['removeShareFromTable', 'createShareActivity']); |
|
| 874 | - $this->share->expects($this->once())->method('getId')->willReturn(42); |
|
| 875 | - $instance->expects($this->once())->method('removeShareFromTable')->with(42); |
|
| 876 | - $instance->expects($this->once())->method('createShareActivity')->with($this->share, 'unshare'); |
|
| 877 | - $instance->delete($this->share); |
|
| 878 | - } |
|
| 879 | - |
|
| 880 | - public function testGetShareById(): void { |
|
| 881 | - $instance = $this->getInstance(['createShareObject']); |
|
| 882 | - |
|
| 883 | - $itemSource = 11; |
|
| 884 | - $itemType = 'file'; |
|
| 885 | - $shareWith = '[email protected]'; |
|
| 886 | - $sharedBy = 'user1'; |
|
| 887 | - $uidOwner = 'user2'; |
|
| 888 | - $permissions = 1; |
|
| 889 | - $token = 'token'; |
|
| 890 | - |
|
| 891 | - $this->createDummyShare($itemType, $itemSource, $shareWith, 'user1wrong', 'user2wrong', $permissions, $token); |
|
| 892 | - $id2 = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 893 | - |
|
| 894 | - $instance->expects($this->once())->method('createShareObject') |
|
| 895 | - ->willReturnCallback( |
|
| 896 | - function ($data) use ($uidOwner, $sharedBy, $id2) { |
|
| 897 | - $this->assertSame($uidOwner, $data['uid_owner']); |
|
| 898 | - $this->assertSame($sharedBy, $data['uid_initiator']); |
|
| 899 | - $this->assertSame($id2, (int)$data['id']); |
|
| 900 | - return $this->share; |
|
| 901 | - } |
|
| 902 | - ); |
|
| 903 | - |
|
| 904 | - $result = $instance->getShareById($id2); |
|
| 905 | - |
|
| 906 | - $this->assertInstanceOf('OCP\Share\IShare', $result); |
|
| 907 | - } |
|
| 908 | - |
|
| 909 | - |
|
| 910 | - public function testGetShareByIdFailed(): void { |
|
| 911 | - $this->expectException(ShareNotFound::class); |
|
| 912 | - |
|
| 913 | - $instance = $this->getInstance(['createShareObject']); |
|
| 914 | - |
|
| 915 | - $itemSource = 11; |
|
| 916 | - $itemType = 'file'; |
|
| 917 | - $shareWith = '[email protected]'; |
|
| 918 | - $sharedBy = 'user1'; |
|
| 919 | - $uidOwner = 'user2'; |
|
| 920 | - $permissions = 1; |
|
| 921 | - $token = 'token'; |
|
| 922 | - |
|
| 923 | - $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 924 | - |
|
| 925 | - $instance->getShareById($id + 1); |
|
| 926 | - } |
|
| 927 | - |
|
| 928 | - public function testGetShareByPath(): void { |
|
| 929 | - $itemSource = 11; |
|
| 930 | - $itemType = 'file'; |
|
| 931 | - $shareWith = '[email protected]'; |
|
| 932 | - $sharedBy = 'user1'; |
|
| 933 | - $uidOwner = 'user2'; |
|
| 934 | - $permissions = 1; |
|
| 935 | - $token = 'token'; |
|
| 936 | - |
|
| 937 | - $node = $this->createMock(Node::class); |
|
| 938 | - $node->expects($this->once())->method('getId')->willReturn($itemSource); |
|
| 939 | - |
|
| 940 | - |
|
| 941 | - $instance = $this->getInstance(['createShareObject']); |
|
| 942 | - |
|
| 943 | - $this->createDummyShare($itemType, 111, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 944 | - $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 945 | - |
|
| 946 | - $instance->expects($this->once())->method('createShareObject') |
|
| 947 | - ->willReturnCallback( |
|
| 948 | - function ($data) use ($uidOwner, $sharedBy, $id) { |
|
| 949 | - $this->assertSame($uidOwner, $data['uid_owner']); |
|
| 950 | - $this->assertSame($sharedBy, $data['uid_initiator']); |
|
| 951 | - $this->assertSame($id, (int)$data['id']); |
|
| 952 | - return $this->share; |
|
| 953 | - } |
|
| 954 | - ); |
|
| 955 | - |
|
| 956 | - $result = $instance->getSharesByPath($node); |
|
| 957 | - |
|
| 958 | - $this->assertTrue(is_array($result)); |
|
| 959 | - $this->assertSame(1, count($result)); |
|
| 960 | - $this->assertInstanceOf('OCP\Share\IShare', $result[0]); |
|
| 961 | - } |
|
| 962 | - |
|
| 963 | - public function testGetShareByToken(): void { |
|
| 964 | - $itemSource = 11; |
|
| 965 | - $itemType = 'file'; |
|
| 966 | - $shareWith = '[email protected]'; |
|
| 967 | - $sharedBy = 'user1'; |
|
| 968 | - $uidOwner = 'user2'; |
|
| 969 | - $permissions = 1; |
|
| 970 | - $token = 'token'; |
|
| 53 | + use EmailValidatorTrait; |
|
| 54 | + |
|
| 55 | + private IDBConnection $connection; |
|
| 56 | + |
|
| 57 | + private IL10N&MockObject $l; |
|
| 58 | + private IShare&MockObject $share; |
|
| 59 | + private IConfig&MockObject $config; |
|
| 60 | + private IMailer&MockObject $mailer; |
|
| 61 | + private IHasher&MockObject $hasher; |
|
| 62 | + private Defaults&MockObject $defaults; |
|
| 63 | + private IManager&MockObject $shareManager; |
|
| 64 | + private LoggerInterface&MockObject $logger; |
|
| 65 | + private IRootFolder&MockObject $rootFolder; |
|
| 66 | + private IUserManager&MockObject $userManager; |
|
| 67 | + private ISecureRandom&MockObject $secureRandom; |
|
| 68 | + private IURLGenerator&MockObject $urlGenerator; |
|
| 69 | + private SettingsManager&MockObject $settingsManager; |
|
| 70 | + private IActivityManager&MockObject $activityManager; |
|
| 71 | + private IEventDispatcher&MockObject $eventDispatcher; |
|
| 72 | + |
|
| 73 | + protected function setUp(): void { |
|
| 74 | + parent::setUp(); |
|
| 75 | + |
|
| 76 | + $this->connection = Server::get(IDBConnection::class); |
|
| 77 | + |
|
| 78 | + $this->l = $this->createMock(IL10N::class); |
|
| 79 | + $this->l->method('t') |
|
| 80 | + ->willReturnCallback(function ($text, $parameters = []) { |
|
| 81 | + return vsprintf($text, $parameters); |
|
| 82 | + }); |
|
| 83 | + $this->config = $this->createMock(IConfig::class); |
|
| 84 | + $this->logger = $this->createMock(LoggerInterface::class); |
|
| 85 | + $this->rootFolder = $this->createMock('OCP\Files\IRootFolder'); |
|
| 86 | + $this->userManager = $this->createMock(IUserManager::class); |
|
| 87 | + $this->secureRandom = $this->createMock('\OCP\Security\ISecureRandom'); |
|
| 88 | + $this->mailer = $this->createMock('\OCP\Mail\IMailer'); |
|
| 89 | + $this->urlGenerator = $this->createMock(IURLGenerator::class); |
|
| 90 | + $this->share = $this->createMock(IShare::class); |
|
| 91 | + $this->activityManager = $this->createMock('OCP\Activity\IManager'); |
|
| 92 | + $this->settingsManager = $this->createMock(SettingsManager::class); |
|
| 93 | + $this->defaults = $this->createMock(Defaults::class); |
|
| 94 | + $this->hasher = $this->createMock(IHasher::class); |
|
| 95 | + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); |
|
| 96 | + $this->shareManager = $this->createMock(IManager::class); |
|
| 97 | + |
|
| 98 | + $this->userManager->expects($this->any())->method('userExists')->willReturn(true); |
|
| 99 | + $this->config->expects($this->any())->method('getAppValue')->with('core', 'enforce_strict_email_check')->willReturn('yes'); |
|
| 100 | + } |
|
| 101 | + |
|
| 102 | + /** |
|
| 103 | + * get instance of Mocked ShareByMailProvider |
|
| 104 | + * |
|
| 105 | + * @param array $mockedMethods internal methods which should be mocked |
|
| 106 | + * @return \PHPUnit\Framework\MockObject\MockObject | ShareByMailProvider |
|
| 107 | + */ |
|
| 108 | + private function getInstance(array $mockedMethods = []) { |
|
| 109 | + if (!empty($mockedMethods)) { |
|
| 110 | + return $this->getMockBuilder(ShareByMailProvider::class) |
|
| 111 | + ->setConstructorArgs([ |
|
| 112 | + $this->config, |
|
| 113 | + $this->connection, |
|
| 114 | + $this->secureRandom, |
|
| 115 | + $this->userManager, |
|
| 116 | + $this->rootFolder, |
|
| 117 | + $this->l, |
|
| 118 | + $this->logger, |
|
| 119 | + $this->mailer, |
|
| 120 | + $this->urlGenerator, |
|
| 121 | + $this->activityManager, |
|
| 122 | + $this->settingsManager, |
|
| 123 | + $this->defaults, |
|
| 124 | + $this->hasher, |
|
| 125 | + $this->eventDispatcher, |
|
| 126 | + $this->shareManager, |
|
| 127 | + $this->getEmailValidatorWithStrictEmailCheck(), |
|
| 128 | + ]) |
|
| 129 | + ->onlyMethods($mockedMethods) |
|
| 130 | + ->getMock(); |
|
| 131 | + } |
|
| 132 | + |
|
| 133 | + return new ShareByMailProvider( |
|
| 134 | + $this->config, |
|
| 135 | + $this->connection, |
|
| 136 | + $this->secureRandom, |
|
| 137 | + $this->userManager, |
|
| 138 | + $this->rootFolder, |
|
| 139 | + $this->l, |
|
| 140 | + $this->logger, |
|
| 141 | + $this->mailer, |
|
| 142 | + $this->urlGenerator, |
|
| 143 | + $this->activityManager, |
|
| 144 | + $this->settingsManager, |
|
| 145 | + $this->defaults, |
|
| 146 | + $this->hasher, |
|
| 147 | + $this->eventDispatcher, |
|
| 148 | + $this->shareManager, |
|
| 149 | + $this->getEmailValidatorWithStrictEmailCheck(), |
|
| 150 | + ); |
|
| 151 | + } |
|
| 152 | + |
|
| 153 | + protected function tearDown(): void { |
|
| 154 | + $this->connection |
|
| 155 | + ->getQueryBuilder() |
|
| 156 | + ->delete('share') |
|
| 157 | + ->executeStatement(); |
|
| 158 | + |
|
| 159 | + parent::tearDown(); |
|
| 160 | + } |
|
| 161 | + |
|
| 162 | + public function testCreate(): void { |
|
| 163 | + $expectedShare = $this->createMock(IShare::class); |
|
| 164 | + |
|
| 165 | + $share = $this->createMock(IShare::class); |
|
| 166 | + $share->expects($this->any())->method('getSharedWith')->willReturn('user1'); |
|
| 167 | + |
|
| 168 | + $node = $this->createMock(File::class); |
|
| 169 | + $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 170 | + |
|
| 171 | + $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', 'createShareActivity', 'sendEmail', 'sendPassword']); |
|
| 172 | + |
|
| 173 | + $instance->expects($this->once())->method('getSharedWith')->willReturn([]); |
|
| 174 | + $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); |
|
| 175 | + $instance->expects($this->once())->method('createShareActivity')->with($share); |
|
| 176 | + $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn(['rawShare']); |
|
| 177 | + $instance->expects($this->once())->method('createShareObject')->with(['rawShare'])->willReturn($expectedShare); |
|
| 178 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 179 | + |
|
| 180 | + // As share api link password is not enforced, the password will not be generated. |
|
| 181 | + $this->shareManager->expects($this->once())->method('shareApiLinkEnforcePassword')->willReturn(false); |
|
| 182 | + $this->settingsManager->expects($this->never())->method('sendPasswordByMail'); |
|
| 183 | + |
|
| 184 | + // Mail notification is triggered by the share manager. |
|
| 185 | + $instance->expects($this->never())->method('sendEmail'); |
|
| 186 | + $instance->expects($this->never())->method('sendPassword'); |
|
| 187 | + |
|
| 188 | + $this->assertSame($expectedShare, $instance->create($share)); |
|
| 189 | + } |
|
| 190 | + |
|
| 191 | + public function testCreateSendPasswordByMailWithoutEnforcedPasswordProtection(): void { |
|
| 192 | + $expectedShare = $this->createMock(IShare::class); |
|
| 193 | + |
|
| 194 | + $node = $this->createMock(File::class); |
|
| 195 | + $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 196 | + |
|
| 197 | + $share = $this->createMock(IShare::class); |
|
| 198 | + $share->expects($this->any())->method('getSharedWith')->willReturn('receiver@examplelölöl.com'); |
|
| 199 | + $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(false); |
|
| 200 | + $share->expects($this->any())->method('getSharedBy')->willReturn('owner'); |
|
| 201 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 202 | + $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 203 | + $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 204 | + $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 205 | + |
|
| 206 | + $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', 'createShareActivity', 'autoGeneratePassword', 'createPasswordSendActivity', 'sendEmail', 'sendPassword', 'sendPasswordToOwner']); |
|
| 207 | + $instance->expects($this->once())->method('getSharedWith')->willReturn([]); |
|
| 208 | + $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); |
|
| 209 | + $instance->expects($this->once())->method('createShareActivity')->with($share); |
|
| 210 | + $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn(['rawShare']); |
|
| 211 | + $instance->expects($this->once())->method('createShareObject')->with(['rawShare'])->willReturn($expectedShare); |
|
| 212 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 213 | + |
|
| 214 | + // The autogenerated password should not be mailed. |
|
| 215 | + $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(false); |
|
| 216 | + $this->settingsManager->expects($this->any())->method('sendPasswordByMail')->willReturn(true); |
|
| 217 | + $instance->expects($this->never())->method('autoGeneratePassword'); |
|
| 218 | + |
|
| 219 | + // No password is set and no password sent via talk is requested |
|
| 220 | + $instance->expects($this->once())->method('sendEmail')->with($share, ['receiver@examplelölöl.com']); |
|
| 221 | + $instance->expects($this->never())->method('sendPassword'); |
|
| 222 | + $instance->expects($this->never())->method('sendPasswordToOwner'); |
|
| 223 | + |
|
| 224 | + // The manager sends the mail notification. |
|
| 225 | + // For the sake of testing simplicity, we will handle it ourselves. |
|
| 226 | + $this->assertSame($expectedShare, $instance->create($share)); |
|
| 227 | + $instance->sendMailNotification($share); |
|
| 228 | + } |
|
| 229 | + |
|
| 230 | + public function testCreateSendPasswordByMailWithPasswordAndWithoutEnforcedPasswordProtectionWithPermanentPassword(): void { |
|
| 231 | + $expectedShare = $this->createMock(IShare::class); |
|
| 232 | + |
|
| 233 | + $node = $this->createMock(File::class); |
|
| 234 | + $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 235 | + |
|
| 236 | + $share = $this->createMock(IShare::class); |
|
| 237 | + $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 238 | + $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(false); |
|
| 239 | + $share->expects($this->any())->method('getSharedBy')->willReturn('owner'); |
|
| 240 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 241 | + $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 242 | + $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 243 | + $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 244 | + |
|
| 245 | + $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', 'createShareActivity', 'autoGeneratePassword', 'createPasswordSendActivity', 'sendEmail', 'sendPassword', 'sendPasswordToOwner']); |
|
| 246 | + |
|
| 247 | + $instance->expects($this->once())->method('getSharedWith')->willReturn([]); |
|
| 248 | + $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); |
|
| 249 | + $instance->expects($this->once())->method('createShareActivity')->with($share); |
|
| 250 | + $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn(['rawShare', 'password' => 'password']); |
|
| 251 | + $instance->expects($this->once())->method('createShareObject')->with(['rawShare', 'password' => 'password'])->willReturn($expectedShare); |
|
| 252 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 253 | + |
|
| 254 | + $share->expects($this->any())->method('getPassword')->willReturn('password'); |
|
| 255 | + $this->hasher->expects($this->once())->method('hash')->with('password')->willReturn('passwordHashed'); |
|
| 256 | + $share->expects($this->once())->method('setPassword')->with('passwordHashed'); |
|
| 257 | + |
|
| 258 | + // The given password (but not the autogenerated password) should not be |
|
| 259 | + // mailed to the receiver of the share because permanent passwords are not enforced. |
|
| 260 | + $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(false); |
|
| 261 | + $this->config->expects($this->once())->method('getSystemValue')->with('sharing.enable_mail_link_password_expiration')->willReturn(false); |
|
| 262 | + $instance->expects($this->never())->method('autoGeneratePassword'); |
|
| 263 | + |
|
| 264 | + // A password is set but no password sent via talk has been requested |
|
| 265 | + $instance->expects($this->once())->method('sendEmail')->with($share, ['[email protected]']); |
|
| 266 | + $instance->expects($this->once())->method('sendPassword')->with($share, 'password'); |
|
| 267 | + $instance->expects($this->never())->method('sendPasswordToOwner'); |
|
| 268 | + |
|
| 269 | + $this->assertSame($expectedShare, $instance->create($share)); |
|
| 270 | + $instance->sendMailNotification($share); |
|
| 271 | + } |
|
| 272 | + |
|
| 273 | + public function testCreateSendPasswordByMailWithPasswordAndWithoutEnforcedPasswordProtectionWithoutPermanentPassword(): void { |
|
| 274 | + $expectedShare = $this->createMock(IShare::class); |
|
| 275 | + |
|
| 276 | + $node = $this->createMock(File::class); |
|
| 277 | + $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 278 | + |
|
| 279 | + $share = $this->createMock(IShare::class); |
|
| 280 | + $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 281 | + $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(false); |
|
| 282 | + $share->expects($this->any())->method('getSharedBy')->willReturn('owner'); |
|
| 283 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 284 | + $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 285 | + $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 286 | + $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 287 | + |
|
| 288 | + $instance = $this->getInstance([ |
|
| 289 | + 'getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', |
|
| 290 | + 'createShareActivity', 'autoGeneratePassword', 'createPasswordSendActivity', |
|
| 291 | + 'sendEmail', 'sendPassword', 'sendPasswordToOwner', |
|
| 292 | + ]); |
|
| 293 | + |
|
| 294 | + $instance->expects($this->once())->method('getSharedWith')->willReturn([]); |
|
| 295 | + $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); |
|
| 296 | + $instance->expects($this->once())->method('createShareActivity')->with($share); |
|
| 297 | + $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn(['rawShare', 'password' => 'password']); |
|
| 298 | + $instance->expects($this->once())->method('createShareObject')->with(['rawShare', 'password' => 'password'])->willReturn($expectedShare); |
|
| 299 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 300 | + |
|
| 301 | + $share->expects($this->any())->method('getPassword')->willReturn('password'); |
|
| 302 | + $this->hasher->expects($this->once())->method('hash')->with('password')->willReturn('passwordHashed'); |
|
| 303 | + $share->expects($this->once())->method('setPassword')->with('passwordHashed'); |
|
| 304 | + |
|
| 305 | + // No password is generated, so no emails need to be sent |
|
| 306 | + // aside from the main email notification. |
|
| 307 | + $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(false); |
|
| 308 | + $instance->expects($this->never())->method('autoGeneratePassword'); |
|
| 309 | + $this->config->expects($this->once())->method('getSystemValue') |
|
| 310 | + ->with('sharing.enable_mail_link_password_expiration') |
|
| 311 | + ->willReturn(true); |
|
| 312 | + |
|
| 313 | + // No password has been set and no password sent via talk has been requested, |
|
| 314 | + // but password has been enforced for the whole instance and will be generated. |
|
| 315 | + $instance->expects($this->once())->method('sendEmail')->with($share, ['[email protected]']); |
|
| 316 | + $instance->expects($this->never())->method('sendPassword'); |
|
| 317 | + $instance->expects($this->never())->method('sendPasswordToOwner'); |
|
| 318 | + |
|
| 319 | + $this->assertSame($expectedShare, $instance->create($share)); |
|
| 320 | + $instance->sendMailNotification($share); |
|
| 321 | + } |
|
| 322 | + |
|
| 323 | + public function testCreateSendPasswordByMailWithEnforcedPasswordProtectionWithPermanentPassword(): void { |
|
| 324 | + $expectedShare = $this->createMock(IShare::class); |
|
| 325 | + |
|
| 326 | + $node = $this->createMock(File::class); |
|
| 327 | + $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 328 | + |
|
| 329 | + $share = $this->createMock(IShare::class); |
|
| 330 | + $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 331 | + $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(false); |
|
| 332 | + $share->expects($this->any())->method('getSharedBy')->willReturn('owner'); |
|
| 333 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 334 | + $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 335 | + $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 336 | + $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 337 | + |
|
| 338 | + $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 339 | + ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 340 | + ->willReturn('https://example.com/file.txt'); |
|
| 341 | + |
|
| 342 | + $this->secureRandom->expects($this->once()) |
|
| 343 | + ->method('generate') |
|
| 344 | + ->with(8, ISecureRandom::CHAR_HUMAN_READABLE) |
|
| 345 | + ->willReturn('autogeneratedPassword'); |
|
| 346 | + $this->eventDispatcher->expects($this->once()) |
|
| 347 | + ->method('dispatchTyped') |
|
| 348 | + ->with(new GenerateSecurePasswordEvent(PasswordContext::SHARING)); |
|
| 349 | + |
|
| 350 | + $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', 'createShareActivity', 'createPasswordSendActivity', 'sendPasswordToOwner']); |
|
| 351 | + |
|
| 352 | + $instance->expects($this->once())->method('getSharedWith')->willReturn([]); |
|
| 353 | + $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); |
|
| 354 | + $instance->expects($this->once())->method('createShareActivity')->with($share); |
|
| 355 | + $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn(['rawShare', 'password' => 'autogeneratedPassword']); |
|
| 356 | + $instance->expects($this->once())->method('createShareObject')->with(['rawShare', 'password' => 'autogeneratedPassword'])->willReturn($expectedShare); |
|
| 357 | + |
|
| 358 | + // Initially not set, but will be set by the autoGeneratePassword method. |
|
| 359 | + $share->expects($this->exactly(3))->method('getPassword')->willReturnOnConsecutiveCalls(null, 'autogeneratedPassword', 'autogeneratedPassword'); |
|
| 360 | + $this->hasher->expects($this->once())->method('hash')->with('autogeneratedPassword')->willReturn('autogeneratedPasswordHashed'); |
|
| 361 | + $share->expects($this->once())->method('setPassword')->with('autogeneratedPasswordHashed'); |
|
| 362 | + |
|
| 363 | + // The autogenerated password should be mailed to the receiver of the share because permanent passwords are enforced. |
|
| 364 | + $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(true); |
|
| 365 | + $this->config->expects($this->any())->method('getSystemValue')->with('sharing.enable_mail_link_password_expiration')->willReturn(false); |
|
| 366 | + $this->settingsManager->expects($this->any())->method('sendPasswordByMail')->willReturn(true); |
|
| 367 | + |
|
| 368 | + $message = $this->createMock(IMessage::class); |
|
| 369 | + $message->expects($this->exactly(2))->method('setTo')->with(['[email protected]']); |
|
| 370 | + $this->mailer->expects($this->exactly(2))->method('createMessage')->willReturn($message); |
|
| 371 | + $calls = [ |
|
| 372 | + [ |
|
| 373 | + 'sharebymail.RecipientNotification', |
|
| 374 | + [ |
|
| 375 | + 'filename' => 'filename', |
|
| 376 | + 'link' => 'https://example.com/file.txt', |
|
| 377 | + 'initiator' => 'owner', |
|
| 378 | + 'expiration' => null, |
|
| 379 | + 'shareWith' => '[email protected]', |
|
| 380 | + 'note' => '', |
|
| 381 | + ], |
|
| 382 | + ], |
|
| 383 | + [ |
|
| 384 | + 'sharebymail.RecipientPasswordNotification', |
|
| 385 | + [ |
|
| 386 | + 'filename' => 'filename', |
|
| 387 | + 'password' => 'autogeneratedPassword', |
|
| 388 | + 'initiator' => 'owner', |
|
| 389 | + 'initiatorEmail' => null, |
|
| 390 | + 'shareWith' => '[email protected]', |
|
| 391 | + ], |
|
| 392 | + ], |
|
| 393 | + ]; |
|
| 394 | + $this->mailer->expects($this->exactly(2)) |
|
| 395 | + ->method('createEMailTemplate') |
|
| 396 | + ->willReturnCallback(function () use (&$calls) { |
|
| 397 | + $expected = array_shift($calls); |
|
| 398 | + $this->assertEquals($expected, func_get_args()); |
|
| 399 | + return $this->createMock(IEMailTemplate::class); |
|
| 400 | + }); |
|
| 401 | + |
|
| 402 | + // Main email notification is sent as well as the password |
|
| 403 | + // to the recipient because shareApiLinkEnforcePassword is enabled. |
|
| 404 | + $this->mailer->expects($this->exactly(2))->method('send'); |
|
| 405 | + $instance->expects($this->never())->method('sendPasswordToOwner'); |
|
| 406 | + |
|
| 407 | + $this->assertSame($expectedShare, $instance->create($share)); |
|
| 408 | + $instance->sendMailNotification($share); |
|
| 409 | + } |
|
| 410 | + |
|
| 411 | + public function testCreateSendPasswordByMailWithPasswordAndWithEnforcedPasswordProtectionWithPermanentPassword(): void { |
|
| 412 | + $expectedShare = $this->createMock(IShare::class); |
|
| 413 | + |
|
| 414 | + $node = $this->createMock(File::class); |
|
| 415 | + $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 416 | + |
|
| 417 | + $share = $this->createMock(IShare::class); |
|
| 418 | + $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 419 | + $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(false); |
|
| 420 | + $share->expects($this->any())->method('getSharedBy')->willReturn('owner'); |
|
| 421 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 422 | + $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 423 | + $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 424 | + $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 425 | + |
|
| 426 | + $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 427 | + ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 428 | + ->willReturn('https://example.com/file.txt'); |
|
| 429 | + |
|
| 430 | + $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', 'createShareActivity', 'autoGeneratePassword', 'createPasswordSendActivity', 'sendPasswordToOwner']); |
|
| 431 | + |
|
| 432 | + $instance->expects($this->once())->method('getSharedWith')->willReturn([]); |
|
| 433 | + $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); |
|
| 434 | + $instance->expects($this->once())->method('createShareActivity')->with($share); |
|
| 435 | + $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn(['rawShare', 'password' => 'password']); |
|
| 436 | + $instance->expects($this->once())->method('createShareObject')->with(['rawShare', 'password' => 'password'])->willReturn($expectedShare); |
|
| 437 | + |
|
| 438 | + $share->expects($this->exactly(3))->method('getPassword')->willReturn('password'); |
|
| 439 | + $this->hasher->expects($this->once())->method('hash')->with('password')->willReturn('passwordHashed'); |
|
| 440 | + $share->expects($this->once())->method('setPassword')->with('passwordHashed'); |
|
| 441 | + |
|
| 442 | + // The given password (but not the autogenerated password) should be |
|
| 443 | + // mailed to the receiver of the share. |
|
| 444 | + $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(true); |
|
| 445 | + $this->config->expects($this->any())->method('getSystemValue')->with('sharing.enable_mail_link_password_expiration')->willReturn(false); |
|
| 446 | + $this->settingsManager->expects($this->any())->method('sendPasswordByMail')->willReturn(true); |
|
| 447 | + $instance->expects($this->never())->method('autoGeneratePassword'); |
|
| 448 | + |
|
| 449 | + $message = $this->createMock(IMessage::class); |
|
| 450 | + $message->expects($this->exactly(2))->method('setTo')->with(['[email protected]']); |
|
| 451 | + $this->mailer->expects($this->exactly(2))->method('createMessage')->willReturn($message); |
|
| 452 | + |
|
| 453 | + $calls = [ |
|
| 454 | + [ |
|
| 455 | + 'sharebymail.RecipientNotification', |
|
| 456 | + [ |
|
| 457 | + 'filename' => 'filename', |
|
| 458 | + 'link' => 'https://example.com/file.txt', |
|
| 459 | + 'initiator' => 'owner', |
|
| 460 | + 'expiration' => null, |
|
| 461 | + 'shareWith' => '[email protected]', |
|
| 462 | + 'note' => '', |
|
| 463 | + ], |
|
| 464 | + ], |
|
| 465 | + [ |
|
| 466 | + 'sharebymail.RecipientPasswordNotification', |
|
| 467 | + [ |
|
| 468 | + 'filename' => 'filename', |
|
| 469 | + 'password' => 'password', |
|
| 470 | + 'initiator' => 'owner', |
|
| 471 | + 'initiatorEmail' => null, |
|
| 472 | + 'shareWith' => '[email protected]', |
|
| 473 | + ], |
|
| 474 | + ], |
|
| 475 | + ]; |
|
| 476 | + $this->mailer->expects($this->exactly(2)) |
|
| 477 | + ->method('createEMailTemplate') |
|
| 478 | + ->willReturnCallback(function () use (&$calls) { |
|
| 479 | + $expected = array_shift($calls); |
|
| 480 | + $this->assertEquals($expected, func_get_args()); |
|
| 481 | + return $this->createMock(IEMailTemplate::class); |
|
| 482 | + }); |
|
| 483 | + |
|
| 484 | + // Main email notification is sent as well as the password |
|
| 485 | + // to the recipient because the password is set. |
|
| 486 | + $this->mailer->expects($this->exactly(2))->method('send'); |
|
| 487 | + $instance->expects($this->never())->method('sendPasswordToOwner'); |
|
| 488 | + |
|
| 489 | + $this->assertSame($expectedShare, $instance->create($share)); |
|
| 490 | + $instance->sendMailNotification($share); |
|
| 491 | + } |
|
| 492 | + |
|
| 493 | + public function testCreateSendPasswordByTalkWithEnforcedPasswordProtectionWithPermanentPassword(): void { |
|
| 494 | + $expectedShare = $this->createMock(IShare::class); |
|
| 495 | + |
|
| 496 | + // The owner of the share. |
|
| 497 | + $owner = $this->createMock(IUser::class); |
|
| 498 | + $this->userManager->expects($this->any())->method('get')->with('owner')->willReturn($owner); |
|
| 499 | + $owner->expects($this->any())->method('getEMailAddress')->willReturn('[email protected]'); |
|
| 500 | + $owner->expects($this->any())->method('getDisplayName')->willReturn('owner'); |
|
| 501 | + |
|
| 502 | + $node = $this->createMock(File::class); |
|
| 503 | + $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 504 | + |
|
| 505 | + $share = $this->createMock(IShare::class); |
|
| 506 | + $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 507 | + $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(true); |
|
| 508 | + $share->expects($this->any())->method('getSharedBy')->willReturn('owner'); |
|
| 509 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 510 | + $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 511 | + $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 512 | + $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 513 | + |
|
| 514 | + $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 515 | + ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 516 | + ->willReturn('https://example.com/file.txt'); |
|
| 517 | + |
|
| 518 | + $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', 'createShareActivity', 'autoGeneratePassword', 'createPasswordSendActivity']); |
|
| 519 | + |
|
| 520 | + $instance->expects($this->once())->method('getSharedWith')->willReturn([]); |
|
| 521 | + $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); |
|
| 522 | + $instance->expects($this->once())->method('createShareActivity')->with($share); |
|
| 523 | + $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn(['rawShare', 'password' => 'autogeneratedPassword']); |
|
| 524 | + $instance->expects($this->once())->method('createShareObject')->with(['rawShare', 'password' => 'autogeneratedPassword'])->willReturn($expectedShare); |
|
| 525 | + |
|
| 526 | + $share->expects($this->exactly(4))->method('getPassword')->willReturnOnConsecutiveCalls(null, 'autogeneratedPassword', 'autogeneratedPassword', 'autogeneratedPassword'); |
|
| 527 | + $this->hasher->expects($this->once())->method('hash')->with('autogeneratedPassword')->willReturn('autogeneratedPasswordHashed'); |
|
| 528 | + $share->expects($this->once())->method('setPassword')->with('autogeneratedPasswordHashed'); |
|
| 529 | + |
|
| 530 | + // The autogenerated password should be mailed to the owner of the share. |
|
| 531 | + $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(true); |
|
| 532 | + $this->config->expects($this->any())->method('getSystemValue')->with('sharing.enable_mail_link_password_expiration')->willReturn(false); |
|
| 533 | + $this->settingsManager->expects($this->any())->method('sendPasswordByMail')->willReturn(true); |
|
| 534 | + $instance->expects($this->once())->method('autoGeneratePassword')->with($share)->willReturn('autogeneratedPassword'); |
|
| 535 | + |
|
| 536 | + $message = $this->createMock(IMessage::class); |
|
| 537 | + $setToCalls = [ |
|
| 538 | + [['[email protected]']], |
|
| 539 | + [['[email protected]' => 'owner']], |
|
| 540 | + ]; |
|
| 541 | + $message->expects($this->exactly(2)) |
|
| 542 | + ->method('setTo') |
|
| 543 | + ->willReturnCallback(function () use (&$setToCalls, $message) { |
|
| 544 | + $expected = array_shift($setToCalls); |
|
| 545 | + $this->assertEquals($expected, func_get_args()); |
|
| 546 | + return $message; |
|
| 547 | + }); |
|
| 548 | + $this->mailer->expects($this->exactly(2))->method('createMessage')->willReturn($message); |
|
| 549 | + |
|
| 550 | + $calls = [ |
|
| 551 | + [ |
|
| 552 | + 'sharebymail.RecipientNotification', |
|
| 553 | + [ |
|
| 554 | + 'filename' => 'filename', |
|
| 555 | + 'link' => 'https://example.com/file.txt', |
|
| 556 | + 'initiator' => 'owner', |
|
| 557 | + 'expiration' => null, |
|
| 558 | + 'shareWith' => '[email protected]', |
|
| 559 | + 'note' => '', |
|
| 560 | + ], |
|
| 561 | + ], |
|
| 562 | + [ |
|
| 563 | + 'sharebymail.OwnerPasswordNotification', |
|
| 564 | + [ |
|
| 565 | + 'filename' => 'filename', |
|
| 566 | + 'password' => 'autogeneratedPassword', |
|
| 567 | + 'initiator' => 'owner', |
|
| 568 | + 'initiatorEmail' => '[email protected]', |
|
| 569 | + 'shareWith' => '[email protected]', |
|
| 570 | + ], |
|
| 571 | + ], |
|
| 572 | + ]; |
|
| 573 | + $this->mailer->expects($this->exactly(2)) |
|
| 574 | + ->method('createEMailTemplate') |
|
| 575 | + ->willReturnCallback(function () use (&$calls) { |
|
| 576 | + $expected = array_shift($calls); |
|
| 577 | + $this->assertEquals($expected, func_get_args()); |
|
| 578 | + return $this->createMock(IEMailTemplate::class); |
|
| 579 | + }); |
|
| 580 | + |
|
| 581 | + // Main email notification is sent as well as the password to owner |
|
| 582 | + // because the password is set and SendPasswordByTalk is enabled. |
|
| 583 | + $this->mailer->expects($this->exactly(2))->method('send'); |
|
| 584 | + |
|
| 585 | + $this->assertSame($expectedShare, $instance->create($share)); |
|
| 586 | + $instance->sendMailNotification($share); |
|
| 587 | + } |
|
| 588 | + |
|
| 589 | + // If attributes is set to multiple emails, use them as BCC |
|
| 590 | + public function sendNotificationToMultipleEmails() { |
|
| 591 | + $expectedShare = $this->createMock(IShare::class); |
|
| 592 | + |
|
| 593 | + $node = $this->createMock(File::class); |
|
| 594 | + $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 595 | + |
|
| 596 | + $share = $this->createMock(IShare::class); |
|
| 597 | + $share->expects($this->any())->method('getSharedWith')->willReturn(''); |
|
| 598 | + $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(false); |
|
| 599 | + $share->expects($this->any())->method('getSharedBy')->willReturn('owner'); |
|
| 600 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 601 | + $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 602 | + $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 603 | + $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 604 | + |
|
| 605 | + $attributes = $this->createMock(IAttributes::class); |
|
| 606 | + $share->expects($this->any())->method('getAttributes')->willReturn($attributes); |
|
| 607 | + $attributes->expects($this->any())->method('getAttribute')->with('shareWith', 'emails')->willReturn([ |
|
| 608 | + '[email protected]', |
|
| 609 | + '[email protected]', |
|
| 610 | + '[email protected]', |
|
| 611 | + ]); |
|
| 612 | + |
|
| 613 | + $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', 'createShareActivity', 'autoGeneratePassword', 'createPasswordSendActivity', 'sendEmail', 'sendPassword', 'sendPasswordToOwner']); |
|
| 614 | + |
|
| 615 | + $instance->expects($this->once())->method('getSharedWith')->willReturn([]); |
|
| 616 | + $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); |
|
| 617 | + $instance->expects($this->once())->method('createShareActivity')->with($share); |
|
| 618 | + $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn(['rawShare', 'password' => 'password']); |
|
| 619 | + $instance->expects($this->once())->method('createShareObject')->with(['rawShare', 'password' => 'password'])->willReturn($expectedShare); |
|
| 620 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 621 | + |
|
| 622 | + $share->expects($this->any())->method('getPassword')->willReturn('password'); |
|
| 623 | + $this->hasher->expects($this->once())->method('hash')->with('password')->willReturn('passwordHashed'); |
|
| 624 | + $share->expects($this->once())->method('setPassword')->with('passwordHashed'); |
|
| 625 | + |
|
| 626 | + // The given password (but not the autogenerated password) should not be |
|
| 627 | + // mailed to the receiver of the share because permanent passwords are not enforced. |
|
| 628 | + $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(false); |
|
| 629 | + $this->config->expects($this->once())->method('getSystemValue')->with('sharing.enable_mail_link_password_expiration')->willReturn(false); |
|
| 630 | + $instance->expects($this->never())->method('autoGeneratePassword'); |
|
| 631 | + |
|
| 632 | + // A password is set but no password sent via talk has been requested |
|
| 633 | + $instance->expects($this->once())->method('sendEmail') |
|
| 634 | + ->with($share, ['[email protected]', '[email protected]', '[email protected]']); |
|
| 635 | + $instance->expects($this->once())->method('sendPassword')->with($share, 'password'); |
|
| 636 | + $instance->expects($this->never())->method('sendPasswordToOwner'); |
|
| 637 | + |
|
| 638 | + |
|
| 639 | + $message = $this->createMock(IMessage::class); |
|
| 640 | + $message->expects($this->never())->method('setTo'); |
|
| 641 | + $message->expects($this->exactly(2))->method('setBcc')->with(['[email protected]', '[email protected]', '[email protected]']); |
|
| 642 | + $this->mailer->expects($this->exactly(2))->method('createMessage')->willReturn($message); |
|
| 643 | + |
|
| 644 | + // Main email notification is sent as well as the password |
|
| 645 | + // to recipients because the password is set. |
|
| 646 | + $this->mailer->expects($this->exactly(2))->method('send'); |
|
| 647 | + |
|
| 648 | + $this->assertSame($expectedShare, $instance->create($share)); |
|
| 649 | + $instance->sendMailNotification($share); |
|
| 650 | + } |
|
| 651 | + |
|
| 652 | + public function testCreateFailed(): void { |
|
| 653 | + $this->expectException(\Exception::class); |
|
| 654 | + |
|
| 655 | + $this->share->expects($this->once())->method('getSharedWith')->willReturn('user1'); |
|
| 656 | + $node = $this->createMock('OCP\Files\Node'); |
|
| 657 | + $node->expects($this->any())->method('getName')->willReturn('fileName'); |
|
| 658 | + $this->share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 659 | + |
|
| 660 | + $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject']); |
|
| 661 | + |
|
| 662 | + $instance->expects($this->once())->method('getSharedWith')->willReturn(['found']); |
|
| 663 | + $instance->expects($this->never())->method('createMailShare'); |
|
| 664 | + $instance->expects($this->never())->method('getRawShare'); |
|
| 665 | + $instance->expects($this->never())->method('createShareObject'); |
|
| 666 | + |
|
| 667 | + $this->assertSame('shareObject', |
|
| 668 | + $instance->create($this->share) |
|
| 669 | + ); |
|
| 670 | + } |
|
| 671 | + |
|
| 672 | + public function testCreateMailShare(): void { |
|
| 673 | + $this->share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 674 | + $this->share->expects($this->once())->method('setToken')->with('token'); |
|
| 675 | + $this->share->expects($this->any())->method('getSharedBy')->willReturn('[email protected]'); |
|
| 676 | + $this->share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 677 | + $this->share->expects($this->any())->method('getNote')->willReturn('Check this!'); |
|
| 678 | + $this->share->expects($this->any())->method('getMailSend')->willReturn(true); |
|
| 679 | + |
|
| 680 | + $node = $this->createMock('OCP\Files\Node'); |
|
| 681 | + $node->expects($this->any())->method('getName')->willReturn('fileName'); |
|
| 682 | + $this->share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 683 | + |
|
| 684 | + $instance = $this->getInstance(['generateToken', 'addShareToDB', 'sendMailNotification']); |
|
| 685 | + |
|
| 686 | + $instance->expects($this->once())->method('generateToken')->willReturn('token'); |
|
| 687 | + $instance->expects($this->once())->method('addShareToDB')->willReturn(42); |
|
| 688 | + |
|
| 689 | + // The manager handle the mail sending |
|
| 690 | + $instance->expects($this->never())->method('sendMailNotification'); |
|
| 691 | + |
|
| 692 | + $this->assertSame(42, |
|
| 693 | + $this->invokePrivate($instance, 'createMailShare', [$this->share]) |
|
| 694 | + ); |
|
| 695 | + } |
|
| 696 | + |
|
| 697 | + public function testGenerateToken(): void { |
|
| 698 | + $instance = $this->getInstance(); |
|
| 699 | + |
|
| 700 | + $this->secureRandom->expects($this->once())->method('generate')->willReturn('token'); |
|
| 701 | + |
|
| 702 | + $this->assertSame('token', |
|
| 703 | + $this->invokePrivate($instance, 'generateToken') |
|
| 704 | + ); |
|
| 705 | + } |
|
| 706 | + |
|
| 707 | + public function testAddShareToDB(): void { |
|
| 708 | + $itemSource = 11; |
|
| 709 | + $itemType = 'file'; |
|
| 710 | + $shareWith = '[email protected]'; |
|
| 711 | + $sharedBy = 'user1'; |
|
| 712 | + $uidOwner = 'user2'; |
|
| 713 | + $permissions = 1; |
|
| 714 | + $token = 'token'; |
|
| 715 | + $password = 'password'; |
|
| 716 | + $sendPasswordByTalk = true; |
|
| 717 | + $hideDownload = true; |
|
| 718 | + $label = 'label'; |
|
| 719 | + $expiration = new \DateTime(); |
|
| 720 | + $passwordExpirationTime = new \DateTime(); |
|
| 721 | + |
|
| 722 | + |
|
| 723 | + $instance = $this->getInstance(); |
|
| 724 | + $id = $this->invokePrivate( |
|
| 725 | + $instance, |
|
| 726 | + 'addShareToDB', |
|
| 727 | + [ |
|
| 728 | + $itemSource, |
|
| 729 | + $itemType, |
|
| 730 | + $shareWith, |
|
| 731 | + $sharedBy, |
|
| 732 | + $uidOwner, |
|
| 733 | + $permissions, |
|
| 734 | + $token, |
|
| 735 | + $password, |
|
| 736 | + $passwordExpirationTime, |
|
| 737 | + $sendPasswordByTalk, |
|
| 738 | + $hideDownload, |
|
| 739 | + $label, |
|
| 740 | + $expiration |
|
| 741 | + ] |
|
| 742 | + ); |
|
| 743 | + |
|
| 744 | + $qb = $this->connection->getQueryBuilder(); |
|
| 745 | + $qb->select('*') |
|
| 746 | + ->from('share') |
|
| 747 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
| 748 | + |
|
| 749 | + $qResult = $qb->execute(); |
|
| 750 | + $result = $qResult->fetchAll(); |
|
| 751 | + $qResult->closeCursor(); |
|
| 752 | + |
|
| 753 | + $this->assertSame(1, count($result)); |
|
| 754 | + |
|
| 755 | + $this->assertSame($itemSource, (int)$result[0]['item_source']); |
|
| 756 | + $this->assertSame($itemType, $result[0]['item_type']); |
|
| 757 | + $this->assertSame($shareWith, $result[0]['share_with']); |
|
| 758 | + $this->assertSame($sharedBy, $result[0]['uid_initiator']); |
|
| 759 | + $this->assertSame($uidOwner, $result[0]['uid_owner']); |
|
| 760 | + $this->assertSame($permissions, (int)$result[0]['permissions']); |
|
| 761 | + $this->assertSame($token, $result[0]['token']); |
|
| 762 | + $this->assertSame($password, $result[0]['password']); |
|
| 763 | + $this->assertSame($passwordExpirationTime->getTimestamp(), \DateTime::createFromFormat('Y-m-d H:i:s', $result[0]['password_expiration_time'])->getTimestamp()); |
|
| 764 | + $this->assertSame($sendPasswordByTalk, (bool)$result[0]['password_by_talk']); |
|
| 765 | + $this->assertSame($hideDownload, (bool)$result[0]['hide_download']); |
|
| 766 | + $this->assertSame($label, $result[0]['label']); |
|
| 767 | + $this->assertSame($expiration->getTimestamp(), \DateTime::createFromFormat('Y-m-d H:i:s', $result[0]['expiration'])->getTimestamp()); |
|
| 768 | + } |
|
| 769 | + |
|
| 770 | + public function testUpdate(): void { |
|
| 771 | + $itemSource = 11; |
|
| 772 | + $itemType = 'file'; |
|
| 773 | + $shareWith = '[email protected]'; |
|
| 774 | + $sharedBy = 'user1'; |
|
| 775 | + $uidOwner = 'user2'; |
|
| 776 | + $permissions = 1; |
|
| 777 | + $token = 'token'; |
|
| 778 | + $note = 'personal note'; |
|
| 779 | + |
|
| 780 | + |
|
| 781 | + $instance = $this->getInstance(); |
|
| 782 | + |
|
| 783 | + $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $note); |
|
| 784 | + |
|
| 785 | + $this->share->expects($this->once())->method('getPermissions')->willReturn($permissions + 1); |
|
| 786 | + $this->share->expects($this->once())->method('getShareOwner')->willReturn($uidOwner); |
|
| 787 | + $this->share->expects($this->once())->method('getSharedBy')->willReturn($sharedBy); |
|
| 788 | + $this->share->expects($this->any())->method('getNote')->willReturn($note); |
|
| 789 | + $this->share->expects($this->atLeastOnce())->method('getId')->willReturn($id); |
|
| 790 | + $this->share->expects($this->atLeastOnce())->method('getNodeId')->willReturn($itemSource); |
|
| 791 | + $this->share->expects($this->once())->method('getSharedWith')->willReturn($shareWith); |
|
| 792 | + |
|
| 793 | + $this->assertSame($this->share, |
|
| 794 | + $instance->update($this->share) |
|
| 795 | + ); |
|
| 796 | + |
|
| 797 | + $qb = $this->connection->getQueryBuilder(); |
|
| 798 | + $qb->select('*') |
|
| 799 | + ->from('share') |
|
| 800 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
| 801 | + |
|
| 802 | + $qResult = $qb->execute(); |
|
| 803 | + $result = $qResult->fetchAll(); |
|
| 804 | + $qResult->closeCursor(); |
|
| 805 | + |
|
| 806 | + $this->assertSame(1, count($result)); |
|
| 807 | + |
|
| 808 | + $this->assertSame($itemSource, (int)$result[0]['item_source']); |
|
| 809 | + $this->assertSame($itemType, $result[0]['item_type']); |
|
| 810 | + $this->assertSame($shareWith, $result[0]['share_with']); |
|
| 811 | + $this->assertSame($sharedBy, $result[0]['uid_initiator']); |
|
| 812 | + $this->assertSame($uidOwner, $result[0]['uid_owner']); |
|
| 813 | + $this->assertSame($permissions + 1, (int)$result[0]['permissions']); |
|
| 814 | + $this->assertSame($token, $result[0]['token']); |
|
| 815 | + $this->assertSame($note, $result[0]['note']); |
|
| 816 | + } |
|
| 817 | + |
|
| 818 | + public static function dataUpdateSendPassword(): array { |
|
| 819 | + return [ |
|
| 820 | + ['password', 'hashed', 'hashed new', false, false, true], |
|
| 821 | + ['', 'hashed', 'hashed new', false, false, false], |
|
| 822 | + [null, 'hashed', 'hashed new', false, false, false], |
|
| 823 | + ['password', 'hashed', 'hashed', false, false, false], |
|
| 824 | + ['password', 'hashed', 'hashed new', false, true, false], |
|
| 825 | + ['password', 'hashed', 'hashed new', true, false, true], |
|
| 826 | + ['password', 'hashed', 'hashed', true, false, true], |
|
| 827 | + ]; |
|
| 828 | + } |
|
| 829 | + |
|
| 830 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataUpdateSendPassword')] |
|
| 831 | + public function testUpdateSendPassword(?string $plainTextPassword, string $originalPassword, string $newPassword, bool $originalSendPasswordByTalk, bool $newSendPasswordByTalk, bool $sendMail): void { |
|
| 832 | + $node = $this->createMock(File::class); |
|
| 833 | + $node->expects($this->any())->method('getName')->willReturn('filename'); |
|
| 834 | + |
|
| 835 | + $this->settingsManager->method('sendPasswordByMail')->willReturn(true); |
|
| 836 | + |
|
| 837 | + $originalShare = $this->createMock(IShare::class); |
|
| 838 | + $originalShare->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 839 | + $originalShare->expects($this->any())->method('getNode')->willReturn($node); |
|
| 840 | + $originalShare->expects($this->any())->method('getId')->willReturn(42); |
|
| 841 | + $originalShare->expects($this->any())->method('getPassword')->willReturn($originalPassword); |
|
| 842 | + $originalShare->expects($this->any())->method('getSendPasswordByTalk')->willReturn($originalSendPasswordByTalk); |
|
| 843 | + |
|
| 844 | + $share = $this->createMock(IShare::class); |
|
| 845 | + $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 846 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 847 | + $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 848 | + $share->expects($this->any())->method('getPassword')->willReturn($newPassword); |
|
| 849 | + $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn($newSendPasswordByTalk); |
|
| 850 | + |
|
| 851 | + if ($sendMail) { |
|
| 852 | + $this->mailer->expects($this->once())->method('createEMailTemplate')->with('sharebymail.RecipientPasswordNotification', [ |
|
| 853 | + 'filename' => 'filename', |
|
| 854 | + 'password' => $plainTextPassword, |
|
| 855 | + 'initiator' => null, |
|
| 856 | + 'initiatorEmail' => null, |
|
| 857 | + 'shareWith' => '[email protected]', |
|
| 858 | + ]); |
|
| 859 | + $this->mailer->expects($this->once())->method('send'); |
|
| 860 | + } else { |
|
| 861 | + $this->mailer->expects($this->never())->method('send'); |
|
| 862 | + } |
|
| 863 | + |
|
| 864 | + $instance = $this->getInstance(['getShareById', 'createPasswordSendActivity']); |
|
| 865 | + $instance->expects($this->once())->method('getShareById')->willReturn($originalShare); |
|
| 866 | + |
|
| 867 | + $this->assertSame($share, |
|
| 868 | + $instance->update($share, $plainTextPassword) |
|
| 869 | + ); |
|
| 870 | + } |
|
| 871 | + |
|
| 872 | + public function testDelete(): void { |
|
| 873 | + $instance = $this->getInstance(['removeShareFromTable', 'createShareActivity']); |
|
| 874 | + $this->share->expects($this->once())->method('getId')->willReturn(42); |
|
| 875 | + $instance->expects($this->once())->method('removeShareFromTable')->with(42); |
|
| 876 | + $instance->expects($this->once())->method('createShareActivity')->with($this->share, 'unshare'); |
|
| 877 | + $instance->delete($this->share); |
|
| 878 | + } |
|
| 879 | + |
|
| 880 | + public function testGetShareById(): void { |
|
| 881 | + $instance = $this->getInstance(['createShareObject']); |
|
| 882 | + |
|
| 883 | + $itemSource = 11; |
|
| 884 | + $itemType = 'file'; |
|
| 885 | + $shareWith = '[email protected]'; |
|
| 886 | + $sharedBy = 'user1'; |
|
| 887 | + $uidOwner = 'user2'; |
|
| 888 | + $permissions = 1; |
|
| 889 | + $token = 'token'; |
|
| 890 | + |
|
| 891 | + $this->createDummyShare($itemType, $itemSource, $shareWith, 'user1wrong', 'user2wrong', $permissions, $token); |
|
| 892 | + $id2 = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 893 | + |
|
| 894 | + $instance->expects($this->once())->method('createShareObject') |
|
| 895 | + ->willReturnCallback( |
|
| 896 | + function ($data) use ($uidOwner, $sharedBy, $id2) { |
|
| 897 | + $this->assertSame($uidOwner, $data['uid_owner']); |
|
| 898 | + $this->assertSame($sharedBy, $data['uid_initiator']); |
|
| 899 | + $this->assertSame($id2, (int)$data['id']); |
|
| 900 | + return $this->share; |
|
| 901 | + } |
|
| 902 | + ); |
|
| 903 | + |
|
| 904 | + $result = $instance->getShareById($id2); |
|
| 905 | + |
|
| 906 | + $this->assertInstanceOf('OCP\Share\IShare', $result); |
|
| 907 | + } |
|
| 908 | + |
|
| 909 | + |
|
| 910 | + public function testGetShareByIdFailed(): void { |
|
| 911 | + $this->expectException(ShareNotFound::class); |
|
| 912 | + |
|
| 913 | + $instance = $this->getInstance(['createShareObject']); |
|
| 914 | + |
|
| 915 | + $itemSource = 11; |
|
| 916 | + $itemType = 'file'; |
|
| 917 | + $shareWith = '[email protected]'; |
|
| 918 | + $sharedBy = 'user1'; |
|
| 919 | + $uidOwner = 'user2'; |
|
| 920 | + $permissions = 1; |
|
| 921 | + $token = 'token'; |
|
| 922 | + |
|
| 923 | + $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 924 | + |
|
| 925 | + $instance->getShareById($id + 1); |
|
| 926 | + } |
|
| 927 | + |
|
| 928 | + public function testGetShareByPath(): void { |
|
| 929 | + $itemSource = 11; |
|
| 930 | + $itemType = 'file'; |
|
| 931 | + $shareWith = '[email protected]'; |
|
| 932 | + $sharedBy = 'user1'; |
|
| 933 | + $uidOwner = 'user2'; |
|
| 934 | + $permissions = 1; |
|
| 935 | + $token = 'token'; |
|
| 936 | + |
|
| 937 | + $node = $this->createMock(Node::class); |
|
| 938 | + $node->expects($this->once())->method('getId')->willReturn($itemSource); |
|
| 939 | + |
|
| 940 | + |
|
| 941 | + $instance = $this->getInstance(['createShareObject']); |
|
| 942 | + |
|
| 943 | + $this->createDummyShare($itemType, 111, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 944 | + $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 945 | + |
|
| 946 | + $instance->expects($this->once())->method('createShareObject') |
|
| 947 | + ->willReturnCallback( |
|
| 948 | + function ($data) use ($uidOwner, $sharedBy, $id) { |
|
| 949 | + $this->assertSame($uidOwner, $data['uid_owner']); |
|
| 950 | + $this->assertSame($sharedBy, $data['uid_initiator']); |
|
| 951 | + $this->assertSame($id, (int)$data['id']); |
|
| 952 | + return $this->share; |
|
| 953 | + } |
|
| 954 | + ); |
|
| 955 | + |
|
| 956 | + $result = $instance->getSharesByPath($node); |
|
| 957 | + |
|
| 958 | + $this->assertTrue(is_array($result)); |
|
| 959 | + $this->assertSame(1, count($result)); |
|
| 960 | + $this->assertInstanceOf('OCP\Share\IShare', $result[0]); |
|
| 961 | + } |
|
| 962 | + |
|
| 963 | + public function testGetShareByToken(): void { |
|
| 964 | + $itemSource = 11; |
|
| 965 | + $itemType = 'file'; |
|
| 966 | + $shareWith = '[email protected]'; |
|
| 967 | + $sharedBy = 'user1'; |
|
| 968 | + $uidOwner = 'user2'; |
|
| 969 | + $permissions = 1; |
|
| 970 | + $token = 'token'; |
|
| 971 | 971 | |
| 972 | - $instance = $this->getInstance(['createShareObject']); |
|
| 972 | + $instance = $this->getInstance(['createShareObject']); |
|
| 973 | 973 | |
| 974 | - $idMail = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 975 | - $idPublic = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token, '', IShare::TYPE_LINK); |
|
| 974 | + $idMail = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 975 | + $idPublic = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token, '', IShare::TYPE_LINK); |
|
| 976 | 976 | |
| 977 | - $this->assertTrue($idMail !== $idPublic); |
|
| 977 | + $this->assertTrue($idMail !== $idPublic); |
|
| 978 | 978 | |
| 979 | - $instance->expects($this->once())->method('createShareObject') |
|
| 980 | - ->willReturnCallback( |
|
| 981 | - function ($data) use ($idMail) { |
|
| 982 | - $this->assertSame($idMail, (int)$data['id']); |
|
| 983 | - return $this->share; |
|
| 984 | - } |
|
| 985 | - ); |
|
| 979 | + $instance->expects($this->once())->method('createShareObject') |
|
| 980 | + ->willReturnCallback( |
|
| 981 | + function ($data) use ($idMail) { |
|
| 982 | + $this->assertSame($idMail, (int)$data['id']); |
|
| 983 | + return $this->share; |
|
| 984 | + } |
|
| 985 | + ); |
|
| 986 | 986 | |
| 987 | - $result = $instance->getShareByToken('token'); |
|
| 987 | + $result = $instance->getShareByToken('token'); |
|
| 988 | 988 | |
| 989 | - $this->assertInstanceOf('OCP\Share\IShare', $result); |
|
| 990 | - } |
|
| 989 | + $this->assertInstanceOf('OCP\Share\IShare', $result); |
|
| 990 | + } |
|
| 991 | 991 | |
| 992 | 992 | |
| 993 | - public function testGetShareByTokenFailed(): void { |
|
| 994 | - $this->expectException(ShareNotFound::class); |
|
| 993 | + public function testGetShareByTokenFailed(): void { |
|
| 994 | + $this->expectException(ShareNotFound::class); |
|
| 995 | 995 | |
| 996 | 996 | |
| 997 | - $itemSource = 11; |
|
| 998 | - $itemType = 'file'; |
|
| 999 | - $shareWith = '[email protected]'; |
|
| 1000 | - $sharedBy = 'user1'; |
|
| 1001 | - $uidOwner = 'user2'; |
|
| 1002 | - $permissions = 1; |
|
| 1003 | - $token = 'token'; |
|
| 997 | + $itemSource = 11; |
|
| 998 | + $itemType = 'file'; |
|
| 999 | + $shareWith = '[email protected]'; |
|
| 1000 | + $sharedBy = 'user1'; |
|
| 1001 | + $uidOwner = 'user2'; |
|
| 1002 | + $permissions = 1; |
|
| 1003 | + $token = 'token'; |
|
| 1004 | 1004 | |
| 1005 | - $instance = $this->getInstance(['createShareObject']); |
|
| 1005 | + $instance = $this->getInstance(['createShareObject']); |
|
| 1006 | 1006 | |
| 1007 | - $idMail = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 1008 | - $idPublic = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, 'token2', '', IShare::TYPE_LINK); |
|
| 1007 | + $idMail = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 1008 | + $idPublic = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, 'token2', '', IShare::TYPE_LINK); |
|
| 1009 | 1009 | |
| 1010 | - $this->assertTrue($idMail !== $idPublic); |
|
| 1010 | + $this->assertTrue($idMail !== $idPublic); |
|
| 1011 | 1011 | |
| 1012 | - $this->assertInstanceOf('OCP\Share\IShare', |
|
| 1013 | - $instance->getShareByToken('token2') |
|
| 1014 | - ); |
|
| 1015 | - } |
|
| 1012 | + $this->assertInstanceOf('OCP\Share\IShare', |
|
| 1013 | + $instance->getShareByToken('token2') |
|
| 1014 | + ); |
|
| 1015 | + } |
|
| 1016 | 1016 | |
| 1017 | - public function testRemoveShareFromTable(): void { |
|
| 1018 | - $itemSource = 11; |
|
| 1019 | - $itemType = 'file'; |
|
| 1020 | - $shareWith = '[email protected]'; |
|
| 1021 | - $sharedBy = 'user1'; |
|
| 1022 | - $uidOwner = 'user2'; |
|
| 1023 | - $permissions = 1; |
|
| 1024 | - $token = 'token'; |
|
| 1017 | + public function testRemoveShareFromTable(): void { |
|
| 1018 | + $itemSource = 11; |
|
| 1019 | + $itemType = 'file'; |
|
| 1020 | + $shareWith = '[email protected]'; |
|
| 1021 | + $sharedBy = 'user1'; |
|
| 1022 | + $uidOwner = 'user2'; |
|
| 1023 | + $permissions = 1; |
|
| 1024 | + $token = 'token'; |
|
| 1025 | 1025 | |
| 1026 | - $instance = $this->getInstance(); |
|
| 1026 | + $instance = $this->getInstance(); |
|
| 1027 | 1027 | |
| 1028 | - $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 1028 | + $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 1029 | 1029 | |
| 1030 | - $query = $this->connection->getQueryBuilder(); |
|
| 1031 | - $query->select('*')->from('share') |
|
| 1032 | - ->where($query->expr()->eq('id', $query->createNamedParameter($id))); |
|
| 1030 | + $query = $this->connection->getQueryBuilder(); |
|
| 1031 | + $query->select('*')->from('share') |
|
| 1032 | + ->where($query->expr()->eq('id', $query->createNamedParameter($id))); |
|
| 1033 | 1033 | |
| 1034 | - $result = $query->execute(); |
|
| 1035 | - $before = $result->fetchAll(); |
|
| 1036 | - $result->closeCursor(); |
|
| 1034 | + $result = $query->execute(); |
|
| 1035 | + $before = $result->fetchAll(); |
|
| 1036 | + $result->closeCursor(); |
|
| 1037 | 1037 | |
| 1038 | - $this->assertTrue(is_array($before)); |
|
| 1039 | - $this->assertSame(1, count($before)); |
|
| 1038 | + $this->assertTrue(is_array($before)); |
|
| 1039 | + $this->assertSame(1, count($before)); |
|
| 1040 | 1040 | |
| 1041 | - $this->invokePrivate($instance, 'removeShareFromTable', [$id]); |
|
| 1041 | + $this->invokePrivate($instance, 'removeShareFromTable', [$id]); |
|
| 1042 | 1042 | |
| 1043 | - $query = $this->connection->getQueryBuilder(); |
|
| 1044 | - $query->select('*')->from('share') |
|
| 1045 | - ->where($query->expr()->eq('id', $query->createNamedParameter($id))); |
|
| 1043 | + $query = $this->connection->getQueryBuilder(); |
|
| 1044 | + $query->select('*')->from('share') |
|
| 1045 | + ->where($query->expr()->eq('id', $query->createNamedParameter($id))); |
|
| 1046 | 1046 | |
| 1047 | - $result = $query->execute(); |
|
| 1048 | - $after = $result->fetchAll(); |
|
| 1049 | - $result->closeCursor(); |
|
| 1047 | + $result = $query->execute(); |
|
| 1048 | + $after = $result->fetchAll(); |
|
| 1049 | + $result->closeCursor(); |
|
| 1050 | 1050 | |
| 1051 | - $this->assertTrue(is_array($after)); |
|
| 1052 | - $this->assertEmpty($after); |
|
| 1053 | - } |
|
| 1051 | + $this->assertTrue(is_array($after)); |
|
| 1052 | + $this->assertEmpty($after); |
|
| 1053 | + } |
|
| 1054 | 1054 | |
| 1055 | - public function testUserDeleted(): void { |
|
| 1056 | - $itemSource = 11; |
|
| 1057 | - $itemType = 'file'; |
|
| 1058 | - $shareWith = '[email protected]'; |
|
| 1059 | - $sharedBy = 'user1'; |
|
| 1060 | - $uidOwner = 'user2'; |
|
| 1061 | - $permissions = 1; |
|
| 1062 | - $token = 'token'; |
|
| 1055 | + public function testUserDeleted(): void { |
|
| 1056 | + $itemSource = 11; |
|
| 1057 | + $itemType = 'file'; |
|
| 1058 | + $shareWith = '[email protected]'; |
|
| 1059 | + $sharedBy = 'user1'; |
|
| 1060 | + $uidOwner = 'user2'; |
|
| 1061 | + $permissions = 1; |
|
| 1062 | + $token = 'token'; |
|
| 1063 | 1063 | |
| 1064 | - $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 1065 | - $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, 'user2Wrong', $permissions, $token); |
|
| 1064 | + $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 1065 | + $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, 'user2Wrong', $permissions, $token); |
|
| 1066 | 1066 | |
| 1067 | - $query = $this->connection->getQueryBuilder(); |
|
| 1068 | - $query->select('*')->from('share'); |
|
| 1067 | + $query = $this->connection->getQueryBuilder(); |
|
| 1068 | + $query->select('*')->from('share'); |
|
| 1069 | 1069 | |
| 1070 | - $result = $query->execute(); |
|
| 1071 | - $before = $result->fetchAll(); |
|
| 1072 | - $result->closeCursor(); |
|
| 1070 | + $result = $query->execute(); |
|
| 1071 | + $before = $result->fetchAll(); |
|
| 1072 | + $result->closeCursor(); |
|
| 1073 | 1073 | |
| 1074 | - $this->assertTrue(is_array($before)); |
|
| 1075 | - $this->assertSame(2, count($before)); |
|
| 1074 | + $this->assertTrue(is_array($before)); |
|
| 1075 | + $this->assertSame(2, count($before)); |
|
| 1076 | 1076 | |
| 1077 | 1077 | |
| 1078 | - $instance = $this->getInstance(); |
|
| 1078 | + $instance = $this->getInstance(); |
|
| 1079 | 1079 | |
| 1080 | - $instance->userDeleted($uidOwner, IShare::TYPE_EMAIL); |
|
| 1080 | + $instance->userDeleted($uidOwner, IShare::TYPE_EMAIL); |
|
| 1081 | 1081 | |
| 1082 | - $query = $this->connection->getQueryBuilder(); |
|
| 1083 | - $query->select('*')->from('share'); |
|
| 1082 | + $query = $this->connection->getQueryBuilder(); |
|
| 1083 | + $query->select('*')->from('share'); |
|
| 1084 | 1084 | |
| 1085 | - $result = $query->execute(); |
|
| 1086 | - $after = $result->fetchAll(); |
|
| 1087 | - $result->closeCursor(); |
|
| 1085 | + $result = $query->execute(); |
|
| 1086 | + $after = $result->fetchAll(); |
|
| 1087 | + $result->closeCursor(); |
|
| 1088 | 1088 | |
| 1089 | - $this->assertTrue(is_array($after)); |
|
| 1090 | - $this->assertSame(1, count($after)); |
|
| 1091 | - $this->assertSame($id, (int)$after[0]['id']); |
|
| 1092 | - } |
|
| 1089 | + $this->assertTrue(is_array($after)); |
|
| 1090 | + $this->assertSame(1, count($after)); |
|
| 1091 | + $this->assertSame($id, (int)$after[0]['id']); |
|
| 1092 | + } |
|
| 1093 | 1093 | |
| 1094 | - public function testGetRawShare(): void { |
|
| 1095 | - $itemSource = 11; |
|
| 1096 | - $itemType = 'file'; |
|
| 1097 | - $shareWith = '[email protected]'; |
|
| 1098 | - $sharedBy = 'user1'; |
|
| 1099 | - $uidOwner = 'user2'; |
|
| 1100 | - $permissions = 1; |
|
| 1101 | - $token = 'token'; |
|
| 1094 | + public function testGetRawShare(): void { |
|
| 1095 | + $itemSource = 11; |
|
| 1096 | + $itemType = 'file'; |
|
| 1097 | + $shareWith = '[email protected]'; |
|
| 1098 | + $sharedBy = 'user1'; |
|
| 1099 | + $uidOwner = 'user2'; |
|
| 1100 | + $permissions = 1; |
|
| 1101 | + $token = 'token'; |
|
| 1102 | 1102 | |
| 1103 | - $instance = $this->getInstance(); |
|
| 1103 | + $instance = $this->getInstance(); |
|
| 1104 | 1104 | |
| 1105 | - $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 1105 | + $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 1106 | 1106 | |
| 1107 | - $result = $this->invokePrivate($instance, 'getRawShare', [$id]); |
|
| 1108 | - |
|
| 1109 | - $this->assertTrue(is_array($result)); |
|
| 1110 | - $this->assertSame($itemSource, (int)$result['item_source']); |
|
| 1111 | - $this->assertSame($itemType, $result['item_type']); |
|
| 1112 | - $this->assertSame($shareWith, $result['share_with']); |
|
| 1113 | - $this->assertSame($sharedBy, $result['uid_initiator']); |
|
| 1114 | - $this->assertSame($uidOwner, $result['uid_owner']); |
|
| 1115 | - $this->assertSame($permissions, (int)$result['permissions']); |
|
| 1116 | - $this->assertSame($token, $result['token']); |
|
| 1117 | - } |
|
| 1107 | + $result = $this->invokePrivate($instance, 'getRawShare', [$id]); |
|
| 1108 | + |
|
| 1109 | + $this->assertTrue(is_array($result)); |
|
| 1110 | + $this->assertSame($itemSource, (int)$result['item_source']); |
|
| 1111 | + $this->assertSame($itemType, $result['item_type']); |
|
| 1112 | + $this->assertSame($shareWith, $result['share_with']); |
|
| 1113 | + $this->assertSame($sharedBy, $result['uid_initiator']); |
|
| 1114 | + $this->assertSame($uidOwner, $result['uid_owner']); |
|
| 1115 | + $this->assertSame($permissions, (int)$result['permissions']); |
|
| 1116 | + $this->assertSame($token, $result['token']); |
|
| 1117 | + } |
|
| 1118 | 1118 | |
| 1119 | 1119 | |
| 1120 | - public function testGetRawShareFailed(): void { |
|
| 1121 | - $this->expectException(ShareNotFound::class); |
|
| 1120 | + public function testGetRawShareFailed(): void { |
|
| 1121 | + $this->expectException(ShareNotFound::class); |
|
| 1122 | 1122 | |
| 1123 | - $itemSource = 11; |
|
| 1124 | - $itemType = 'file'; |
|
| 1125 | - $shareWith = '[email protected]'; |
|
| 1126 | - $sharedBy = 'user1'; |
|
| 1127 | - $uidOwner = 'user2'; |
|
| 1128 | - $permissions = 1; |
|
| 1129 | - $token = 'token'; |
|
| 1130 | - |
|
| 1131 | - $instance = $this->getInstance(); |
|
| 1123 | + $itemSource = 11; |
|
| 1124 | + $itemType = 'file'; |
|
| 1125 | + $shareWith = '[email protected]'; |
|
| 1126 | + $sharedBy = 'user1'; |
|
| 1127 | + $uidOwner = 'user2'; |
|
| 1128 | + $permissions = 1; |
|
| 1129 | + $token = 'token'; |
|
| 1130 | + |
|
| 1131 | + $instance = $this->getInstance(); |
|
| 1132 | 1132 | |
| 1133 | - $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 1134 | - |
|
| 1135 | - $this->invokePrivate($instance, 'getRawShare', [$id + 1]); |
|
| 1136 | - } |
|
| 1133 | + $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); |
|
| 1134 | + |
|
| 1135 | + $this->invokePrivate($instance, 'getRawShare', [$id + 1]); |
|
| 1136 | + } |
|
| 1137 | 1137 | |
| 1138 | - private function createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $note = '', $shareType = IShare::TYPE_EMAIL) { |
|
| 1139 | - $qb = $this->connection->getQueryBuilder(); |
|
| 1140 | - $qb->insert('share') |
|
| 1141 | - ->setValue('share_type', $qb->createNamedParameter($shareType)) |
|
| 1142 | - ->setValue('item_type', $qb->createNamedParameter($itemType)) |
|
| 1143 | - ->setValue('item_source', $qb->createNamedParameter($itemSource)) |
|
| 1144 | - ->setValue('file_source', $qb->createNamedParameter($itemSource)) |
|
| 1145 | - ->setValue('share_with', $qb->createNamedParameter($shareWith)) |
|
| 1146 | - ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) |
|
| 1147 | - ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) |
|
| 1148 | - ->setValue('permissions', $qb->createNamedParameter($permissions)) |
|
| 1149 | - ->setValue('token', $qb->createNamedParameter($token)) |
|
| 1150 | - ->setValue('note', $qb->createNamedParameter($note)) |
|
| 1151 | - ->setValue('stime', $qb->createNamedParameter(time())); |
|
| 1152 | - |
|
| 1153 | - /* |
|
| 1138 | + private function createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $note = '', $shareType = IShare::TYPE_EMAIL) { |
|
| 1139 | + $qb = $this->connection->getQueryBuilder(); |
|
| 1140 | + $qb->insert('share') |
|
| 1141 | + ->setValue('share_type', $qb->createNamedParameter($shareType)) |
|
| 1142 | + ->setValue('item_type', $qb->createNamedParameter($itemType)) |
|
| 1143 | + ->setValue('item_source', $qb->createNamedParameter($itemSource)) |
|
| 1144 | + ->setValue('file_source', $qb->createNamedParameter($itemSource)) |
|
| 1145 | + ->setValue('share_with', $qb->createNamedParameter($shareWith)) |
|
| 1146 | + ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) |
|
| 1147 | + ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) |
|
| 1148 | + ->setValue('permissions', $qb->createNamedParameter($permissions)) |
|
| 1149 | + ->setValue('token', $qb->createNamedParameter($token)) |
|
| 1150 | + ->setValue('note', $qb->createNamedParameter($note)) |
|
| 1151 | + ->setValue('stime', $qb->createNamedParameter(time())); |
|
| 1152 | + |
|
| 1153 | + /* |
|
| 1154 | 1154 | * Added to fix https://github.com/owncloud/core/issues/22215 |
| 1155 | 1155 | * Can be removed once we get rid of ajax/share.php |
| 1156 | 1156 | */ |
| 1157 | - $qb->setValue('file_target', $qb->createNamedParameter('')); |
|
| 1158 | - |
|
| 1159 | - $qb->execute(); |
|
| 1160 | - $id = $qb->getLastInsertId(); |
|
| 1161 | - |
|
| 1162 | - return (int)$id; |
|
| 1163 | - } |
|
| 1164 | - |
|
| 1165 | - public function testGetSharesInFolder(): void { |
|
| 1166 | - $userManager = Server::get(IUserManager::class); |
|
| 1167 | - $rootFolder = Server::get(IRootFolder::class); |
|
| 1168 | - |
|
| 1169 | - $this->shareManager->expects($this->any()) |
|
| 1170 | - ->method('newShare') |
|
| 1171 | - ->willReturn(new Share($rootFolder, $userManager)); |
|
| 1172 | - |
|
| 1173 | - $provider = $this->getInstance(['sendMailNotification', 'createShareActivity']); |
|
| 1174 | - |
|
| 1175 | - $u1 = $userManager->createUser('testFed', md5((string)time())); |
|
| 1176 | - $u2 = $userManager->createUser('testFed2', md5((string)time())); |
|
| 1177 | - |
|
| 1178 | - $folder1 = $rootFolder->getUserFolder($u1->getUID())->newFolder('foo'); |
|
| 1179 | - $file1 = $folder1->newFile('bar1'); |
|
| 1180 | - $file2 = $folder1->newFile('bar2'); |
|
| 1181 | - |
|
| 1182 | - $share1 = $this->shareManager->newShare(); |
|
| 1183 | - $share1->setSharedWith('[email protected]') |
|
| 1184 | - ->setSharedBy($u1->getUID()) |
|
| 1185 | - ->setShareOwner($u1->getUID()) |
|
| 1186 | - ->setPermissions(Constants::PERMISSION_READ) |
|
| 1187 | - ->setNode($file1); |
|
| 1188 | - $provider->create($share1); |
|
| 1189 | - |
|
| 1190 | - $share2 = $this->shareManager->newShare(); |
|
| 1191 | - $share2->setSharedWith('[email protected]') |
|
| 1192 | - ->setSharedBy($u2->getUID()) |
|
| 1193 | - ->setShareOwner($u1->getUID()) |
|
| 1194 | - ->setPermissions(Constants::PERMISSION_READ) |
|
| 1195 | - ->setNode($file2); |
|
| 1196 | - $provider->create($share2); |
|
| 1197 | - |
|
| 1198 | - $result = $provider->getSharesInFolder($u1->getUID(), $folder1, false); |
|
| 1199 | - $this->assertCount(1, $result); |
|
| 1200 | - $this->assertCount(1, $result[$file1->getId()]); |
|
| 1201 | - |
|
| 1202 | - $result = $provider->getSharesInFolder($u1->getUID(), $folder1, true); |
|
| 1203 | - $this->assertCount(2, $result); |
|
| 1204 | - $this->assertCount(1, $result[$file1->getId()]); |
|
| 1205 | - $this->assertCount(1, $result[$file2->getId()]); |
|
| 1206 | - |
|
| 1207 | - $u1->delete(); |
|
| 1208 | - $u2->delete(); |
|
| 1209 | - } |
|
| 1210 | - |
|
| 1211 | - public function testGetAccessList(): void { |
|
| 1212 | - $userManager = Server::get(IUserManager::class); |
|
| 1213 | - $rootFolder = Server::get(IRootFolder::class); |
|
| 1214 | - |
|
| 1215 | - $this->shareManager->expects($this->any()) |
|
| 1216 | - ->method('newShare') |
|
| 1217 | - ->willReturn(new Share($rootFolder, $userManager)); |
|
| 1218 | - |
|
| 1219 | - $provider = $this->getInstance(['sendMailNotification', 'createShareActivity']); |
|
| 1220 | - |
|
| 1221 | - $u1 = $userManager->createUser('testFed', md5((string)time())); |
|
| 1222 | - $u2 = $userManager->createUser('testFed2', md5((string)time())); |
|
| 1223 | - |
|
| 1224 | - $folder = $rootFolder->getUserFolder($u1->getUID())->newFolder('foo'); |
|
| 1225 | - |
|
| 1226 | - $accessList = $provider->getAccessList([$folder], true); |
|
| 1227 | - $this->assertArrayHasKey('public', $accessList); |
|
| 1228 | - $this->assertFalse($accessList['public']); |
|
| 1229 | - $accessList = $provider->getAccessList([$folder], false); |
|
| 1230 | - $this->assertArrayHasKey('public', $accessList); |
|
| 1231 | - $this->assertFalse($accessList['public']); |
|
| 1232 | - |
|
| 1233 | - $share1 = $this->shareManager->newShare(); |
|
| 1234 | - $share1->setSharedWith('[email protected]') |
|
| 1235 | - ->setSharedBy($u1->getUID()) |
|
| 1236 | - ->setShareOwner($u1->getUID()) |
|
| 1237 | - ->setPermissions(Constants::PERMISSION_READ) |
|
| 1238 | - ->setNode($folder); |
|
| 1239 | - $share1 = $provider->create($share1); |
|
| 1240 | - |
|
| 1241 | - $share2 = $this->shareManager->newShare(); |
|
| 1242 | - $share2->setSharedWith('[email protected]') |
|
| 1243 | - ->setSharedBy($u2->getUID()) |
|
| 1244 | - ->setShareOwner($u1->getUID()) |
|
| 1245 | - ->setPermissions(Constants::PERMISSION_READ) |
|
| 1246 | - ->setNode($folder); |
|
| 1247 | - $share2 = $provider->create($share2); |
|
| 1248 | - |
|
| 1249 | - $accessList = $provider->getAccessList([$folder], true); |
|
| 1250 | - $this->assertArrayHasKey('public', $accessList); |
|
| 1251 | - $this->assertTrue($accessList['public']); |
|
| 1252 | - $accessList = $provider->getAccessList([$folder], false); |
|
| 1253 | - $this->assertArrayHasKey('public', $accessList); |
|
| 1254 | - $this->assertTrue($accessList['public']); |
|
| 1255 | - |
|
| 1256 | - $provider->delete($share2); |
|
| 1257 | - |
|
| 1258 | - $accessList = $provider->getAccessList([$folder], true); |
|
| 1259 | - $this->assertArrayHasKey('public', $accessList); |
|
| 1260 | - $this->assertTrue($accessList['public']); |
|
| 1261 | - $accessList = $provider->getAccessList([$folder], false); |
|
| 1262 | - $this->assertArrayHasKey('public', $accessList); |
|
| 1263 | - $this->assertTrue($accessList['public']); |
|
| 1264 | - |
|
| 1265 | - $provider->delete($share1); |
|
| 1266 | - |
|
| 1267 | - $accessList = $provider->getAccessList([$folder], true); |
|
| 1268 | - $this->assertArrayHasKey('public', $accessList); |
|
| 1269 | - $this->assertFalse($accessList['public']); |
|
| 1270 | - $accessList = $provider->getAccessList([$folder], false); |
|
| 1271 | - $this->assertArrayHasKey('public', $accessList); |
|
| 1272 | - $this->assertFalse($accessList['public']); |
|
| 1273 | - |
|
| 1274 | - $u1->delete(); |
|
| 1275 | - $u2->delete(); |
|
| 1276 | - } |
|
| 1277 | - |
|
| 1278 | - public function testSendMailNotificationWithSameUserAndUserEmail(): void { |
|
| 1279 | - $provider = $this->getInstance(); |
|
| 1280 | - $user = $this->createMock(IUser::class); |
|
| 1281 | - $this->settingsManager->expects($this->any())->method('replyToInitiator')->willReturn(true); |
|
| 1282 | - $this->userManager |
|
| 1283 | - ->expects($this->once()) |
|
| 1284 | - ->method('get') |
|
| 1285 | - ->with('OwnerUser') |
|
| 1286 | - ->willReturn($user); |
|
| 1287 | - $user |
|
| 1288 | - ->expects($this->once()) |
|
| 1289 | - ->method('getDisplayName') |
|
| 1290 | - ->willReturn('Mrs. Owner User'); |
|
| 1291 | - $message = $this->createMock(Message::class); |
|
| 1292 | - $this->mailer |
|
| 1293 | - ->expects($this->once()) |
|
| 1294 | - ->method('createMessage') |
|
| 1295 | - ->willReturn($message); |
|
| 1296 | - $template = $this->createMock(IEMailTemplate::class); |
|
| 1297 | - $this->mailer |
|
| 1298 | - ->expects($this->once()) |
|
| 1299 | - ->method('createEMailTemplate') |
|
| 1300 | - ->willReturn($template); |
|
| 1301 | - $template |
|
| 1302 | - ->expects($this->once()) |
|
| 1303 | - ->method('addHeader'); |
|
| 1304 | - $template |
|
| 1305 | - ->expects($this->once()) |
|
| 1306 | - ->method('addHeading') |
|
| 1307 | - ->with('Mrs. Owner User shared file.txt with you'); |
|
| 1308 | - $template |
|
| 1309 | - ->expects($this->once()) |
|
| 1310 | - ->method('addBodyButton') |
|
| 1311 | - ->with( |
|
| 1312 | - 'Open file.txt', |
|
| 1313 | - 'https://example.com/file.txt' |
|
| 1314 | - ); |
|
| 1315 | - $message |
|
| 1316 | - ->expects($this->once()) |
|
| 1317 | - ->method('setTo') |
|
| 1318 | - ->with(['[email protected]']); |
|
| 1319 | - $this->defaults |
|
| 1320 | - ->expects($this->once()) |
|
| 1321 | - ->method('getName') |
|
| 1322 | - ->willReturn('UnitTestCloud'); |
|
| 1323 | - $message |
|
| 1324 | - ->expects($this->once()) |
|
| 1325 | - ->method('setFrom') |
|
| 1326 | - ->with([ |
|
| 1327 | - Util::getDefaultEmailAddress('UnitTestCloud') => 'Mrs. Owner User via UnitTestCloud' |
|
| 1328 | - ]); |
|
| 1329 | - $user |
|
| 1330 | - ->expects($this->once()) |
|
| 1331 | - ->method('getEMailAddress') |
|
| 1332 | - ->willReturn('[email protected]'); |
|
| 1333 | - $message |
|
| 1334 | - ->expects($this->once()) |
|
| 1335 | - ->method('setReplyTo') |
|
| 1336 | - ->with(['[email protected]' => 'Mrs. Owner User']); |
|
| 1337 | - $this->defaults |
|
| 1338 | - ->expects($this->exactly(2)) |
|
| 1339 | - ->method('getSlogan') |
|
| 1340 | - ->willReturn('Testing like 1990'); |
|
| 1341 | - $template |
|
| 1342 | - ->expects($this->once()) |
|
| 1343 | - ->method('addFooter') |
|
| 1344 | - ->with('UnitTestCloud - Testing like 1990'); |
|
| 1345 | - $template |
|
| 1346 | - ->expects($this->once()) |
|
| 1347 | - ->method('setSubject') |
|
| 1348 | - ->with('Mrs. Owner User shared file.txt with you'); |
|
| 1349 | - $message |
|
| 1350 | - ->expects($this->once()) |
|
| 1351 | - ->method('useTemplate') |
|
| 1352 | - ->with($template); |
|
| 1353 | - |
|
| 1354 | - $this->mailer |
|
| 1355 | - ->expects($this->once()) |
|
| 1356 | - ->method('send') |
|
| 1357 | - ->with($message); |
|
| 1358 | - |
|
| 1359 | - $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 1360 | - ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 1361 | - ->willReturn('https://example.com/file.txt'); |
|
| 1362 | - |
|
| 1363 | - $node = $this->createMock(File::class); |
|
| 1364 | - $node->expects($this->any())->method('getName')->willReturn('file.txt'); |
|
| 1365 | - |
|
| 1366 | - $share = $this->createMock(IShare::class); |
|
| 1367 | - $share->expects($this->any())->method('getSharedBy')->willReturn('OwnerUser'); |
|
| 1368 | - $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 1369 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 1370 | - $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 1371 | - $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 1372 | - $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 1373 | - |
|
| 1374 | - self::invokePrivate( |
|
| 1375 | - $provider, |
|
| 1376 | - 'sendMailNotification', |
|
| 1377 | - [$share] |
|
| 1378 | - ); |
|
| 1379 | - } |
|
| 1380 | - |
|
| 1381 | - public function testSendMailNotificationWithSameUserAndUserEmailAndNote(): void { |
|
| 1382 | - $provider = $this->getInstance(); |
|
| 1383 | - $user = $this->createMock(IUser::class); |
|
| 1384 | - $this->settingsManager->expects($this->any())->method('replyToInitiator')->willReturn(true); |
|
| 1385 | - $this->userManager |
|
| 1386 | - ->expects($this->once()) |
|
| 1387 | - ->method('get') |
|
| 1388 | - ->with('OwnerUser') |
|
| 1389 | - ->willReturn($user); |
|
| 1390 | - $user |
|
| 1391 | - ->expects($this->once()) |
|
| 1392 | - ->method('getDisplayName') |
|
| 1393 | - ->willReturn('Mrs. Owner User'); |
|
| 1394 | - $message = $this->createMock(Message::class); |
|
| 1395 | - $this->mailer |
|
| 1396 | - ->expects($this->once()) |
|
| 1397 | - ->method('createMessage') |
|
| 1398 | - ->willReturn($message); |
|
| 1399 | - $template = $this->createMock(IEMailTemplate::class); |
|
| 1400 | - $this->mailer |
|
| 1401 | - ->expects($this->once()) |
|
| 1402 | - ->method('createEMailTemplate') |
|
| 1403 | - ->willReturn($template); |
|
| 1404 | - $template |
|
| 1405 | - ->expects($this->once()) |
|
| 1406 | - ->method('addHeader'); |
|
| 1407 | - $template |
|
| 1408 | - ->expects($this->once()) |
|
| 1409 | - ->method('addHeading') |
|
| 1410 | - ->with('Mrs. Owner User shared file.txt with you'); |
|
| 1411 | - |
|
| 1412 | - $this->urlGenerator->expects($this->once())->method('imagePath') |
|
| 1413 | - ->with('core', 'caldav/description.png') |
|
| 1414 | - ->willReturn('core/img/caldav/description.png'); |
|
| 1415 | - $this->urlGenerator->expects($this->once())->method('getAbsoluteURL') |
|
| 1416 | - ->with('core/img/caldav/description.png') |
|
| 1417 | - ->willReturn('https://example.com/core/img/caldav/description.png'); |
|
| 1418 | - $template |
|
| 1419 | - ->expects($this->once()) |
|
| 1420 | - ->method('addBodyListItem') |
|
| 1421 | - ->with( |
|
| 1422 | - 'This is a note to the recipient', |
|
| 1423 | - 'Note:', |
|
| 1424 | - 'https://example.com/core/img/caldav/description.png', |
|
| 1425 | - 'This is a note to the recipient' |
|
| 1426 | - ); |
|
| 1427 | - $template |
|
| 1428 | - ->expects($this->once()) |
|
| 1429 | - ->method('addBodyButton') |
|
| 1430 | - ->with( |
|
| 1431 | - 'Open file.txt', |
|
| 1432 | - 'https://example.com/file.txt' |
|
| 1433 | - ); |
|
| 1434 | - $message |
|
| 1435 | - ->expects($this->once()) |
|
| 1436 | - ->method('setTo') |
|
| 1437 | - ->with(['[email protected]']); |
|
| 1438 | - $this->defaults |
|
| 1439 | - ->expects($this->once()) |
|
| 1440 | - ->method('getName') |
|
| 1441 | - ->willReturn('UnitTestCloud'); |
|
| 1442 | - $message |
|
| 1443 | - ->expects($this->once()) |
|
| 1444 | - ->method('setFrom') |
|
| 1445 | - ->with([ |
|
| 1446 | - Util::getDefaultEmailAddress('UnitTestCloud') => 'Mrs. Owner User via UnitTestCloud' |
|
| 1447 | - ]); |
|
| 1448 | - $user |
|
| 1449 | - ->expects($this->once()) |
|
| 1450 | - ->method('getEMailAddress') |
|
| 1451 | - ->willReturn('[email protected]'); |
|
| 1452 | - $message |
|
| 1453 | - ->expects($this->once()) |
|
| 1454 | - ->method('setReplyTo') |
|
| 1455 | - ->with(['[email protected]' => 'Mrs. Owner User']); |
|
| 1456 | - $this->defaults |
|
| 1457 | - ->expects($this->exactly(2)) |
|
| 1458 | - ->method('getSlogan') |
|
| 1459 | - ->willReturn('Testing like 1990'); |
|
| 1460 | - $template |
|
| 1461 | - ->expects($this->once()) |
|
| 1462 | - ->method('addFooter') |
|
| 1463 | - ->with('UnitTestCloud - Testing like 1990'); |
|
| 1464 | - $template |
|
| 1465 | - ->expects($this->once()) |
|
| 1466 | - ->method('setSubject') |
|
| 1467 | - ->with('Mrs. Owner User shared file.txt with you'); |
|
| 1468 | - $message |
|
| 1469 | - ->expects($this->once()) |
|
| 1470 | - ->method('useTemplate') |
|
| 1471 | - ->with($template); |
|
| 1472 | - |
|
| 1473 | - $this->mailer |
|
| 1474 | - ->expects($this->once()) |
|
| 1475 | - ->method('send') |
|
| 1476 | - ->with($message); |
|
| 1477 | - |
|
| 1478 | - $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 1479 | - ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 1480 | - ->willReturn('https://example.com/file.txt'); |
|
| 1481 | - |
|
| 1482 | - $node = $this->createMock(File::class); |
|
| 1483 | - $node->expects($this->any())->method('getName')->willReturn('file.txt'); |
|
| 1484 | - |
|
| 1485 | - $share = $this->createMock(IShare::class); |
|
| 1486 | - $share->expects($this->any())->method('getSharedBy')->willReturn('OwnerUser'); |
|
| 1487 | - $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 1488 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 1489 | - $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 1490 | - $share->expects($this->any())->method('getNote')->willReturn('This is a note to the recipient'); |
|
| 1491 | - $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 1492 | - |
|
| 1493 | - self::invokePrivate( |
|
| 1494 | - $provider, |
|
| 1495 | - 'sendMailNotification', |
|
| 1496 | - [$share] |
|
| 1497 | - ); |
|
| 1498 | - } |
|
| 1499 | - |
|
| 1500 | - public function testSendMailNotificationWithSameUserAndUserEmailAndExpiration(): void { |
|
| 1501 | - $provider = $this->getInstance(); |
|
| 1502 | - $user = $this->createMock(IUser::class); |
|
| 1503 | - $this->settingsManager->expects($this->any())->method('replyToInitiator')->willReturn(true); |
|
| 1504 | - $this->userManager |
|
| 1505 | - ->expects($this->once()) |
|
| 1506 | - ->method('get') |
|
| 1507 | - ->with('OwnerUser') |
|
| 1508 | - ->willReturn($user); |
|
| 1509 | - $user |
|
| 1510 | - ->expects($this->once()) |
|
| 1511 | - ->method('getDisplayName') |
|
| 1512 | - ->willReturn('Mrs. Owner User'); |
|
| 1513 | - $message = $this->createMock(Message::class); |
|
| 1514 | - $this->mailer |
|
| 1515 | - ->expects($this->once()) |
|
| 1516 | - ->method('createMessage') |
|
| 1517 | - ->willReturn($message); |
|
| 1518 | - $template = $this->createMock(IEMailTemplate::class); |
|
| 1519 | - $this->mailer |
|
| 1520 | - ->expects($this->once()) |
|
| 1521 | - ->method('createEMailTemplate') |
|
| 1522 | - ->willReturn($template); |
|
| 1523 | - $template |
|
| 1524 | - ->expects($this->once()) |
|
| 1525 | - ->method('addHeader'); |
|
| 1526 | - $template |
|
| 1527 | - ->expects($this->once()) |
|
| 1528 | - ->method('addHeading') |
|
| 1529 | - ->with('Mrs. Owner User shared file.txt with you'); |
|
| 1530 | - |
|
| 1531 | - $expiration = new DateTime('2001-01-01'); |
|
| 1532 | - $this->l->expects($this->once()) |
|
| 1533 | - ->method('l') |
|
| 1534 | - ->with('date', $expiration, ['width' => 'medium']) |
|
| 1535 | - ->willReturn('2001-01-01'); |
|
| 1536 | - $this->urlGenerator->expects($this->once())->method('imagePath') |
|
| 1537 | - ->with('core', 'caldav/time.png') |
|
| 1538 | - ->willReturn('core/img/caldav/time.png'); |
|
| 1539 | - $this->urlGenerator->expects($this->once())->method('getAbsoluteURL') |
|
| 1540 | - ->with('core/img/caldav/time.png') |
|
| 1541 | - ->willReturn('https://example.com/core/img/caldav/time.png'); |
|
| 1542 | - $template |
|
| 1543 | - ->expects($this->once()) |
|
| 1544 | - ->method('addBodyListItem') |
|
| 1545 | - ->with( |
|
| 1546 | - 'This share is valid until 2001-01-01 at midnight', |
|
| 1547 | - 'Expiration:', |
|
| 1548 | - 'https://example.com/core/img/caldav/time.png', |
|
| 1549 | - ); |
|
| 1550 | - |
|
| 1551 | - $template |
|
| 1552 | - ->expects($this->once()) |
|
| 1553 | - ->method('addBodyButton') |
|
| 1554 | - ->with( |
|
| 1555 | - 'Open file.txt', |
|
| 1556 | - 'https://example.com/file.txt' |
|
| 1557 | - ); |
|
| 1558 | - $message |
|
| 1559 | - ->expects($this->once()) |
|
| 1560 | - ->method('setTo') |
|
| 1561 | - ->with(['[email protected]']); |
|
| 1562 | - $this->defaults |
|
| 1563 | - ->expects($this->once()) |
|
| 1564 | - ->method('getName') |
|
| 1565 | - ->willReturn('UnitTestCloud'); |
|
| 1566 | - $message |
|
| 1567 | - ->expects($this->once()) |
|
| 1568 | - ->method('setFrom') |
|
| 1569 | - ->with([ |
|
| 1570 | - Util::getDefaultEmailAddress('UnitTestCloud') => 'Mrs. Owner User via UnitTestCloud' |
|
| 1571 | - ]); |
|
| 1572 | - $user |
|
| 1573 | - ->expects($this->once()) |
|
| 1574 | - ->method('getEMailAddress') |
|
| 1575 | - ->willReturn('[email protected]'); |
|
| 1576 | - $message |
|
| 1577 | - ->expects($this->once()) |
|
| 1578 | - ->method('setReplyTo') |
|
| 1579 | - ->with(['[email protected]' => 'Mrs. Owner User']); |
|
| 1580 | - $this->defaults |
|
| 1581 | - ->expects($this->exactly(2)) |
|
| 1582 | - ->method('getSlogan') |
|
| 1583 | - ->willReturn('Testing like 1990'); |
|
| 1584 | - $template |
|
| 1585 | - ->expects($this->once()) |
|
| 1586 | - ->method('addFooter') |
|
| 1587 | - ->with('UnitTestCloud - Testing like 1990'); |
|
| 1588 | - $template |
|
| 1589 | - ->expects($this->once()) |
|
| 1590 | - ->method('setSubject') |
|
| 1591 | - ->with('Mrs. Owner User shared file.txt with you'); |
|
| 1592 | - $message |
|
| 1593 | - ->expects($this->once()) |
|
| 1594 | - ->method('useTemplate') |
|
| 1595 | - ->with($template); |
|
| 1596 | - |
|
| 1597 | - $this->mailer |
|
| 1598 | - ->expects($this->once()) |
|
| 1599 | - ->method('send') |
|
| 1600 | - ->with($message); |
|
| 1601 | - |
|
| 1602 | - $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 1603 | - ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 1604 | - ->willReturn('https://example.com/file.txt'); |
|
| 1605 | - |
|
| 1606 | - $node = $this->createMock(File::class); |
|
| 1607 | - $node->expects($this->any())->method('getName')->willReturn('file.txt'); |
|
| 1608 | - |
|
| 1609 | - $share = $this->createMock(IShare::class); |
|
| 1610 | - $share->expects($this->any())->method('getSharedBy')->willReturn('OwnerUser'); |
|
| 1611 | - $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 1612 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 1613 | - $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 1614 | - $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 1615 | - $share->expects($this->any())->method('getExpirationDate')->willReturn($expiration); |
|
| 1616 | - $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 1617 | - |
|
| 1618 | - self::invokePrivate( |
|
| 1619 | - $provider, |
|
| 1620 | - 'sendMailNotification', |
|
| 1621 | - [$share] |
|
| 1622 | - ); |
|
| 1623 | - } |
|
| 1624 | - |
|
| 1625 | - public function testSendMailNotificationWithDifferentUserAndNoUserEmail(): void { |
|
| 1626 | - $provider = $this->getInstance(); |
|
| 1627 | - $initiatorUser = $this->createMock(IUser::class); |
|
| 1628 | - $this->settingsManager->expects($this->any())->method('replyToInitiator')->willReturn(true); |
|
| 1629 | - $this->userManager |
|
| 1630 | - ->expects($this->once()) |
|
| 1631 | - ->method('get') |
|
| 1632 | - ->with('InitiatorUser') |
|
| 1633 | - ->willReturn($initiatorUser); |
|
| 1634 | - $initiatorUser |
|
| 1635 | - ->expects($this->once()) |
|
| 1636 | - ->method('getDisplayName') |
|
| 1637 | - ->willReturn('Mr. Initiator User'); |
|
| 1638 | - $message = $this->createMock(Message::class); |
|
| 1639 | - $this->mailer |
|
| 1640 | - ->expects($this->once()) |
|
| 1641 | - ->method('createMessage') |
|
| 1642 | - ->willReturn($message); |
|
| 1643 | - $template = $this->createMock(IEMailTemplate::class); |
|
| 1644 | - $this->mailer |
|
| 1645 | - ->expects($this->once()) |
|
| 1646 | - ->method('createEMailTemplate') |
|
| 1647 | - ->willReturn($template); |
|
| 1648 | - $template |
|
| 1649 | - ->expects($this->once()) |
|
| 1650 | - ->method('addHeader'); |
|
| 1651 | - $template |
|
| 1652 | - ->expects($this->once()) |
|
| 1653 | - ->method('addHeading') |
|
| 1654 | - ->with('Mr. Initiator User shared file.txt with you'); |
|
| 1655 | - $template |
|
| 1656 | - ->expects($this->once()) |
|
| 1657 | - ->method('addBodyButton') |
|
| 1658 | - ->with( |
|
| 1659 | - 'Open file.txt', |
|
| 1660 | - 'https://example.com/file.txt' |
|
| 1661 | - ); |
|
| 1662 | - $message |
|
| 1663 | - ->expects($this->once()) |
|
| 1664 | - ->method('setTo') |
|
| 1665 | - ->with(['[email protected]']); |
|
| 1666 | - $this->defaults |
|
| 1667 | - ->expects($this->once()) |
|
| 1668 | - ->method('getName') |
|
| 1669 | - ->willReturn('UnitTestCloud'); |
|
| 1670 | - $message |
|
| 1671 | - ->expects($this->once()) |
|
| 1672 | - ->method('setFrom') |
|
| 1673 | - ->with([ |
|
| 1674 | - Util::getDefaultEmailAddress('UnitTestCloud') => 'Mr. Initiator User via UnitTestCloud' |
|
| 1675 | - ]); |
|
| 1676 | - $message |
|
| 1677 | - ->expects($this->never()) |
|
| 1678 | - ->method('setReplyTo'); |
|
| 1679 | - $template |
|
| 1680 | - ->expects($this->once()) |
|
| 1681 | - ->method('addFooter') |
|
| 1682 | - ->with(''); |
|
| 1683 | - $template |
|
| 1684 | - ->expects($this->once()) |
|
| 1685 | - ->method('setSubject') |
|
| 1686 | - ->with('Mr. Initiator User shared file.txt with you'); |
|
| 1687 | - $message |
|
| 1688 | - ->expects($this->once()) |
|
| 1689 | - ->method('useTemplate') |
|
| 1690 | - ->with($template); |
|
| 1691 | - |
|
| 1692 | - $this->mailer |
|
| 1693 | - ->expects($this->once()) |
|
| 1694 | - ->method('send') |
|
| 1695 | - ->with($message); |
|
| 1696 | - |
|
| 1697 | - $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 1698 | - ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 1699 | - ->willReturn('https://example.com/file.txt'); |
|
| 1700 | - |
|
| 1701 | - $node = $this->createMock(File::class); |
|
| 1702 | - $node->expects($this->any())->method('getName')->willReturn('file.txt'); |
|
| 1703 | - |
|
| 1704 | - $share = $this->createMock(IShare::class); |
|
| 1705 | - $share->expects($this->any())->method('getSharedBy')->willReturn('InitiatorUser'); |
|
| 1706 | - $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 1707 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 1708 | - $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 1709 | - $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 1710 | - $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 1711 | - |
|
| 1712 | - self::invokePrivate( |
|
| 1713 | - $provider, |
|
| 1714 | - 'sendMailNotification', |
|
| 1715 | - [$share] |
|
| 1716 | - ); |
|
| 1717 | - } |
|
| 1718 | - |
|
| 1719 | - public function testSendMailNotificationWithSameUserAndUserEmailAndReplyToDesactivate(): void { |
|
| 1720 | - $provider = $this->getInstance(); |
|
| 1721 | - $user = $this->createMock(IUser::class); |
|
| 1722 | - $this->settingsManager->expects($this->any())->method('replyToInitiator')->willReturn(false); |
|
| 1723 | - $this->userManager |
|
| 1724 | - ->expects($this->once()) |
|
| 1725 | - ->method('get') |
|
| 1726 | - ->with('OwnerUser') |
|
| 1727 | - ->willReturn($user); |
|
| 1728 | - $user |
|
| 1729 | - ->expects($this->once()) |
|
| 1730 | - ->method('getDisplayName') |
|
| 1731 | - ->willReturn('Mrs. Owner User'); |
|
| 1732 | - $message = $this->createMock(Message::class); |
|
| 1733 | - $this->mailer |
|
| 1734 | - ->expects($this->once()) |
|
| 1735 | - ->method('createMessage') |
|
| 1736 | - ->willReturn($message); |
|
| 1737 | - $template = $this->createMock(IEMailTemplate::class); |
|
| 1738 | - $this->mailer |
|
| 1739 | - ->expects($this->once()) |
|
| 1740 | - ->method('createEMailTemplate') |
|
| 1741 | - ->willReturn($template); |
|
| 1742 | - $template |
|
| 1743 | - ->expects($this->once()) |
|
| 1744 | - ->method('addHeader'); |
|
| 1745 | - $template |
|
| 1746 | - ->expects($this->once()) |
|
| 1747 | - ->method('addHeading') |
|
| 1748 | - ->with('Mrs. Owner User shared file.txt with you'); |
|
| 1749 | - $template |
|
| 1750 | - ->expects($this->once()) |
|
| 1751 | - ->method('addBodyButton') |
|
| 1752 | - ->with( |
|
| 1753 | - 'Open file.txt', |
|
| 1754 | - 'https://example.com/file.txt' |
|
| 1755 | - ); |
|
| 1756 | - $message |
|
| 1757 | - ->expects($this->once()) |
|
| 1758 | - ->method('setTo') |
|
| 1759 | - ->with(['[email protected]']); |
|
| 1760 | - $this->defaults |
|
| 1761 | - ->expects($this->once()) |
|
| 1762 | - ->method('getName') |
|
| 1763 | - ->willReturn('UnitTestCloud'); |
|
| 1764 | - $message |
|
| 1765 | - ->expects($this->once()) |
|
| 1766 | - ->method('setFrom') |
|
| 1767 | - ->with([ |
|
| 1768 | - Util::getDefaultEmailAddress('UnitTestCloud') => 'UnitTestCloud' |
|
| 1769 | - ]); |
|
| 1770 | - // Since replyToInitiator is false, we never get the initiator email address |
|
| 1771 | - $user |
|
| 1772 | - ->expects($this->never()) |
|
| 1773 | - ->method('getEMailAddress'); |
|
| 1774 | - $message |
|
| 1775 | - ->expects($this->never()) |
|
| 1776 | - ->method('setReplyTo'); |
|
| 1777 | - $template |
|
| 1778 | - ->expects($this->once()) |
|
| 1779 | - ->method('addFooter') |
|
| 1780 | - ->with(''); |
|
| 1781 | - $template |
|
| 1782 | - ->expects($this->once()) |
|
| 1783 | - ->method('setSubject') |
|
| 1784 | - ->with('Mrs. Owner User shared file.txt with you'); |
|
| 1785 | - $message |
|
| 1786 | - ->expects($this->once()) |
|
| 1787 | - ->method('useTemplate') |
|
| 1788 | - ->with($template); |
|
| 1789 | - |
|
| 1790 | - $this->mailer |
|
| 1791 | - ->expects($this->once()) |
|
| 1792 | - ->method('send') |
|
| 1793 | - ->with($message); |
|
| 1794 | - |
|
| 1795 | - $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 1796 | - ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 1797 | - ->willReturn('https://example.com/file.txt'); |
|
| 1798 | - |
|
| 1799 | - $node = $this->createMock(File::class); |
|
| 1800 | - $node->expects($this->any())->method('getName')->willReturn('file.txt'); |
|
| 1801 | - |
|
| 1802 | - $share = $this->createMock(IShare::class); |
|
| 1803 | - $share->expects($this->any())->method('getSharedBy')->willReturn('OwnerUser'); |
|
| 1804 | - $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 1805 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 1806 | - $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 1807 | - $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 1808 | - $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 1809 | - |
|
| 1810 | - self::invokePrivate( |
|
| 1811 | - $provider, |
|
| 1812 | - 'sendMailNotification', |
|
| 1813 | - [$share] |
|
| 1814 | - ); |
|
| 1815 | - } |
|
| 1816 | - |
|
| 1817 | - public function testSendMailNotificationWithDifferentUserAndNoUserEmailAndReplyToDesactivate(): void { |
|
| 1818 | - $provider = $this->getInstance(); |
|
| 1819 | - $initiatorUser = $this->createMock(IUser::class); |
|
| 1820 | - $this->settingsManager->expects($this->any())->method('replyToInitiator')->willReturn(false); |
|
| 1821 | - $this->userManager |
|
| 1822 | - ->expects($this->once()) |
|
| 1823 | - ->method('get') |
|
| 1824 | - ->with('InitiatorUser') |
|
| 1825 | - ->willReturn($initiatorUser); |
|
| 1826 | - $initiatorUser |
|
| 1827 | - ->expects($this->once()) |
|
| 1828 | - ->method('getDisplayName') |
|
| 1829 | - ->willReturn('Mr. Initiator User'); |
|
| 1830 | - $message = $this->createMock(Message::class); |
|
| 1831 | - $this->mailer |
|
| 1832 | - ->expects($this->once()) |
|
| 1833 | - ->method('createMessage') |
|
| 1834 | - ->willReturn($message); |
|
| 1835 | - $template = $this->createMock(IEMailTemplate::class); |
|
| 1836 | - $this->mailer |
|
| 1837 | - ->expects($this->once()) |
|
| 1838 | - ->method('createEMailTemplate') |
|
| 1839 | - ->willReturn($template); |
|
| 1840 | - $template |
|
| 1841 | - ->expects($this->once()) |
|
| 1842 | - ->method('addHeader'); |
|
| 1843 | - $template |
|
| 1844 | - ->expects($this->once()) |
|
| 1845 | - ->method('addHeading') |
|
| 1846 | - ->with('Mr. Initiator User shared file.txt with you'); |
|
| 1847 | - $template |
|
| 1848 | - ->expects($this->once()) |
|
| 1849 | - ->method('addBodyButton') |
|
| 1850 | - ->with( |
|
| 1851 | - 'Open file.txt', |
|
| 1852 | - 'https://example.com/file.txt' |
|
| 1853 | - ); |
|
| 1854 | - $message |
|
| 1855 | - ->expects($this->once()) |
|
| 1856 | - ->method('setTo') |
|
| 1857 | - ->with(['[email protected]']); |
|
| 1858 | - $this->defaults |
|
| 1859 | - ->expects($this->once()) |
|
| 1860 | - ->method('getName') |
|
| 1861 | - ->willReturn('UnitTestCloud'); |
|
| 1862 | - $message |
|
| 1863 | - ->expects($this->once()) |
|
| 1864 | - ->method('setFrom') |
|
| 1865 | - ->with([ |
|
| 1866 | - Util::getDefaultEmailAddress('UnitTestCloud') => 'UnitTestCloud' |
|
| 1867 | - ]); |
|
| 1868 | - $message |
|
| 1869 | - ->expects($this->never()) |
|
| 1870 | - ->method('setReplyTo'); |
|
| 1871 | - $template |
|
| 1872 | - ->expects($this->once()) |
|
| 1873 | - ->method('addFooter') |
|
| 1874 | - ->with(''); |
|
| 1875 | - $template |
|
| 1876 | - ->expects($this->once()) |
|
| 1877 | - ->method('setSubject') |
|
| 1878 | - ->with('Mr. Initiator User shared file.txt with you'); |
|
| 1879 | - $message |
|
| 1880 | - ->expects($this->once()) |
|
| 1881 | - ->method('useTemplate') |
|
| 1882 | - ->with($template); |
|
| 1883 | - |
|
| 1884 | - $this->mailer |
|
| 1885 | - ->expects($this->once()) |
|
| 1886 | - ->method('send') |
|
| 1887 | - ->with($message); |
|
| 1888 | - |
|
| 1889 | - $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 1890 | - ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 1891 | - ->willReturn('https://example.com/file.txt'); |
|
| 1892 | - |
|
| 1893 | - $node = $this->createMock(File::class); |
|
| 1894 | - $node->expects($this->any())->method('getName')->willReturn('file.txt'); |
|
| 1895 | - |
|
| 1896 | - $share = $this->createMock(IShare::class); |
|
| 1897 | - $share->expects($this->any())->method('getSharedBy')->willReturn('InitiatorUser'); |
|
| 1898 | - $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 1899 | - $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 1900 | - $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 1901 | - $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 1902 | - $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 1903 | - |
|
| 1904 | - self::invokePrivate( |
|
| 1905 | - $provider, |
|
| 1906 | - 'sendMailNotification', |
|
| 1907 | - [$share] |
|
| 1908 | - ); |
|
| 1909 | - } |
|
| 1157 | + $qb->setValue('file_target', $qb->createNamedParameter('')); |
|
| 1158 | + |
|
| 1159 | + $qb->execute(); |
|
| 1160 | + $id = $qb->getLastInsertId(); |
|
| 1161 | + |
|
| 1162 | + return (int)$id; |
|
| 1163 | + } |
|
| 1164 | + |
|
| 1165 | + public function testGetSharesInFolder(): void { |
|
| 1166 | + $userManager = Server::get(IUserManager::class); |
|
| 1167 | + $rootFolder = Server::get(IRootFolder::class); |
|
| 1168 | + |
|
| 1169 | + $this->shareManager->expects($this->any()) |
|
| 1170 | + ->method('newShare') |
|
| 1171 | + ->willReturn(new Share($rootFolder, $userManager)); |
|
| 1172 | + |
|
| 1173 | + $provider = $this->getInstance(['sendMailNotification', 'createShareActivity']); |
|
| 1174 | + |
|
| 1175 | + $u1 = $userManager->createUser('testFed', md5((string)time())); |
|
| 1176 | + $u2 = $userManager->createUser('testFed2', md5((string)time())); |
|
| 1177 | + |
|
| 1178 | + $folder1 = $rootFolder->getUserFolder($u1->getUID())->newFolder('foo'); |
|
| 1179 | + $file1 = $folder1->newFile('bar1'); |
|
| 1180 | + $file2 = $folder1->newFile('bar2'); |
|
| 1181 | + |
|
| 1182 | + $share1 = $this->shareManager->newShare(); |
|
| 1183 | + $share1->setSharedWith('[email protected]') |
|
| 1184 | + ->setSharedBy($u1->getUID()) |
|
| 1185 | + ->setShareOwner($u1->getUID()) |
|
| 1186 | + ->setPermissions(Constants::PERMISSION_READ) |
|
| 1187 | + ->setNode($file1); |
|
| 1188 | + $provider->create($share1); |
|
| 1189 | + |
|
| 1190 | + $share2 = $this->shareManager->newShare(); |
|
| 1191 | + $share2->setSharedWith('[email protected]') |
|
| 1192 | + ->setSharedBy($u2->getUID()) |
|
| 1193 | + ->setShareOwner($u1->getUID()) |
|
| 1194 | + ->setPermissions(Constants::PERMISSION_READ) |
|
| 1195 | + ->setNode($file2); |
|
| 1196 | + $provider->create($share2); |
|
| 1197 | + |
|
| 1198 | + $result = $provider->getSharesInFolder($u1->getUID(), $folder1, false); |
|
| 1199 | + $this->assertCount(1, $result); |
|
| 1200 | + $this->assertCount(1, $result[$file1->getId()]); |
|
| 1201 | + |
|
| 1202 | + $result = $provider->getSharesInFolder($u1->getUID(), $folder1, true); |
|
| 1203 | + $this->assertCount(2, $result); |
|
| 1204 | + $this->assertCount(1, $result[$file1->getId()]); |
|
| 1205 | + $this->assertCount(1, $result[$file2->getId()]); |
|
| 1206 | + |
|
| 1207 | + $u1->delete(); |
|
| 1208 | + $u2->delete(); |
|
| 1209 | + } |
|
| 1210 | + |
|
| 1211 | + public function testGetAccessList(): void { |
|
| 1212 | + $userManager = Server::get(IUserManager::class); |
|
| 1213 | + $rootFolder = Server::get(IRootFolder::class); |
|
| 1214 | + |
|
| 1215 | + $this->shareManager->expects($this->any()) |
|
| 1216 | + ->method('newShare') |
|
| 1217 | + ->willReturn(new Share($rootFolder, $userManager)); |
|
| 1218 | + |
|
| 1219 | + $provider = $this->getInstance(['sendMailNotification', 'createShareActivity']); |
|
| 1220 | + |
|
| 1221 | + $u1 = $userManager->createUser('testFed', md5((string)time())); |
|
| 1222 | + $u2 = $userManager->createUser('testFed2', md5((string)time())); |
|
| 1223 | + |
|
| 1224 | + $folder = $rootFolder->getUserFolder($u1->getUID())->newFolder('foo'); |
|
| 1225 | + |
|
| 1226 | + $accessList = $provider->getAccessList([$folder], true); |
|
| 1227 | + $this->assertArrayHasKey('public', $accessList); |
|
| 1228 | + $this->assertFalse($accessList['public']); |
|
| 1229 | + $accessList = $provider->getAccessList([$folder], false); |
|
| 1230 | + $this->assertArrayHasKey('public', $accessList); |
|
| 1231 | + $this->assertFalse($accessList['public']); |
|
| 1232 | + |
|
| 1233 | + $share1 = $this->shareManager->newShare(); |
|
| 1234 | + $share1->setSharedWith('[email protected]') |
|
| 1235 | + ->setSharedBy($u1->getUID()) |
|
| 1236 | + ->setShareOwner($u1->getUID()) |
|
| 1237 | + ->setPermissions(Constants::PERMISSION_READ) |
|
| 1238 | + ->setNode($folder); |
|
| 1239 | + $share1 = $provider->create($share1); |
|
| 1240 | + |
|
| 1241 | + $share2 = $this->shareManager->newShare(); |
|
| 1242 | + $share2->setSharedWith('[email protected]') |
|
| 1243 | + ->setSharedBy($u2->getUID()) |
|
| 1244 | + ->setShareOwner($u1->getUID()) |
|
| 1245 | + ->setPermissions(Constants::PERMISSION_READ) |
|
| 1246 | + ->setNode($folder); |
|
| 1247 | + $share2 = $provider->create($share2); |
|
| 1248 | + |
|
| 1249 | + $accessList = $provider->getAccessList([$folder], true); |
|
| 1250 | + $this->assertArrayHasKey('public', $accessList); |
|
| 1251 | + $this->assertTrue($accessList['public']); |
|
| 1252 | + $accessList = $provider->getAccessList([$folder], false); |
|
| 1253 | + $this->assertArrayHasKey('public', $accessList); |
|
| 1254 | + $this->assertTrue($accessList['public']); |
|
| 1255 | + |
|
| 1256 | + $provider->delete($share2); |
|
| 1257 | + |
|
| 1258 | + $accessList = $provider->getAccessList([$folder], true); |
|
| 1259 | + $this->assertArrayHasKey('public', $accessList); |
|
| 1260 | + $this->assertTrue($accessList['public']); |
|
| 1261 | + $accessList = $provider->getAccessList([$folder], false); |
|
| 1262 | + $this->assertArrayHasKey('public', $accessList); |
|
| 1263 | + $this->assertTrue($accessList['public']); |
|
| 1264 | + |
|
| 1265 | + $provider->delete($share1); |
|
| 1266 | + |
|
| 1267 | + $accessList = $provider->getAccessList([$folder], true); |
|
| 1268 | + $this->assertArrayHasKey('public', $accessList); |
|
| 1269 | + $this->assertFalse($accessList['public']); |
|
| 1270 | + $accessList = $provider->getAccessList([$folder], false); |
|
| 1271 | + $this->assertArrayHasKey('public', $accessList); |
|
| 1272 | + $this->assertFalse($accessList['public']); |
|
| 1273 | + |
|
| 1274 | + $u1->delete(); |
|
| 1275 | + $u2->delete(); |
|
| 1276 | + } |
|
| 1277 | + |
|
| 1278 | + public function testSendMailNotificationWithSameUserAndUserEmail(): void { |
|
| 1279 | + $provider = $this->getInstance(); |
|
| 1280 | + $user = $this->createMock(IUser::class); |
|
| 1281 | + $this->settingsManager->expects($this->any())->method('replyToInitiator')->willReturn(true); |
|
| 1282 | + $this->userManager |
|
| 1283 | + ->expects($this->once()) |
|
| 1284 | + ->method('get') |
|
| 1285 | + ->with('OwnerUser') |
|
| 1286 | + ->willReturn($user); |
|
| 1287 | + $user |
|
| 1288 | + ->expects($this->once()) |
|
| 1289 | + ->method('getDisplayName') |
|
| 1290 | + ->willReturn('Mrs. Owner User'); |
|
| 1291 | + $message = $this->createMock(Message::class); |
|
| 1292 | + $this->mailer |
|
| 1293 | + ->expects($this->once()) |
|
| 1294 | + ->method('createMessage') |
|
| 1295 | + ->willReturn($message); |
|
| 1296 | + $template = $this->createMock(IEMailTemplate::class); |
|
| 1297 | + $this->mailer |
|
| 1298 | + ->expects($this->once()) |
|
| 1299 | + ->method('createEMailTemplate') |
|
| 1300 | + ->willReturn($template); |
|
| 1301 | + $template |
|
| 1302 | + ->expects($this->once()) |
|
| 1303 | + ->method('addHeader'); |
|
| 1304 | + $template |
|
| 1305 | + ->expects($this->once()) |
|
| 1306 | + ->method('addHeading') |
|
| 1307 | + ->with('Mrs. Owner User shared file.txt with you'); |
|
| 1308 | + $template |
|
| 1309 | + ->expects($this->once()) |
|
| 1310 | + ->method('addBodyButton') |
|
| 1311 | + ->with( |
|
| 1312 | + 'Open file.txt', |
|
| 1313 | + 'https://example.com/file.txt' |
|
| 1314 | + ); |
|
| 1315 | + $message |
|
| 1316 | + ->expects($this->once()) |
|
| 1317 | + ->method('setTo') |
|
| 1318 | + ->with(['[email protected]']); |
|
| 1319 | + $this->defaults |
|
| 1320 | + ->expects($this->once()) |
|
| 1321 | + ->method('getName') |
|
| 1322 | + ->willReturn('UnitTestCloud'); |
|
| 1323 | + $message |
|
| 1324 | + ->expects($this->once()) |
|
| 1325 | + ->method('setFrom') |
|
| 1326 | + ->with([ |
|
| 1327 | + Util::getDefaultEmailAddress('UnitTestCloud') => 'Mrs. Owner User via UnitTestCloud' |
|
| 1328 | + ]); |
|
| 1329 | + $user |
|
| 1330 | + ->expects($this->once()) |
|
| 1331 | + ->method('getEMailAddress') |
|
| 1332 | + ->willReturn('[email protected]'); |
|
| 1333 | + $message |
|
| 1334 | + ->expects($this->once()) |
|
| 1335 | + ->method('setReplyTo') |
|
| 1336 | + ->with(['[email protected]' => 'Mrs. Owner User']); |
|
| 1337 | + $this->defaults |
|
| 1338 | + ->expects($this->exactly(2)) |
|
| 1339 | + ->method('getSlogan') |
|
| 1340 | + ->willReturn('Testing like 1990'); |
|
| 1341 | + $template |
|
| 1342 | + ->expects($this->once()) |
|
| 1343 | + ->method('addFooter') |
|
| 1344 | + ->with('UnitTestCloud - Testing like 1990'); |
|
| 1345 | + $template |
|
| 1346 | + ->expects($this->once()) |
|
| 1347 | + ->method('setSubject') |
|
| 1348 | + ->with('Mrs. Owner User shared file.txt with you'); |
|
| 1349 | + $message |
|
| 1350 | + ->expects($this->once()) |
|
| 1351 | + ->method('useTemplate') |
|
| 1352 | + ->with($template); |
|
| 1353 | + |
|
| 1354 | + $this->mailer |
|
| 1355 | + ->expects($this->once()) |
|
| 1356 | + ->method('send') |
|
| 1357 | + ->with($message); |
|
| 1358 | + |
|
| 1359 | + $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 1360 | + ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 1361 | + ->willReturn('https://example.com/file.txt'); |
|
| 1362 | + |
|
| 1363 | + $node = $this->createMock(File::class); |
|
| 1364 | + $node->expects($this->any())->method('getName')->willReturn('file.txt'); |
|
| 1365 | + |
|
| 1366 | + $share = $this->createMock(IShare::class); |
|
| 1367 | + $share->expects($this->any())->method('getSharedBy')->willReturn('OwnerUser'); |
|
| 1368 | + $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 1369 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 1370 | + $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 1371 | + $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 1372 | + $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 1373 | + |
|
| 1374 | + self::invokePrivate( |
|
| 1375 | + $provider, |
|
| 1376 | + 'sendMailNotification', |
|
| 1377 | + [$share] |
|
| 1378 | + ); |
|
| 1379 | + } |
|
| 1380 | + |
|
| 1381 | + public function testSendMailNotificationWithSameUserAndUserEmailAndNote(): void { |
|
| 1382 | + $provider = $this->getInstance(); |
|
| 1383 | + $user = $this->createMock(IUser::class); |
|
| 1384 | + $this->settingsManager->expects($this->any())->method('replyToInitiator')->willReturn(true); |
|
| 1385 | + $this->userManager |
|
| 1386 | + ->expects($this->once()) |
|
| 1387 | + ->method('get') |
|
| 1388 | + ->with('OwnerUser') |
|
| 1389 | + ->willReturn($user); |
|
| 1390 | + $user |
|
| 1391 | + ->expects($this->once()) |
|
| 1392 | + ->method('getDisplayName') |
|
| 1393 | + ->willReturn('Mrs. Owner User'); |
|
| 1394 | + $message = $this->createMock(Message::class); |
|
| 1395 | + $this->mailer |
|
| 1396 | + ->expects($this->once()) |
|
| 1397 | + ->method('createMessage') |
|
| 1398 | + ->willReturn($message); |
|
| 1399 | + $template = $this->createMock(IEMailTemplate::class); |
|
| 1400 | + $this->mailer |
|
| 1401 | + ->expects($this->once()) |
|
| 1402 | + ->method('createEMailTemplate') |
|
| 1403 | + ->willReturn($template); |
|
| 1404 | + $template |
|
| 1405 | + ->expects($this->once()) |
|
| 1406 | + ->method('addHeader'); |
|
| 1407 | + $template |
|
| 1408 | + ->expects($this->once()) |
|
| 1409 | + ->method('addHeading') |
|
| 1410 | + ->with('Mrs. Owner User shared file.txt with you'); |
|
| 1411 | + |
|
| 1412 | + $this->urlGenerator->expects($this->once())->method('imagePath') |
|
| 1413 | + ->with('core', 'caldav/description.png') |
|
| 1414 | + ->willReturn('core/img/caldav/description.png'); |
|
| 1415 | + $this->urlGenerator->expects($this->once())->method('getAbsoluteURL') |
|
| 1416 | + ->with('core/img/caldav/description.png') |
|
| 1417 | + ->willReturn('https://example.com/core/img/caldav/description.png'); |
|
| 1418 | + $template |
|
| 1419 | + ->expects($this->once()) |
|
| 1420 | + ->method('addBodyListItem') |
|
| 1421 | + ->with( |
|
| 1422 | + 'This is a note to the recipient', |
|
| 1423 | + 'Note:', |
|
| 1424 | + 'https://example.com/core/img/caldav/description.png', |
|
| 1425 | + 'This is a note to the recipient' |
|
| 1426 | + ); |
|
| 1427 | + $template |
|
| 1428 | + ->expects($this->once()) |
|
| 1429 | + ->method('addBodyButton') |
|
| 1430 | + ->with( |
|
| 1431 | + 'Open file.txt', |
|
| 1432 | + 'https://example.com/file.txt' |
|
| 1433 | + ); |
|
| 1434 | + $message |
|
| 1435 | + ->expects($this->once()) |
|
| 1436 | + ->method('setTo') |
|
| 1437 | + ->with(['[email protected]']); |
|
| 1438 | + $this->defaults |
|
| 1439 | + ->expects($this->once()) |
|
| 1440 | + ->method('getName') |
|
| 1441 | + ->willReturn('UnitTestCloud'); |
|
| 1442 | + $message |
|
| 1443 | + ->expects($this->once()) |
|
| 1444 | + ->method('setFrom') |
|
| 1445 | + ->with([ |
|
| 1446 | + Util::getDefaultEmailAddress('UnitTestCloud') => 'Mrs. Owner User via UnitTestCloud' |
|
| 1447 | + ]); |
|
| 1448 | + $user |
|
| 1449 | + ->expects($this->once()) |
|
| 1450 | + ->method('getEMailAddress') |
|
| 1451 | + ->willReturn('[email protected]'); |
|
| 1452 | + $message |
|
| 1453 | + ->expects($this->once()) |
|
| 1454 | + ->method('setReplyTo') |
|
| 1455 | + ->with(['[email protected]' => 'Mrs. Owner User']); |
|
| 1456 | + $this->defaults |
|
| 1457 | + ->expects($this->exactly(2)) |
|
| 1458 | + ->method('getSlogan') |
|
| 1459 | + ->willReturn('Testing like 1990'); |
|
| 1460 | + $template |
|
| 1461 | + ->expects($this->once()) |
|
| 1462 | + ->method('addFooter') |
|
| 1463 | + ->with('UnitTestCloud - Testing like 1990'); |
|
| 1464 | + $template |
|
| 1465 | + ->expects($this->once()) |
|
| 1466 | + ->method('setSubject') |
|
| 1467 | + ->with('Mrs. Owner User shared file.txt with you'); |
|
| 1468 | + $message |
|
| 1469 | + ->expects($this->once()) |
|
| 1470 | + ->method('useTemplate') |
|
| 1471 | + ->with($template); |
|
| 1472 | + |
|
| 1473 | + $this->mailer |
|
| 1474 | + ->expects($this->once()) |
|
| 1475 | + ->method('send') |
|
| 1476 | + ->with($message); |
|
| 1477 | + |
|
| 1478 | + $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 1479 | + ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 1480 | + ->willReturn('https://example.com/file.txt'); |
|
| 1481 | + |
|
| 1482 | + $node = $this->createMock(File::class); |
|
| 1483 | + $node->expects($this->any())->method('getName')->willReturn('file.txt'); |
|
| 1484 | + |
|
| 1485 | + $share = $this->createMock(IShare::class); |
|
| 1486 | + $share->expects($this->any())->method('getSharedBy')->willReturn('OwnerUser'); |
|
| 1487 | + $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 1488 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 1489 | + $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 1490 | + $share->expects($this->any())->method('getNote')->willReturn('This is a note to the recipient'); |
|
| 1491 | + $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 1492 | + |
|
| 1493 | + self::invokePrivate( |
|
| 1494 | + $provider, |
|
| 1495 | + 'sendMailNotification', |
|
| 1496 | + [$share] |
|
| 1497 | + ); |
|
| 1498 | + } |
|
| 1499 | + |
|
| 1500 | + public function testSendMailNotificationWithSameUserAndUserEmailAndExpiration(): void { |
|
| 1501 | + $provider = $this->getInstance(); |
|
| 1502 | + $user = $this->createMock(IUser::class); |
|
| 1503 | + $this->settingsManager->expects($this->any())->method('replyToInitiator')->willReturn(true); |
|
| 1504 | + $this->userManager |
|
| 1505 | + ->expects($this->once()) |
|
| 1506 | + ->method('get') |
|
| 1507 | + ->with('OwnerUser') |
|
| 1508 | + ->willReturn($user); |
|
| 1509 | + $user |
|
| 1510 | + ->expects($this->once()) |
|
| 1511 | + ->method('getDisplayName') |
|
| 1512 | + ->willReturn('Mrs. Owner User'); |
|
| 1513 | + $message = $this->createMock(Message::class); |
|
| 1514 | + $this->mailer |
|
| 1515 | + ->expects($this->once()) |
|
| 1516 | + ->method('createMessage') |
|
| 1517 | + ->willReturn($message); |
|
| 1518 | + $template = $this->createMock(IEMailTemplate::class); |
|
| 1519 | + $this->mailer |
|
| 1520 | + ->expects($this->once()) |
|
| 1521 | + ->method('createEMailTemplate') |
|
| 1522 | + ->willReturn($template); |
|
| 1523 | + $template |
|
| 1524 | + ->expects($this->once()) |
|
| 1525 | + ->method('addHeader'); |
|
| 1526 | + $template |
|
| 1527 | + ->expects($this->once()) |
|
| 1528 | + ->method('addHeading') |
|
| 1529 | + ->with('Mrs. Owner User shared file.txt with you'); |
|
| 1530 | + |
|
| 1531 | + $expiration = new DateTime('2001-01-01'); |
|
| 1532 | + $this->l->expects($this->once()) |
|
| 1533 | + ->method('l') |
|
| 1534 | + ->with('date', $expiration, ['width' => 'medium']) |
|
| 1535 | + ->willReturn('2001-01-01'); |
|
| 1536 | + $this->urlGenerator->expects($this->once())->method('imagePath') |
|
| 1537 | + ->with('core', 'caldav/time.png') |
|
| 1538 | + ->willReturn('core/img/caldav/time.png'); |
|
| 1539 | + $this->urlGenerator->expects($this->once())->method('getAbsoluteURL') |
|
| 1540 | + ->with('core/img/caldav/time.png') |
|
| 1541 | + ->willReturn('https://example.com/core/img/caldav/time.png'); |
|
| 1542 | + $template |
|
| 1543 | + ->expects($this->once()) |
|
| 1544 | + ->method('addBodyListItem') |
|
| 1545 | + ->with( |
|
| 1546 | + 'This share is valid until 2001-01-01 at midnight', |
|
| 1547 | + 'Expiration:', |
|
| 1548 | + 'https://example.com/core/img/caldav/time.png', |
|
| 1549 | + ); |
|
| 1550 | + |
|
| 1551 | + $template |
|
| 1552 | + ->expects($this->once()) |
|
| 1553 | + ->method('addBodyButton') |
|
| 1554 | + ->with( |
|
| 1555 | + 'Open file.txt', |
|
| 1556 | + 'https://example.com/file.txt' |
|
| 1557 | + ); |
|
| 1558 | + $message |
|
| 1559 | + ->expects($this->once()) |
|
| 1560 | + ->method('setTo') |
|
| 1561 | + ->with(['[email protected]']); |
|
| 1562 | + $this->defaults |
|
| 1563 | + ->expects($this->once()) |
|
| 1564 | + ->method('getName') |
|
| 1565 | + ->willReturn('UnitTestCloud'); |
|
| 1566 | + $message |
|
| 1567 | + ->expects($this->once()) |
|
| 1568 | + ->method('setFrom') |
|
| 1569 | + ->with([ |
|
| 1570 | + Util::getDefaultEmailAddress('UnitTestCloud') => 'Mrs. Owner User via UnitTestCloud' |
|
| 1571 | + ]); |
|
| 1572 | + $user |
|
| 1573 | + ->expects($this->once()) |
|
| 1574 | + ->method('getEMailAddress') |
|
| 1575 | + ->willReturn('[email protected]'); |
|
| 1576 | + $message |
|
| 1577 | + ->expects($this->once()) |
|
| 1578 | + ->method('setReplyTo') |
|
| 1579 | + ->with(['[email protected]' => 'Mrs. Owner User']); |
|
| 1580 | + $this->defaults |
|
| 1581 | + ->expects($this->exactly(2)) |
|
| 1582 | + ->method('getSlogan') |
|
| 1583 | + ->willReturn('Testing like 1990'); |
|
| 1584 | + $template |
|
| 1585 | + ->expects($this->once()) |
|
| 1586 | + ->method('addFooter') |
|
| 1587 | + ->with('UnitTestCloud - Testing like 1990'); |
|
| 1588 | + $template |
|
| 1589 | + ->expects($this->once()) |
|
| 1590 | + ->method('setSubject') |
|
| 1591 | + ->with('Mrs. Owner User shared file.txt with you'); |
|
| 1592 | + $message |
|
| 1593 | + ->expects($this->once()) |
|
| 1594 | + ->method('useTemplate') |
|
| 1595 | + ->with($template); |
|
| 1596 | + |
|
| 1597 | + $this->mailer |
|
| 1598 | + ->expects($this->once()) |
|
| 1599 | + ->method('send') |
|
| 1600 | + ->with($message); |
|
| 1601 | + |
|
| 1602 | + $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 1603 | + ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 1604 | + ->willReturn('https://example.com/file.txt'); |
|
| 1605 | + |
|
| 1606 | + $node = $this->createMock(File::class); |
|
| 1607 | + $node->expects($this->any())->method('getName')->willReturn('file.txt'); |
|
| 1608 | + |
|
| 1609 | + $share = $this->createMock(IShare::class); |
|
| 1610 | + $share->expects($this->any())->method('getSharedBy')->willReturn('OwnerUser'); |
|
| 1611 | + $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 1612 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 1613 | + $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 1614 | + $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 1615 | + $share->expects($this->any())->method('getExpirationDate')->willReturn($expiration); |
|
| 1616 | + $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 1617 | + |
|
| 1618 | + self::invokePrivate( |
|
| 1619 | + $provider, |
|
| 1620 | + 'sendMailNotification', |
|
| 1621 | + [$share] |
|
| 1622 | + ); |
|
| 1623 | + } |
|
| 1624 | + |
|
| 1625 | + public function testSendMailNotificationWithDifferentUserAndNoUserEmail(): void { |
|
| 1626 | + $provider = $this->getInstance(); |
|
| 1627 | + $initiatorUser = $this->createMock(IUser::class); |
|
| 1628 | + $this->settingsManager->expects($this->any())->method('replyToInitiator')->willReturn(true); |
|
| 1629 | + $this->userManager |
|
| 1630 | + ->expects($this->once()) |
|
| 1631 | + ->method('get') |
|
| 1632 | + ->with('InitiatorUser') |
|
| 1633 | + ->willReturn($initiatorUser); |
|
| 1634 | + $initiatorUser |
|
| 1635 | + ->expects($this->once()) |
|
| 1636 | + ->method('getDisplayName') |
|
| 1637 | + ->willReturn('Mr. Initiator User'); |
|
| 1638 | + $message = $this->createMock(Message::class); |
|
| 1639 | + $this->mailer |
|
| 1640 | + ->expects($this->once()) |
|
| 1641 | + ->method('createMessage') |
|
| 1642 | + ->willReturn($message); |
|
| 1643 | + $template = $this->createMock(IEMailTemplate::class); |
|
| 1644 | + $this->mailer |
|
| 1645 | + ->expects($this->once()) |
|
| 1646 | + ->method('createEMailTemplate') |
|
| 1647 | + ->willReturn($template); |
|
| 1648 | + $template |
|
| 1649 | + ->expects($this->once()) |
|
| 1650 | + ->method('addHeader'); |
|
| 1651 | + $template |
|
| 1652 | + ->expects($this->once()) |
|
| 1653 | + ->method('addHeading') |
|
| 1654 | + ->with('Mr. Initiator User shared file.txt with you'); |
|
| 1655 | + $template |
|
| 1656 | + ->expects($this->once()) |
|
| 1657 | + ->method('addBodyButton') |
|
| 1658 | + ->with( |
|
| 1659 | + 'Open file.txt', |
|
| 1660 | + 'https://example.com/file.txt' |
|
| 1661 | + ); |
|
| 1662 | + $message |
|
| 1663 | + ->expects($this->once()) |
|
| 1664 | + ->method('setTo') |
|
| 1665 | + ->with(['[email protected]']); |
|
| 1666 | + $this->defaults |
|
| 1667 | + ->expects($this->once()) |
|
| 1668 | + ->method('getName') |
|
| 1669 | + ->willReturn('UnitTestCloud'); |
|
| 1670 | + $message |
|
| 1671 | + ->expects($this->once()) |
|
| 1672 | + ->method('setFrom') |
|
| 1673 | + ->with([ |
|
| 1674 | + Util::getDefaultEmailAddress('UnitTestCloud') => 'Mr. Initiator User via UnitTestCloud' |
|
| 1675 | + ]); |
|
| 1676 | + $message |
|
| 1677 | + ->expects($this->never()) |
|
| 1678 | + ->method('setReplyTo'); |
|
| 1679 | + $template |
|
| 1680 | + ->expects($this->once()) |
|
| 1681 | + ->method('addFooter') |
|
| 1682 | + ->with(''); |
|
| 1683 | + $template |
|
| 1684 | + ->expects($this->once()) |
|
| 1685 | + ->method('setSubject') |
|
| 1686 | + ->with('Mr. Initiator User shared file.txt with you'); |
|
| 1687 | + $message |
|
| 1688 | + ->expects($this->once()) |
|
| 1689 | + ->method('useTemplate') |
|
| 1690 | + ->with($template); |
|
| 1691 | + |
|
| 1692 | + $this->mailer |
|
| 1693 | + ->expects($this->once()) |
|
| 1694 | + ->method('send') |
|
| 1695 | + ->with($message); |
|
| 1696 | + |
|
| 1697 | + $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 1698 | + ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 1699 | + ->willReturn('https://example.com/file.txt'); |
|
| 1700 | + |
|
| 1701 | + $node = $this->createMock(File::class); |
|
| 1702 | + $node->expects($this->any())->method('getName')->willReturn('file.txt'); |
|
| 1703 | + |
|
| 1704 | + $share = $this->createMock(IShare::class); |
|
| 1705 | + $share->expects($this->any())->method('getSharedBy')->willReturn('InitiatorUser'); |
|
| 1706 | + $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 1707 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 1708 | + $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 1709 | + $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 1710 | + $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 1711 | + |
|
| 1712 | + self::invokePrivate( |
|
| 1713 | + $provider, |
|
| 1714 | + 'sendMailNotification', |
|
| 1715 | + [$share] |
|
| 1716 | + ); |
|
| 1717 | + } |
|
| 1718 | + |
|
| 1719 | + public function testSendMailNotificationWithSameUserAndUserEmailAndReplyToDesactivate(): void { |
|
| 1720 | + $provider = $this->getInstance(); |
|
| 1721 | + $user = $this->createMock(IUser::class); |
|
| 1722 | + $this->settingsManager->expects($this->any())->method('replyToInitiator')->willReturn(false); |
|
| 1723 | + $this->userManager |
|
| 1724 | + ->expects($this->once()) |
|
| 1725 | + ->method('get') |
|
| 1726 | + ->with('OwnerUser') |
|
| 1727 | + ->willReturn($user); |
|
| 1728 | + $user |
|
| 1729 | + ->expects($this->once()) |
|
| 1730 | + ->method('getDisplayName') |
|
| 1731 | + ->willReturn('Mrs. Owner User'); |
|
| 1732 | + $message = $this->createMock(Message::class); |
|
| 1733 | + $this->mailer |
|
| 1734 | + ->expects($this->once()) |
|
| 1735 | + ->method('createMessage') |
|
| 1736 | + ->willReturn($message); |
|
| 1737 | + $template = $this->createMock(IEMailTemplate::class); |
|
| 1738 | + $this->mailer |
|
| 1739 | + ->expects($this->once()) |
|
| 1740 | + ->method('createEMailTemplate') |
|
| 1741 | + ->willReturn($template); |
|
| 1742 | + $template |
|
| 1743 | + ->expects($this->once()) |
|
| 1744 | + ->method('addHeader'); |
|
| 1745 | + $template |
|
| 1746 | + ->expects($this->once()) |
|
| 1747 | + ->method('addHeading') |
|
| 1748 | + ->with('Mrs. Owner User shared file.txt with you'); |
|
| 1749 | + $template |
|
| 1750 | + ->expects($this->once()) |
|
| 1751 | + ->method('addBodyButton') |
|
| 1752 | + ->with( |
|
| 1753 | + 'Open file.txt', |
|
| 1754 | + 'https://example.com/file.txt' |
|
| 1755 | + ); |
|
| 1756 | + $message |
|
| 1757 | + ->expects($this->once()) |
|
| 1758 | + ->method('setTo') |
|
| 1759 | + ->with(['[email protected]']); |
|
| 1760 | + $this->defaults |
|
| 1761 | + ->expects($this->once()) |
|
| 1762 | + ->method('getName') |
|
| 1763 | + ->willReturn('UnitTestCloud'); |
|
| 1764 | + $message |
|
| 1765 | + ->expects($this->once()) |
|
| 1766 | + ->method('setFrom') |
|
| 1767 | + ->with([ |
|
| 1768 | + Util::getDefaultEmailAddress('UnitTestCloud') => 'UnitTestCloud' |
|
| 1769 | + ]); |
|
| 1770 | + // Since replyToInitiator is false, we never get the initiator email address |
|
| 1771 | + $user |
|
| 1772 | + ->expects($this->never()) |
|
| 1773 | + ->method('getEMailAddress'); |
|
| 1774 | + $message |
|
| 1775 | + ->expects($this->never()) |
|
| 1776 | + ->method('setReplyTo'); |
|
| 1777 | + $template |
|
| 1778 | + ->expects($this->once()) |
|
| 1779 | + ->method('addFooter') |
|
| 1780 | + ->with(''); |
|
| 1781 | + $template |
|
| 1782 | + ->expects($this->once()) |
|
| 1783 | + ->method('setSubject') |
|
| 1784 | + ->with('Mrs. Owner User shared file.txt with you'); |
|
| 1785 | + $message |
|
| 1786 | + ->expects($this->once()) |
|
| 1787 | + ->method('useTemplate') |
|
| 1788 | + ->with($template); |
|
| 1789 | + |
|
| 1790 | + $this->mailer |
|
| 1791 | + ->expects($this->once()) |
|
| 1792 | + ->method('send') |
|
| 1793 | + ->with($message); |
|
| 1794 | + |
|
| 1795 | + $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 1796 | + ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 1797 | + ->willReturn('https://example.com/file.txt'); |
|
| 1798 | + |
|
| 1799 | + $node = $this->createMock(File::class); |
|
| 1800 | + $node->expects($this->any())->method('getName')->willReturn('file.txt'); |
|
| 1801 | + |
|
| 1802 | + $share = $this->createMock(IShare::class); |
|
| 1803 | + $share->expects($this->any())->method('getSharedBy')->willReturn('OwnerUser'); |
|
| 1804 | + $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 1805 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 1806 | + $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 1807 | + $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 1808 | + $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 1809 | + |
|
| 1810 | + self::invokePrivate( |
|
| 1811 | + $provider, |
|
| 1812 | + 'sendMailNotification', |
|
| 1813 | + [$share] |
|
| 1814 | + ); |
|
| 1815 | + } |
|
| 1816 | + |
|
| 1817 | + public function testSendMailNotificationWithDifferentUserAndNoUserEmailAndReplyToDesactivate(): void { |
|
| 1818 | + $provider = $this->getInstance(); |
|
| 1819 | + $initiatorUser = $this->createMock(IUser::class); |
|
| 1820 | + $this->settingsManager->expects($this->any())->method('replyToInitiator')->willReturn(false); |
|
| 1821 | + $this->userManager |
|
| 1822 | + ->expects($this->once()) |
|
| 1823 | + ->method('get') |
|
| 1824 | + ->with('InitiatorUser') |
|
| 1825 | + ->willReturn($initiatorUser); |
|
| 1826 | + $initiatorUser |
|
| 1827 | + ->expects($this->once()) |
|
| 1828 | + ->method('getDisplayName') |
|
| 1829 | + ->willReturn('Mr. Initiator User'); |
|
| 1830 | + $message = $this->createMock(Message::class); |
|
| 1831 | + $this->mailer |
|
| 1832 | + ->expects($this->once()) |
|
| 1833 | + ->method('createMessage') |
|
| 1834 | + ->willReturn($message); |
|
| 1835 | + $template = $this->createMock(IEMailTemplate::class); |
|
| 1836 | + $this->mailer |
|
| 1837 | + ->expects($this->once()) |
|
| 1838 | + ->method('createEMailTemplate') |
|
| 1839 | + ->willReturn($template); |
|
| 1840 | + $template |
|
| 1841 | + ->expects($this->once()) |
|
| 1842 | + ->method('addHeader'); |
|
| 1843 | + $template |
|
| 1844 | + ->expects($this->once()) |
|
| 1845 | + ->method('addHeading') |
|
| 1846 | + ->with('Mr. Initiator User shared file.txt with you'); |
|
| 1847 | + $template |
|
| 1848 | + ->expects($this->once()) |
|
| 1849 | + ->method('addBodyButton') |
|
| 1850 | + ->with( |
|
| 1851 | + 'Open file.txt', |
|
| 1852 | + 'https://example.com/file.txt' |
|
| 1853 | + ); |
|
| 1854 | + $message |
|
| 1855 | + ->expects($this->once()) |
|
| 1856 | + ->method('setTo') |
|
| 1857 | + ->with(['[email protected]']); |
|
| 1858 | + $this->defaults |
|
| 1859 | + ->expects($this->once()) |
|
| 1860 | + ->method('getName') |
|
| 1861 | + ->willReturn('UnitTestCloud'); |
|
| 1862 | + $message |
|
| 1863 | + ->expects($this->once()) |
|
| 1864 | + ->method('setFrom') |
|
| 1865 | + ->with([ |
|
| 1866 | + Util::getDefaultEmailAddress('UnitTestCloud') => 'UnitTestCloud' |
|
| 1867 | + ]); |
|
| 1868 | + $message |
|
| 1869 | + ->expects($this->never()) |
|
| 1870 | + ->method('setReplyTo'); |
|
| 1871 | + $template |
|
| 1872 | + ->expects($this->once()) |
|
| 1873 | + ->method('addFooter') |
|
| 1874 | + ->with(''); |
|
| 1875 | + $template |
|
| 1876 | + ->expects($this->once()) |
|
| 1877 | + ->method('setSubject') |
|
| 1878 | + ->with('Mr. Initiator User shared file.txt with you'); |
|
| 1879 | + $message |
|
| 1880 | + ->expects($this->once()) |
|
| 1881 | + ->method('useTemplate') |
|
| 1882 | + ->with($template); |
|
| 1883 | + |
|
| 1884 | + $this->mailer |
|
| 1885 | + ->expects($this->once()) |
|
| 1886 | + ->method('send') |
|
| 1887 | + ->with($message); |
|
| 1888 | + |
|
| 1889 | + $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') |
|
| 1890 | + ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']) |
|
| 1891 | + ->willReturn('https://example.com/file.txt'); |
|
| 1892 | + |
|
| 1893 | + $node = $this->createMock(File::class); |
|
| 1894 | + $node->expects($this->any())->method('getName')->willReturn('file.txt'); |
|
| 1895 | + |
|
| 1896 | + $share = $this->createMock(IShare::class); |
|
| 1897 | + $share->expects($this->any())->method('getSharedBy')->willReturn('InitiatorUser'); |
|
| 1898 | + $share->expects($this->any())->method('getSharedWith')->willReturn('[email protected]'); |
|
| 1899 | + $share->expects($this->any())->method('getNode')->willReturn($node); |
|
| 1900 | + $share->expects($this->any())->method('getId')->willReturn(42); |
|
| 1901 | + $share->expects($this->any())->method('getNote')->willReturn(''); |
|
| 1902 | + $share->expects($this->any())->method('getToken')->willReturn('token'); |
|
| 1903 | + |
|
| 1904 | + self::invokePrivate( |
|
| 1905 | + $provider, |
|
| 1906 | + 'sendMailNotification', |
|
| 1907 | + [$share] |
|
| 1908 | + ); |
|
| 1909 | + } |
|
| 1910 | 1910 | } |
@@ -77,7 +77,7 @@ discard block |
||
| 77 | 77 | |
| 78 | 78 | $this->l = $this->createMock(IL10N::class); |
| 79 | 79 | $this->l->method('t') |
| 80 | - ->willReturnCallback(function ($text, $parameters = []) { |
|
| 80 | + ->willReturnCallback(function($text, $parameters = []) { |
|
| 81 | 81 | return vsprintf($text, $parameters); |
| 82 | 82 | }); |
| 83 | 83 | $this->config = $this->createMock(IConfig::class); |
@@ -393,7 +393,7 @@ discard block |
||
| 393 | 393 | ]; |
| 394 | 394 | $this->mailer->expects($this->exactly(2)) |
| 395 | 395 | ->method('createEMailTemplate') |
| 396 | - ->willReturnCallback(function () use (&$calls) { |
|
| 396 | + ->willReturnCallback(function() use (&$calls) { |
|
| 397 | 397 | $expected = array_shift($calls); |
| 398 | 398 | $this->assertEquals($expected, func_get_args()); |
| 399 | 399 | return $this->createMock(IEMailTemplate::class); |
@@ -475,7 +475,7 @@ discard block |
||
| 475 | 475 | ]; |
| 476 | 476 | $this->mailer->expects($this->exactly(2)) |
| 477 | 477 | ->method('createEMailTemplate') |
| 478 | - ->willReturnCallback(function () use (&$calls) { |
|
| 478 | + ->willReturnCallback(function() use (&$calls) { |
|
| 479 | 479 | $expected = array_shift($calls); |
| 480 | 480 | $this->assertEquals($expected, func_get_args()); |
| 481 | 481 | return $this->createMock(IEMailTemplate::class); |
@@ -540,7 +540,7 @@ discard block |
||
| 540 | 540 | ]; |
| 541 | 541 | $message->expects($this->exactly(2)) |
| 542 | 542 | ->method('setTo') |
| 543 | - ->willReturnCallback(function () use (&$setToCalls, $message) { |
|
| 543 | + ->willReturnCallback(function() use (&$setToCalls, $message) { |
|
| 544 | 544 | $expected = array_shift($setToCalls); |
| 545 | 545 | $this->assertEquals($expected, func_get_args()); |
| 546 | 546 | return $message; |
@@ -572,7 +572,7 @@ discard block |
||
| 572 | 572 | ]; |
| 573 | 573 | $this->mailer->expects($this->exactly(2)) |
| 574 | 574 | ->method('createEMailTemplate') |
| 575 | - ->willReturnCallback(function () use (&$calls) { |
|
| 575 | + ->willReturnCallback(function() use (&$calls) { |
|
| 576 | 576 | $expected = array_shift($calls); |
| 577 | 577 | $this->assertEquals($expected, func_get_args()); |
| 578 | 578 | return $this->createMock(IEMailTemplate::class); |
@@ -752,17 +752,17 @@ discard block |
||
| 752 | 752 | |
| 753 | 753 | $this->assertSame(1, count($result)); |
| 754 | 754 | |
| 755 | - $this->assertSame($itemSource, (int)$result[0]['item_source']); |
|
| 755 | + $this->assertSame($itemSource, (int) $result[0]['item_source']); |
|
| 756 | 756 | $this->assertSame($itemType, $result[0]['item_type']); |
| 757 | 757 | $this->assertSame($shareWith, $result[0]['share_with']); |
| 758 | 758 | $this->assertSame($sharedBy, $result[0]['uid_initiator']); |
| 759 | 759 | $this->assertSame($uidOwner, $result[0]['uid_owner']); |
| 760 | - $this->assertSame($permissions, (int)$result[0]['permissions']); |
|
| 760 | + $this->assertSame($permissions, (int) $result[0]['permissions']); |
|
| 761 | 761 | $this->assertSame($token, $result[0]['token']); |
| 762 | 762 | $this->assertSame($password, $result[0]['password']); |
| 763 | 763 | $this->assertSame($passwordExpirationTime->getTimestamp(), \DateTime::createFromFormat('Y-m-d H:i:s', $result[0]['password_expiration_time'])->getTimestamp()); |
| 764 | - $this->assertSame($sendPasswordByTalk, (bool)$result[0]['password_by_talk']); |
|
| 765 | - $this->assertSame($hideDownload, (bool)$result[0]['hide_download']); |
|
| 764 | + $this->assertSame($sendPasswordByTalk, (bool) $result[0]['password_by_talk']); |
|
| 765 | + $this->assertSame($hideDownload, (bool) $result[0]['hide_download']); |
|
| 766 | 766 | $this->assertSame($label, $result[0]['label']); |
| 767 | 767 | $this->assertSame($expiration->getTimestamp(), \DateTime::createFromFormat('Y-m-d H:i:s', $result[0]['expiration'])->getTimestamp()); |
| 768 | 768 | } |
@@ -805,12 +805,12 @@ discard block |
||
| 805 | 805 | |
| 806 | 806 | $this->assertSame(1, count($result)); |
| 807 | 807 | |
| 808 | - $this->assertSame($itemSource, (int)$result[0]['item_source']); |
|
| 808 | + $this->assertSame($itemSource, (int) $result[0]['item_source']); |
|
| 809 | 809 | $this->assertSame($itemType, $result[0]['item_type']); |
| 810 | 810 | $this->assertSame($shareWith, $result[0]['share_with']); |
| 811 | 811 | $this->assertSame($sharedBy, $result[0]['uid_initiator']); |
| 812 | 812 | $this->assertSame($uidOwner, $result[0]['uid_owner']); |
| 813 | - $this->assertSame($permissions + 1, (int)$result[0]['permissions']); |
|
| 813 | + $this->assertSame($permissions + 1, (int) $result[0]['permissions']); |
|
| 814 | 814 | $this->assertSame($token, $result[0]['token']); |
| 815 | 815 | $this->assertSame($note, $result[0]['note']); |
| 816 | 816 | } |
@@ -893,10 +893,10 @@ discard block |
||
| 893 | 893 | |
| 894 | 894 | $instance->expects($this->once())->method('createShareObject') |
| 895 | 895 | ->willReturnCallback( |
| 896 | - function ($data) use ($uidOwner, $sharedBy, $id2) { |
|
| 896 | + function($data) use ($uidOwner, $sharedBy, $id2) { |
|
| 897 | 897 | $this->assertSame($uidOwner, $data['uid_owner']); |
| 898 | 898 | $this->assertSame($sharedBy, $data['uid_initiator']); |
| 899 | - $this->assertSame($id2, (int)$data['id']); |
|
| 899 | + $this->assertSame($id2, (int) $data['id']); |
|
| 900 | 900 | return $this->share; |
| 901 | 901 | } |
| 902 | 902 | ); |
@@ -945,10 +945,10 @@ discard block |
||
| 945 | 945 | |
| 946 | 946 | $instance->expects($this->once())->method('createShareObject') |
| 947 | 947 | ->willReturnCallback( |
| 948 | - function ($data) use ($uidOwner, $sharedBy, $id) { |
|
| 948 | + function($data) use ($uidOwner, $sharedBy, $id) { |
|
| 949 | 949 | $this->assertSame($uidOwner, $data['uid_owner']); |
| 950 | 950 | $this->assertSame($sharedBy, $data['uid_initiator']); |
| 951 | - $this->assertSame($id, (int)$data['id']); |
|
| 951 | + $this->assertSame($id, (int) $data['id']); |
|
| 952 | 952 | return $this->share; |
| 953 | 953 | } |
| 954 | 954 | ); |
@@ -978,8 +978,8 @@ discard block |
||
| 978 | 978 | |
| 979 | 979 | $instance->expects($this->once())->method('createShareObject') |
| 980 | 980 | ->willReturnCallback( |
| 981 | - function ($data) use ($idMail) { |
|
| 982 | - $this->assertSame($idMail, (int)$data['id']); |
|
| 981 | + function($data) use ($idMail) { |
|
| 982 | + $this->assertSame($idMail, (int) $data['id']); |
|
| 983 | 983 | return $this->share; |
| 984 | 984 | } |
| 985 | 985 | ); |
@@ -1088,7 +1088,7 @@ discard block |
||
| 1088 | 1088 | |
| 1089 | 1089 | $this->assertTrue(is_array($after)); |
| 1090 | 1090 | $this->assertSame(1, count($after)); |
| 1091 | - $this->assertSame($id, (int)$after[0]['id']); |
|
| 1091 | + $this->assertSame($id, (int) $after[0]['id']); |
|
| 1092 | 1092 | } |
| 1093 | 1093 | |
| 1094 | 1094 | public function testGetRawShare(): void { |
@@ -1107,12 +1107,12 @@ discard block |
||
| 1107 | 1107 | $result = $this->invokePrivate($instance, 'getRawShare', [$id]); |
| 1108 | 1108 | |
| 1109 | 1109 | $this->assertTrue(is_array($result)); |
| 1110 | - $this->assertSame($itemSource, (int)$result['item_source']); |
|
| 1110 | + $this->assertSame($itemSource, (int) $result['item_source']); |
|
| 1111 | 1111 | $this->assertSame($itemType, $result['item_type']); |
| 1112 | 1112 | $this->assertSame($shareWith, $result['share_with']); |
| 1113 | 1113 | $this->assertSame($sharedBy, $result['uid_initiator']); |
| 1114 | 1114 | $this->assertSame($uidOwner, $result['uid_owner']); |
| 1115 | - $this->assertSame($permissions, (int)$result['permissions']); |
|
| 1115 | + $this->assertSame($permissions, (int) $result['permissions']); |
|
| 1116 | 1116 | $this->assertSame($token, $result['token']); |
| 1117 | 1117 | } |
| 1118 | 1118 | |
@@ -1159,7 +1159,7 @@ discard block |
||
| 1159 | 1159 | $qb->execute(); |
| 1160 | 1160 | $id = $qb->getLastInsertId(); |
| 1161 | 1161 | |
| 1162 | - return (int)$id; |
|
| 1162 | + return (int) $id; |
|
| 1163 | 1163 | } |
| 1164 | 1164 | |
| 1165 | 1165 | public function testGetSharesInFolder(): void { |
@@ -1172,8 +1172,8 @@ discard block |
||
| 1172 | 1172 | |
| 1173 | 1173 | $provider = $this->getInstance(['sendMailNotification', 'createShareActivity']); |
| 1174 | 1174 | |
| 1175 | - $u1 = $userManager->createUser('testFed', md5((string)time())); |
|
| 1176 | - $u2 = $userManager->createUser('testFed2', md5((string)time())); |
|
| 1175 | + $u1 = $userManager->createUser('testFed', md5((string) time())); |
|
| 1176 | + $u2 = $userManager->createUser('testFed2', md5((string) time())); |
|
| 1177 | 1177 | |
| 1178 | 1178 | $folder1 = $rootFolder->getUserFolder($u1->getUID())->newFolder('foo'); |
| 1179 | 1179 | $file1 = $folder1->newFile('bar1'); |
@@ -1218,8 +1218,8 @@ discard block |
||
| 1218 | 1218 | |
| 1219 | 1219 | $provider = $this->getInstance(['sendMailNotification', 'createShareActivity']); |
| 1220 | 1220 | |
| 1221 | - $u1 = $userManager->createUser('testFed', md5((string)time())); |
|
| 1222 | - $u2 = $userManager->createUser('testFed2', md5((string)time())); |
|
| 1221 | + $u1 = $userManager->createUser('testFed', md5((string) time())); |
|
| 1222 | + $u2 = $userManager->createUser('testFed2', md5((string) time())); |
|
| 1223 | 1223 | |
| 1224 | 1224 | $folder = $rootFolder->getUserFolder($u1->getUID())->newFolder('foo'); |
| 1225 | 1225 | |
@@ -105,340 +105,340 @@ |
||
| 105 | 105 | use SearchDAV\DAV\SearchPlugin; |
| 106 | 106 | |
| 107 | 107 | class Server { |
| 108 | - public Connector\Sabre\Server $server; |
|
| 109 | - private IProfiler $profiler; |
|
| 110 | - |
|
| 111 | - public function __construct( |
|
| 112 | - private IRequest $request, |
|
| 113 | - private string $baseUri, |
|
| 114 | - ) { |
|
| 115 | - $debugEnabled = \OCP\Server::get(IConfig::class)->getSystemValue('debug', false); |
|
| 116 | - $this->profiler = \OCP\Server::get(IProfiler::class); |
|
| 117 | - if ($this->profiler->isEnabled()) { |
|
| 118 | - /** @var IEventLogger $eventLogger */ |
|
| 119 | - $eventLogger = \OCP\Server::get(IEventLogger::class); |
|
| 120 | - $eventLogger->start('runtime', 'DAV Runtime'); |
|
| 121 | - } |
|
| 122 | - |
|
| 123 | - $logger = \OCP\Server::get(LoggerInterface::class); |
|
| 124 | - $eventDispatcher = \OCP\Server::get(IEventDispatcher::class); |
|
| 125 | - |
|
| 126 | - $root = new RootCollection(); |
|
| 127 | - $this->server = new \OCA\DAV\Connector\Sabre\Server(new CachingTree($root)); |
|
| 128 | - $this->server->setLogger($logger); |
|
| 129 | - |
|
| 130 | - // Add maintenance plugin |
|
| 131 | - $this->server->addPlugin(new MaintenancePlugin(\OCP\Server::get(IConfig::class), \OC::$server->getL10N('dav'))); |
|
| 132 | - |
|
| 133 | - $this->server->addPlugin(new AppleQuirksPlugin()); |
|
| 134 | - |
|
| 135 | - // Backends |
|
| 136 | - $authBackend = new Auth( |
|
| 137 | - \OCP\Server::get(ISession::class), |
|
| 138 | - \OCP\Server::get(IUserSession::class), |
|
| 139 | - \OCP\Server::get(IRequest::class), |
|
| 140 | - \OCP\Server::get(\OC\Authentication\TwoFactorAuth\Manager::class), |
|
| 141 | - \OCP\Server::get(IThrottler::class) |
|
| 142 | - ); |
|
| 143 | - |
|
| 144 | - // Set URL explicitly due to reverse-proxy situations |
|
| 145 | - $this->server->httpRequest->setUrl($this->request->getRequestUri()); |
|
| 146 | - $this->server->setBaseUri($this->baseUri); |
|
| 147 | - |
|
| 148 | - $this->server->addPlugin(new ProfilerPlugin($this->request)); |
|
| 149 | - $this->server->addPlugin(new BlockLegacyClientPlugin( |
|
| 150 | - \OCP\Server::get(IConfig::class), |
|
| 151 | - \OCP\Server::get(ThemingDefaults::class), |
|
| 152 | - )); |
|
| 153 | - $this->server->addPlugin(new AnonymousOptionsPlugin()); |
|
| 154 | - $authPlugin = new Plugin(); |
|
| 155 | - $authPlugin->addBackend(new PublicAuth()); |
|
| 156 | - $this->server->addPlugin($authPlugin); |
|
| 157 | - |
|
| 158 | - // allow setup of additional auth backends |
|
| 159 | - $event = new SabrePluginEvent($this->server); |
|
| 160 | - $eventDispatcher->dispatch('OCA\DAV\Connector\Sabre::authInit', $event); |
|
| 161 | - |
|
| 162 | - $newAuthEvent = new SabrePluginAuthInitEvent($this->server); |
|
| 163 | - $eventDispatcher->dispatchTyped($newAuthEvent); |
|
| 164 | - |
|
| 165 | - $bearerAuthBackend = new BearerAuth( |
|
| 166 | - \OCP\Server::get(IUserSession::class), |
|
| 167 | - \OCP\Server::get(ISession::class), |
|
| 168 | - \OCP\Server::get(IRequest::class), |
|
| 169 | - \OCP\Server::get(IConfig::class), |
|
| 170 | - ); |
|
| 171 | - $authPlugin->addBackend($bearerAuthBackend); |
|
| 172 | - // because we are throwing exceptions this plugin has to be the last one |
|
| 173 | - $authPlugin->addBackend($authBackend); |
|
| 174 | - |
|
| 175 | - // debugging |
|
| 176 | - if ($debugEnabled) { |
|
| 177 | - $this->server->debugEnabled = true; |
|
| 178 | - $this->server->addPlugin(new PropFindMonitorPlugin()); |
|
| 179 | - $this->server->addPlugin(new \Sabre\DAV\Browser\Plugin()); |
|
| 180 | - } else { |
|
| 181 | - $this->server->addPlugin(new DummyGetResponsePlugin()); |
|
| 182 | - } |
|
| 183 | - |
|
| 184 | - $this->server->addPlugin(new ExceptionLoggerPlugin('webdav', $logger)); |
|
| 185 | - $this->server->addPlugin(new LockPlugin()); |
|
| 186 | - $this->server->addPlugin(new \Sabre\DAV\Sync\Plugin()); |
|
| 187 | - |
|
| 188 | - // acl |
|
| 189 | - $acl = new DavAclPlugin(); |
|
| 190 | - $acl->principalCollectionSet = [ |
|
| 191 | - 'principals/users', |
|
| 192 | - 'principals/groups', |
|
| 193 | - 'principals/calendar-resources', |
|
| 194 | - 'principals/calendar-rooms', |
|
| 195 | - ]; |
|
| 196 | - $this->server->addPlugin($acl); |
|
| 197 | - |
|
| 198 | - // calendar plugins |
|
| 199 | - if ($this->requestIsForSubtree(['calendars', 'public-calendars', 'system-calendars', 'principals'])) { |
|
| 200 | - $this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, \OCP\Server::get(IRequest::class), \OCP\Server::get(IConfig::class))); |
|
| 201 | - $this->server->addPlugin(new \OCA\DAV\CalDAV\Plugin()); |
|
| 202 | - $this->server->addPlugin(new ICSExportPlugin(\OCP\Server::get(IConfig::class), $logger)); |
|
| 203 | - $this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin(\OCP\Server::get(IConfig::class), \OCP\Server::get(LoggerInterface::class), \OCP\Server::get(DefaultCalendarValidator::class))); |
|
| 204 | - |
|
| 205 | - $this->server->addPlugin(\OCP\Server::get(\OCA\DAV\CalDAV\Trashbin\Plugin::class)); |
|
| 206 | - $this->server->addPlugin(new \OCA\DAV\CalDAV\WebcalCaching\Plugin($this->request)); |
|
| 207 | - if (\OCP\Server::get(IConfig::class)->getAppValue('dav', 'allow_calendar_link_subscriptions', 'yes') === 'yes') { |
|
| 208 | - $this->server->addPlugin(new \Sabre\CalDAV\Subscriptions\Plugin()); |
|
| 209 | - } |
|
| 210 | - |
|
| 211 | - $this->server->addPlugin(new \Sabre\CalDAV\Notifications\Plugin()); |
|
| 212 | - $this->server->addPlugin(new PublishPlugin( |
|
| 213 | - \OCP\Server::get(IConfig::class), |
|
| 214 | - \OCP\Server::get(IURLGenerator::class) |
|
| 215 | - )); |
|
| 216 | - |
|
| 217 | - $this->server->addPlugin(\OCP\Server::get(RateLimitingPlugin::class)); |
|
| 218 | - $this->server->addPlugin(\OCP\Server::get(CalDavValidatePlugin::class)); |
|
| 219 | - } |
|
| 220 | - |
|
| 221 | - // addressbook plugins |
|
| 222 | - if ($this->requestIsForSubtree(['addressbooks', 'principals'])) { |
|
| 223 | - $this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, \OCP\Server::get(IRequest::class), \OCP\Server::get(IConfig::class))); |
|
| 224 | - $this->server->addPlugin(new \OCA\DAV\CardDAV\Plugin()); |
|
| 225 | - $this->server->addPlugin(new VCFExportPlugin()); |
|
| 226 | - $this->server->addPlugin(new MultiGetExportPlugin()); |
|
| 227 | - $this->server->addPlugin(new HasPhotoPlugin()); |
|
| 228 | - $this->server->addPlugin(new ImageExportPlugin(\OCP\Server::get(PhotoCache::class))); |
|
| 229 | - |
|
| 230 | - $this->server->addPlugin(\OCP\Server::get(CardDavRateLimitingPlugin::class)); |
|
| 231 | - $this->server->addPlugin(\OCP\Server::get(CardDavValidatePlugin::class)); |
|
| 232 | - } |
|
| 233 | - |
|
| 234 | - // system tags plugins |
|
| 235 | - $this->server->addPlugin(\OCP\Server::get(SystemTagPlugin::class)); |
|
| 236 | - |
|
| 237 | - // comments plugin |
|
| 238 | - $this->server->addPlugin(new CommentsPlugin( |
|
| 239 | - \OCP\Server::get(ICommentsManager::class), |
|
| 240 | - \OCP\Server::get(IUserSession::class) |
|
| 241 | - )); |
|
| 242 | - |
|
| 243 | - // performance improvement plugins |
|
| 244 | - $this->server->addPlugin(new CopyEtagHeaderPlugin()); |
|
| 245 | - $this->server->addPlugin(new RequestIdHeaderPlugin(\OCP\Server::get(IRequest::class))); |
|
| 246 | - $this->server->addPlugin(new UploadAutoMkcolPlugin()); |
|
| 247 | - $this->server->addPlugin(new ChunkingV2Plugin(\OCP\Server::get(ICacheFactory::class))); |
|
| 248 | - $this->server->addPlugin(new ChunkingPlugin()); |
|
| 249 | - $this->server->addPlugin(new ZipFolderPlugin( |
|
| 250 | - $this->server->tree, |
|
| 251 | - $logger, |
|
| 252 | - $eventDispatcher, |
|
| 253 | - \OCP\Server::get(IDateTimeZone::class), |
|
| 254 | - )); |
|
| 255 | - $this->server->addPlugin(\OCP\Server::get(PaginatePlugin::class)); |
|
| 256 | - $this->server->addPlugin(new PropFindPreloadNotifyPlugin()); |
|
| 257 | - |
|
| 258 | - // allow setup of additional plugins |
|
| 259 | - $eventDispatcher->dispatch('OCA\DAV\Connector\Sabre::addPlugin', $event); |
|
| 260 | - $typedEvent = new SabrePluginAddEvent($this->server); |
|
| 261 | - $eventDispatcher->dispatchTyped($typedEvent); |
|
| 262 | - |
|
| 263 | - // Some WebDAV clients do require Class 2 WebDAV support (locking), since |
|
| 264 | - // we do not provide locking we emulate it using a fake locking plugin. |
|
| 265 | - if ($this->request->isUserAgent([ |
|
| 266 | - '/WebDAVFS/', |
|
| 267 | - '/OneNote/', |
|
| 268 | - '/^Microsoft-WebDAV/',// Microsoft-WebDAV-MiniRedir/6.1.7601 |
|
| 269 | - ])) { |
|
| 270 | - $this->server->addPlugin(new FakeLockerPlugin()); |
|
| 271 | - } |
|
| 272 | - |
|
| 273 | - if (BrowserErrorPagePlugin::isBrowserRequest($request)) { |
|
| 274 | - $this->server->addPlugin(new BrowserErrorPagePlugin()); |
|
| 275 | - } |
|
| 276 | - |
|
| 277 | - $lazySearchBackend = new LazySearchBackend(); |
|
| 278 | - $this->server->addPlugin(new SearchPlugin($lazySearchBackend)); |
|
| 279 | - |
|
| 280 | - // wait with registering these until auth is handled and the filesystem is setup |
|
| 281 | - $this->server->on('beforeMethod:*', function () use ($root, $lazySearchBackend, $logger): void { |
|
| 282 | - // Allow view-only plugin for webdav requests |
|
| 283 | - $this->server->addPlugin(new ViewOnlyPlugin( |
|
| 284 | - \OC::$server->getUserFolder(), |
|
| 285 | - )); |
|
| 286 | - |
|
| 287 | - // custom properties plugin must be the last one |
|
| 288 | - $userSession = \OCP\Server::get(IUserSession::class); |
|
| 289 | - $user = $userSession->getUser(); |
|
| 290 | - if ($user !== null) { |
|
| 291 | - $view = Filesystem::getView(); |
|
| 292 | - $config = \OCP\Server::get(IConfig::class); |
|
| 293 | - $this->server->addPlugin( |
|
| 294 | - new FilesPlugin( |
|
| 295 | - $this->server->tree, |
|
| 296 | - $config, |
|
| 297 | - $this->request, |
|
| 298 | - \OCP\Server::get(IPreview::class), |
|
| 299 | - \OCP\Server::get(IUserSession::class), |
|
| 300 | - \OCP\Server::get(IFilenameValidator::class), |
|
| 301 | - \OCP\Server::get(IAccountManager::class), |
|
| 302 | - false, |
|
| 303 | - $config->getSystemValueBool('debug', false) === false, |
|
| 304 | - ) |
|
| 305 | - ); |
|
| 306 | - $this->server->addPlugin(new ChecksumUpdatePlugin()); |
|
| 307 | - |
|
| 308 | - $this->server->addPlugin( |
|
| 309 | - new \Sabre\DAV\PropertyStorage\Plugin( |
|
| 310 | - new CustomPropertiesBackend( |
|
| 311 | - $this->server, |
|
| 312 | - $this->server->tree, |
|
| 313 | - \OCP\Server::get(IDBConnection::class), |
|
| 314 | - \OCP\Server::get(IUserSession::class)->getUser(), |
|
| 315 | - \OCP\Server::get(PropertyMapper::class), |
|
| 316 | - \OCP\Server::get(DefaultCalendarValidator::class), |
|
| 317 | - ) |
|
| 318 | - ) |
|
| 319 | - ); |
|
| 320 | - if ($view !== null) { |
|
| 321 | - $this->server->addPlugin( |
|
| 322 | - new QuotaPlugin($view)); |
|
| 323 | - } |
|
| 324 | - $this->server->addPlugin( |
|
| 325 | - new TagsPlugin( |
|
| 326 | - $this->server->tree, \OCP\Server::get(ITagManager::class), \OCP\Server::get(IEventDispatcher::class), \OCP\Server::get(IUserSession::class) |
|
| 327 | - ) |
|
| 328 | - ); |
|
| 329 | - |
|
| 330 | - // TODO: switch to LazyUserFolder |
|
| 331 | - $userFolder = \OC::$server->getUserFolder(); |
|
| 332 | - $shareManager = \OCP\Server::get(\OCP\Share\IManager::class); |
|
| 333 | - $this->server->addPlugin(new SharesPlugin( |
|
| 334 | - $this->server->tree, |
|
| 335 | - $userSession, |
|
| 336 | - $userFolder, |
|
| 337 | - $shareManager, |
|
| 338 | - )); |
|
| 339 | - $this->server->addPlugin(new CommentPropertiesPlugin( |
|
| 340 | - \OCP\Server::get(ICommentsManager::class), |
|
| 341 | - $userSession |
|
| 342 | - )); |
|
| 343 | - if (\OCP\Server::get(IConfig::class)->getAppValue('dav', 'sendInvitations', 'yes') === 'yes') { |
|
| 344 | - $this->server->addPlugin(new IMipPlugin( |
|
| 345 | - \OCP\Server::get(IAppConfig::class), |
|
| 346 | - \OCP\Server::get(IMailer::class), |
|
| 347 | - \OCP\Server::get(LoggerInterface::class), |
|
| 348 | - \OCP\Server::get(ITimeFactory::class), |
|
| 349 | - \OCP\Server::get(Defaults::class), |
|
| 350 | - $userSession, |
|
| 351 | - \OCP\Server::get(IMipService::class), |
|
| 352 | - \OCP\Server::get(EventComparisonService::class), |
|
| 353 | - \OCP\Server::get(\OCP\Mail\Provider\IManager::class), |
|
| 354 | - \OCP\Server::get(\OCP\Mail\IEmailValidator::class), |
|
| 355 | - )); |
|
| 356 | - } |
|
| 357 | - $this->server->addPlugin(new \OCA\DAV\CalDAV\Search\SearchPlugin()); |
|
| 358 | - if ($view !== null) { |
|
| 359 | - $this->server->addPlugin(new FilesReportPlugin( |
|
| 360 | - $this->server->tree, |
|
| 361 | - $view, |
|
| 362 | - \OCP\Server::get(ISystemTagManager::class), |
|
| 363 | - \OCP\Server::get(ISystemTagObjectMapper::class), |
|
| 364 | - \OCP\Server::get(ITagManager::class), |
|
| 365 | - $userSession, |
|
| 366 | - \OCP\Server::get(IGroupManager::class), |
|
| 367 | - $userFolder, |
|
| 368 | - \OCP\Server::get(IAppManager::class) |
|
| 369 | - )); |
|
| 370 | - $lazySearchBackend->setBackend(new FileSearchBackend( |
|
| 371 | - $this->server, |
|
| 372 | - $this->server->tree, |
|
| 373 | - $user, |
|
| 374 | - \OCP\Server::get(IRootFolder::class), |
|
| 375 | - $shareManager, |
|
| 376 | - $view, |
|
| 377 | - \OCP\Server::get(IFilesMetadataManager::class) |
|
| 378 | - )); |
|
| 379 | - $this->server->addPlugin( |
|
| 380 | - new BulkUploadPlugin( |
|
| 381 | - $userFolder, |
|
| 382 | - $logger |
|
| 383 | - ) |
|
| 384 | - ); |
|
| 385 | - } |
|
| 386 | - $this->server->addPlugin(new EnablePlugin( |
|
| 387 | - \OCP\Server::get(IConfig::class), |
|
| 388 | - \OCP\Server::get(BirthdayService::class), |
|
| 389 | - $user |
|
| 390 | - )); |
|
| 391 | - $this->server->addPlugin(new AppleProvisioningPlugin( |
|
| 392 | - \OCP\Server::get(IUserSession::class), |
|
| 393 | - \OCP\Server::get(IURLGenerator::class), |
|
| 394 | - \OCP\Server::get(ThemingDefaults::class), |
|
| 395 | - \OCP\Server::get(IRequest::class), |
|
| 396 | - \OC::$server->getL10N('dav'), |
|
| 397 | - function () { |
|
| 398 | - return UUIDUtil::getUUID(); |
|
| 399 | - } |
|
| 400 | - )); |
|
| 401 | - } |
|
| 402 | - |
|
| 403 | - // register plugins from apps |
|
| 404 | - $pluginManager = new PluginManager( |
|
| 405 | - \OC::$server, |
|
| 406 | - \OCP\Server::get(IAppManager::class) |
|
| 407 | - ); |
|
| 408 | - foreach ($pluginManager->getAppPlugins() as $appPlugin) { |
|
| 409 | - $this->server->addPlugin($appPlugin); |
|
| 410 | - } |
|
| 411 | - foreach ($pluginManager->getAppCollections() as $appCollection) { |
|
| 412 | - $root->addChild($appCollection); |
|
| 413 | - } |
|
| 414 | - }); |
|
| 415 | - |
|
| 416 | - $this->server->addPlugin( |
|
| 417 | - new PropfindCompressionPlugin() |
|
| 418 | - ); |
|
| 419 | - } |
|
| 420 | - |
|
| 421 | - public function exec() { |
|
| 422 | - /** @var IEventLogger $eventLogger */ |
|
| 423 | - $eventLogger = \OCP\Server::get(IEventLogger::class); |
|
| 424 | - $eventLogger->start('dav_server_exec', ''); |
|
| 425 | - $this->server->start(); |
|
| 426 | - $eventLogger->end('dav_server_exec'); |
|
| 427 | - if ($this->profiler->isEnabled()) { |
|
| 428 | - $eventLogger->end('runtime'); |
|
| 429 | - $profile = $this->profiler->collect(\OCP\Server::get(IRequest::class), new Response()); |
|
| 430 | - $this->profiler->saveProfile($profile); |
|
| 431 | - } |
|
| 432 | - } |
|
| 433 | - |
|
| 434 | - private function requestIsForSubtree(array $subTrees): bool { |
|
| 435 | - foreach ($subTrees as $subTree) { |
|
| 436 | - $subTree = trim($subTree, ' /'); |
|
| 437 | - if (str_starts_with($this->server->getRequestUri(), $subTree . '/')) { |
|
| 438 | - return true; |
|
| 439 | - } |
|
| 440 | - } |
|
| 441 | - return false; |
|
| 442 | - } |
|
| 108 | + public Connector\Sabre\Server $server; |
|
| 109 | + private IProfiler $profiler; |
|
| 110 | + |
|
| 111 | + public function __construct( |
|
| 112 | + private IRequest $request, |
|
| 113 | + private string $baseUri, |
|
| 114 | + ) { |
|
| 115 | + $debugEnabled = \OCP\Server::get(IConfig::class)->getSystemValue('debug', false); |
|
| 116 | + $this->profiler = \OCP\Server::get(IProfiler::class); |
|
| 117 | + if ($this->profiler->isEnabled()) { |
|
| 118 | + /** @var IEventLogger $eventLogger */ |
|
| 119 | + $eventLogger = \OCP\Server::get(IEventLogger::class); |
|
| 120 | + $eventLogger->start('runtime', 'DAV Runtime'); |
|
| 121 | + } |
|
| 122 | + |
|
| 123 | + $logger = \OCP\Server::get(LoggerInterface::class); |
|
| 124 | + $eventDispatcher = \OCP\Server::get(IEventDispatcher::class); |
|
| 125 | + |
|
| 126 | + $root = new RootCollection(); |
|
| 127 | + $this->server = new \OCA\DAV\Connector\Sabre\Server(new CachingTree($root)); |
|
| 128 | + $this->server->setLogger($logger); |
|
| 129 | + |
|
| 130 | + // Add maintenance plugin |
|
| 131 | + $this->server->addPlugin(new MaintenancePlugin(\OCP\Server::get(IConfig::class), \OC::$server->getL10N('dav'))); |
|
| 132 | + |
|
| 133 | + $this->server->addPlugin(new AppleQuirksPlugin()); |
|
| 134 | + |
|
| 135 | + // Backends |
|
| 136 | + $authBackend = new Auth( |
|
| 137 | + \OCP\Server::get(ISession::class), |
|
| 138 | + \OCP\Server::get(IUserSession::class), |
|
| 139 | + \OCP\Server::get(IRequest::class), |
|
| 140 | + \OCP\Server::get(\OC\Authentication\TwoFactorAuth\Manager::class), |
|
| 141 | + \OCP\Server::get(IThrottler::class) |
|
| 142 | + ); |
|
| 143 | + |
|
| 144 | + // Set URL explicitly due to reverse-proxy situations |
|
| 145 | + $this->server->httpRequest->setUrl($this->request->getRequestUri()); |
|
| 146 | + $this->server->setBaseUri($this->baseUri); |
|
| 147 | + |
|
| 148 | + $this->server->addPlugin(new ProfilerPlugin($this->request)); |
|
| 149 | + $this->server->addPlugin(new BlockLegacyClientPlugin( |
|
| 150 | + \OCP\Server::get(IConfig::class), |
|
| 151 | + \OCP\Server::get(ThemingDefaults::class), |
|
| 152 | + )); |
|
| 153 | + $this->server->addPlugin(new AnonymousOptionsPlugin()); |
|
| 154 | + $authPlugin = new Plugin(); |
|
| 155 | + $authPlugin->addBackend(new PublicAuth()); |
|
| 156 | + $this->server->addPlugin($authPlugin); |
|
| 157 | + |
|
| 158 | + // allow setup of additional auth backends |
|
| 159 | + $event = new SabrePluginEvent($this->server); |
|
| 160 | + $eventDispatcher->dispatch('OCA\DAV\Connector\Sabre::authInit', $event); |
|
| 161 | + |
|
| 162 | + $newAuthEvent = new SabrePluginAuthInitEvent($this->server); |
|
| 163 | + $eventDispatcher->dispatchTyped($newAuthEvent); |
|
| 164 | + |
|
| 165 | + $bearerAuthBackend = new BearerAuth( |
|
| 166 | + \OCP\Server::get(IUserSession::class), |
|
| 167 | + \OCP\Server::get(ISession::class), |
|
| 168 | + \OCP\Server::get(IRequest::class), |
|
| 169 | + \OCP\Server::get(IConfig::class), |
|
| 170 | + ); |
|
| 171 | + $authPlugin->addBackend($bearerAuthBackend); |
|
| 172 | + // because we are throwing exceptions this plugin has to be the last one |
|
| 173 | + $authPlugin->addBackend($authBackend); |
|
| 174 | + |
|
| 175 | + // debugging |
|
| 176 | + if ($debugEnabled) { |
|
| 177 | + $this->server->debugEnabled = true; |
|
| 178 | + $this->server->addPlugin(new PropFindMonitorPlugin()); |
|
| 179 | + $this->server->addPlugin(new \Sabre\DAV\Browser\Plugin()); |
|
| 180 | + } else { |
|
| 181 | + $this->server->addPlugin(new DummyGetResponsePlugin()); |
|
| 182 | + } |
|
| 183 | + |
|
| 184 | + $this->server->addPlugin(new ExceptionLoggerPlugin('webdav', $logger)); |
|
| 185 | + $this->server->addPlugin(new LockPlugin()); |
|
| 186 | + $this->server->addPlugin(new \Sabre\DAV\Sync\Plugin()); |
|
| 187 | + |
|
| 188 | + // acl |
|
| 189 | + $acl = new DavAclPlugin(); |
|
| 190 | + $acl->principalCollectionSet = [ |
|
| 191 | + 'principals/users', |
|
| 192 | + 'principals/groups', |
|
| 193 | + 'principals/calendar-resources', |
|
| 194 | + 'principals/calendar-rooms', |
|
| 195 | + ]; |
|
| 196 | + $this->server->addPlugin($acl); |
|
| 197 | + |
|
| 198 | + // calendar plugins |
|
| 199 | + if ($this->requestIsForSubtree(['calendars', 'public-calendars', 'system-calendars', 'principals'])) { |
|
| 200 | + $this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, \OCP\Server::get(IRequest::class), \OCP\Server::get(IConfig::class))); |
|
| 201 | + $this->server->addPlugin(new \OCA\DAV\CalDAV\Plugin()); |
|
| 202 | + $this->server->addPlugin(new ICSExportPlugin(\OCP\Server::get(IConfig::class), $logger)); |
|
| 203 | + $this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin(\OCP\Server::get(IConfig::class), \OCP\Server::get(LoggerInterface::class), \OCP\Server::get(DefaultCalendarValidator::class))); |
|
| 204 | + |
|
| 205 | + $this->server->addPlugin(\OCP\Server::get(\OCA\DAV\CalDAV\Trashbin\Plugin::class)); |
|
| 206 | + $this->server->addPlugin(new \OCA\DAV\CalDAV\WebcalCaching\Plugin($this->request)); |
|
| 207 | + if (\OCP\Server::get(IConfig::class)->getAppValue('dav', 'allow_calendar_link_subscriptions', 'yes') === 'yes') { |
|
| 208 | + $this->server->addPlugin(new \Sabre\CalDAV\Subscriptions\Plugin()); |
|
| 209 | + } |
|
| 210 | + |
|
| 211 | + $this->server->addPlugin(new \Sabre\CalDAV\Notifications\Plugin()); |
|
| 212 | + $this->server->addPlugin(new PublishPlugin( |
|
| 213 | + \OCP\Server::get(IConfig::class), |
|
| 214 | + \OCP\Server::get(IURLGenerator::class) |
|
| 215 | + )); |
|
| 216 | + |
|
| 217 | + $this->server->addPlugin(\OCP\Server::get(RateLimitingPlugin::class)); |
|
| 218 | + $this->server->addPlugin(\OCP\Server::get(CalDavValidatePlugin::class)); |
|
| 219 | + } |
|
| 220 | + |
|
| 221 | + // addressbook plugins |
|
| 222 | + if ($this->requestIsForSubtree(['addressbooks', 'principals'])) { |
|
| 223 | + $this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, \OCP\Server::get(IRequest::class), \OCP\Server::get(IConfig::class))); |
|
| 224 | + $this->server->addPlugin(new \OCA\DAV\CardDAV\Plugin()); |
|
| 225 | + $this->server->addPlugin(new VCFExportPlugin()); |
|
| 226 | + $this->server->addPlugin(new MultiGetExportPlugin()); |
|
| 227 | + $this->server->addPlugin(new HasPhotoPlugin()); |
|
| 228 | + $this->server->addPlugin(new ImageExportPlugin(\OCP\Server::get(PhotoCache::class))); |
|
| 229 | + |
|
| 230 | + $this->server->addPlugin(\OCP\Server::get(CardDavRateLimitingPlugin::class)); |
|
| 231 | + $this->server->addPlugin(\OCP\Server::get(CardDavValidatePlugin::class)); |
|
| 232 | + } |
|
| 233 | + |
|
| 234 | + // system tags plugins |
|
| 235 | + $this->server->addPlugin(\OCP\Server::get(SystemTagPlugin::class)); |
|
| 236 | + |
|
| 237 | + // comments plugin |
|
| 238 | + $this->server->addPlugin(new CommentsPlugin( |
|
| 239 | + \OCP\Server::get(ICommentsManager::class), |
|
| 240 | + \OCP\Server::get(IUserSession::class) |
|
| 241 | + )); |
|
| 242 | + |
|
| 243 | + // performance improvement plugins |
|
| 244 | + $this->server->addPlugin(new CopyEtagHeaderPlugin()); |
|
| 245 | + $this->server->addPlugin(new RequestIdHeaderPlugin(\OCP\Server::get(IRequest::class))); |
|
| 246 | + $this->server->addPlugin(new UploadAutoMkcolPlugin()); |
|
| 247 | + $this->server->addPlugin(new ChunkingV2Plugin(\OCP\Server::get(ICacheFactory::class))); |
|
| 248 | + $this->server->addPlugin(new ChunkingPlugin()); |
|
| 249 | + $this->server->addPlugin(new ZipFolderPlugin( |
|
| 250 | + $this->server->tree, |
|
| 251 | + $logger, |
|
| 252 | + $eventDispatcher, |
|
| 253 | + \OCP\Server::get(IDateTimeZone::class), |
|
| 254 | + )); |
|
| 255 | + $this->server->addPlugin(\OCP\Server::get(PaginatePlugin::class)); |
|
| 256 | + $this->server->addPlugin(new PropFindPreloadNotifyPlugin()); |
|
| 257 | + |
|
| 258 | + // allow setup of additional plugins |
|
| 259 | + $eventDispatcher->dispatch('OCA\DAV\Connector\Sabre::addPlugin', $event); |
|
| 260 | + $typedEvent = new SabrePluginAddEvent($this->server); |
|
| 261 | + $eventDispatcher->dispatchTyped($typedEvent); |
|
| 262 | + |
|
| 263 | + // Some WebDAV clients do require Class 2 WebDAV support (locking), since |
|
| 264 | + // we do not provide locking we emulate it using a fake locking plugin. |
|
| 265 | + if ($this->request->isUserAgent([ |
|
| 266 | + '/WebDAVFS/', |
|
| 267 | + '/OneNote/', |
|
| 268 | + '/^Microsoft-WebDAV/',// Microsoft-WebDAV-MiniRedir/6.1.7601 |
|
| 269 | + ])) { |
|
| 270 | + $this->server->addPlugin(new FakeLockerPlugin()); |
|
| 271 | + } |
|
| 272 | + |
|
| 273 | + if (BrowserErrorPagePlugin::isBrowserRequest($request)) { |
|
| 274 | + $this->server->addPlugin(new BrowserErrorPagePlugin()); |
|
| 275 | + } |
|
| 276 | + |
|
| 277 | + $lazySearchBackend = new LazySearchBackend(); |
|
| 278 | + $this->server->addPlugin(new SearchPlugin($lazySearchBackend)); |
|
| 279 | + |
|
| 280 | + // wait with registering these until auth is handled and the filesystem is setup |
|
| 281 | + $this->server->on('beforeMethod:*', function () use ($root, $lazySearchBackend, $logger): void { |
|
| 282 | + // Allow view-only plugin for webdav requests |
|
| 283 | + $this->server->addPlugin(new ViewOnlyPlugin( |
|
| 284 | + \OC::$server->getUserFolder(), |
|
| 285 | + )); |
|
| 286 | + |
|
| 287 | + // custom properties plugin must be the last one |
|
| 288 | + $userSession = \OCP\Server::get(IUserSession::class); |
|
| 289 | + $user = $userSession->getUser(); |
|
| 290 | + if ($user !== null) { |
|
| 291 | + $view = Filesystem::getView(); |
|
| 292 | + $config = \OCP\Server::get(IConfig::class); |
|
| 293 | + $this->server->addPlugin( |
|
| 294 | + new FilesPlugin( |
|
| 295 | + $this->server->tree, |
|
| 296 | + $config, |
|
| 297 | + $this->request, |
|
| 298 | + \OCP\Server::get(IPreview::class), |
|
| 299 | + \OCP\Server::get(IUserSession::class), |
|
| 300 | + \OCP\Server::get(IFilenameValidator::class), |
|
| 301 | + \OCP\Server::get(IAccountManager::class), |
|
| 302 | + false, |
|
| 303 | + $config->getSystemValueBool('debug', false) === false, |
|
| 304 | + ) |
|
| 305 | + ); |
|
| 306 | + $this->server->addPlugin(new ChecksumUpdatePlugin()); |
|
| 307 | + |
|
| 308 | + $this->server->addPlugin( |
|
| 309 | + new \Sabre\DAV\PropertyStorage\Plugin( |
|
| 310 | + new CustomPropertiesBackend( |
|
| 311 | + $this->server, |
|
| 312 | + $this->server->tree, |
|
| 313 | + \OCP\Server::get(IDBConnection::class), |
|
| 314 | + \OCP\Server::get(IUserSession::class)->getUser(), |
|
| 315 | + \OCP\Server::get(PropertyMapper::class), |
|
| 316 | + \OCP\Server::get(DefaultCalendarValidator::class), |
|
| 317 | + ) |
|
| 318 | + ) |
|
| 319 | + ); |
|
| 320 | + if ($view !== null) { |
|
| 321 | + $this->server->addPlugin( |
|
| 322 | + new QuotaPlugin($view)); |
|
| 323 | + } |
|
| 324 | + $this->server->addPlugin( |
|
| 325 | + new TagsPlugin( |
|
| 326 | + $this->server->tree, \OCP\Server::get(ITagManager::class), \OCP\Server::get(IEventDispatcher::class), \OCP\Server::get(IUserSession::class) |
|
| 327 | + ) |
|
| 328 | + ); |
|
| 329 | + |
|
| 330 | + // TODO: switch to LazyUserFolder |
|
| 331 | + $userFolder = \OC::$server->getUserFolder(); |
|
| 332 | + $shareManager = \OCP\Server::get(\OCP\Share\IManager::class); |
|
| 333 | + $this->server->addPlugin(new SharesPlugin( |
|
| 334 | + $this->server->tree, |
|
| 335 | + $userSession, |
|
| 336 | + $userFolder, |
|
| 337 | + $shareManager, |
|
| 338 | + )); |
|
| 339 | + $this->server->addPlugin(new CommentPropertiesPlugin( |
|
| 340 | + \OCP\Server::get(ICommentsManager::class), |
|
| 341 | + $userSession |
|
| 342 | + )); |
|
| 343 | + if (\OCP\Server::get(IConfig::class)->getAppValue('dav', 'sendInvitations', 'yes') === 'yes') { |
|
| 344 | + $this->server->addPlugin(new IMipPlugin( |
|
| 345 | + \OCP\Server::get(IAppConfig::class), |
|
| 346 | + \OCP\Server::get(IMailer::class), |
|
| 347 | + \OCP\Server::get(LoggerInterface::class), |
|
| 348 | + \OCP\Server::get(ITimeFactory::class), |
|
| 349 | + \OCP\Server::get(Defaults::class), |
|
| 350 | + $userSession, |
|
| 351 | + \OCP\Server::get(IMipService::class), |
|
| 352 | + \OCP\Server::get(EventComparisonService::class), |
|
| 353 | + \OCP\Server::get(\OCP\Mail\Provider\IManager::class), |
|
| 354 | + \OCP\Server::get(\OCP\Mail\IEmailValidator::class), |
|
| 355 | + )); |
|
| 356 | + } |
|
| 357 | + $this->server->addPlugin(new \OCA\DAV\CalDAV\Search\SearchPlugin()); |
|
| 358 | + if ($view !== null) { |
|
| 359 | + $this->server->addPlugin(new FilesReportPlugin( |
|
| 360 | + $this->server->tree, |
|
| 361 | + $view, |
|
| 362 | + \OCP\Server::get(ISystemTagManager::class), |
|
| 363 | + \OCP\Server::get(ISystemTagObjectMapper::class), |
|
| 364 | + \OCP\Server::get(ITagManager::class), |
|
| 365 | + $userSession, |
|
| 366 | + \OCP\Server::get(IGroupManager::class), |
|
| 367 | + $userFolder, |
|
| 368 | + \OCP\Server::get(IAppManager::class) |
|
| 369 | + )); |
|
| 370 | + $lazySearchBackend->setBackend(new FileSearchBackend( |
|
| 371 | + $this->server, |
|
| 372 | + $this->server->tree, |
|
| 373 | + $user, |
|
| 374 | + \OCP\Server::get(IRootFolder::class), |
|
| 375 | + $shareManager, |
|
| 376 | + $view, |
|
| 377 | + \OCP\Server::get(IFilesMetadataManager::class) |
|
| 378 | + )); |
|
| 379 | + $this->server->addPlugin( |
|
| 380 | + new BulkUploadPlugin( |
|
| 381 | + $userFolder, |
|
| 382 | + $logger |
|
| 383 | + ) |
|
| 384 | + ); |
|
| 385 | + } |
|
| 386 | + $this->server->addPlugin(new EnablePlugin( |
|
| 387 | + \OCP\Server::get(IConfig::class), |
|
| 388 | + \OCP\Server::get(BirthdayService::class), |
|
| 389 | + $user |
|
| 390 | + )); |
|
| 391 | + $this->server->addPlugin(new AppleProvisioningPlugin( |
|
| 392 | + \OCP\Server::get(IUserSession::class), |
|
| 393 | + \OCP\Server::get(IURLGenerator::class), |
|
| 394 | + \OCP\Server::get(ThemingDefaults::class), |
|
| 395 | + \OCP\Server::get(IRequest::class), |
|
| 396 | + \OC::$server->getL10N('dav'), |
|
| 397 | + function () { |
|
| 398 | + return UUIDUtil::getUUID(); |
|
| 399 | + } |
|
| 400 | + )); |
|
| 401 | + } |
|
| 402 | + |
|
| 403 | + // register plugins from apps |
|
| 404 | + $pluginManager = new PluginManager( |
|
| 405 | + \OC::$server, |
|
| 406 | + \OCP\Server::get(IAppManager::class) |
|
| 407 | + ); |
|
| 408 | + foreach ($pluginManager->getAppPlugins() as $appPlugin) { |
|
| 409 | + $this->server->addPlugin($appPlugin); |
|
| 410 | + } |
|
| 411 | + foreach ($pluginManager->getAppCollections() as $appCollection) { |
|
| 412 | + $root->addChild($appCollection); |
|
| 413 | + } |
|
| 414 | + }); |
|
| 415 | + |
|
| 416 | + $this->server->addPlugin( |
|
| 417 | + new PropfindCompressionPlugin() |
|
| 418 | + ); |
|
| 419 | + } |
|
| 420 | + |
|
| 421 | + public function exec() { |
|
| 422 | + /** @var IEventLogger $eventLogger */ |
|
| 423 | + $eventLogger = \OCP\Server::get(IEventLogger::class); |
|
| 424 | + $eventLogger->start('dav_server_exec', ''); |
|
| 425 | + $this->server->start(); |
|
| 426 | + $eventLogger->end('dav_server_exec'); |
|
| 427 | + if ($this->profiler->isEnabled()) { |
|
| 428 | + $eventLogger->end('runtime'); |
|
| 429 | + $profile = $this->profiler->collect(\OCP\Server::get(IRequest::class), new Response()); |
|
| 430 | + $this->profiler->saveProfile($profile); |
|
| 431 | + } |
|
| 432 | + } |
|
| 433 | + |
|
| 434 | + private function requestIsForSubtree(array $subTrees): bool { |
|
| 435 | + foreach ($subTrees as $subTree) { |
|
| 436 | + $subTree = trim($subTree, ' /'); |
|
| 437 | + if (str_starts_with($this->server->getRequestUri(), $subTree . '/')) { |
|
| 438 | + return true; |
|
| 439 | + } |
|
| 440 | + } |
|
| 441 | + return false; |
|
| 442 | + } |
|
| 443 | 443 | |
| 444 | 444 | } |
@@ -30,113 +30,113 @@ |
||
| 30 | 30 | |
| 31 | 31 | /** @template-implements IEventListener<CalendarObjectCreatedEvent|CalendarObjectUpdatedEvent|CalendarShareUpdatedEvent> */ |
| 32 | 32 | class CalendarContactInteractionListener implements IEventListener { |
| 33 | - private const URI_USERS = 'principals/users/'; |
|
| 34 | - |
|
| 35 | - public function __construct( |
|
| 36 | - private IEventDispatcher $dispatcher, |
|
| 37 | - private IUserSession $userSession, |
|
| 38 | - private Principal $principalConnector, |
|
| 39 | - private IEmailValidator $emailValidator, |
|
| 40 | - private LoggerInterface $logger, |
|
| 41 | - ) { |
|
| 42 | - } |
|
| 43 | - |
|
| 44 | - public function handle(Event $event): void { |
|
| 45 | - if (($user = $this->userSession->getUser()) === null) { |
|
| 46 | - // Without user context we can't do anything |
|
| 47 | - return; |
|
| 48 | - } |
|
| 49 | - |
|
| 50 | - if ($event instanceof CalendarObjectCreatedEvent || $event instanceof CalendarObjectUpdatedEvent) { |
|
| 51 | - // users: href => principal:principals/users/admin |
|
| 52 | - foreach ($event->getShares() as $share) { |
|
| 53 | - if (!isset($share['href'])) { |
|
| 54 | - continue; |
|
| 55 | - } |
|
| 56 | - $this->emitFromUri($share['href'], $user); |
|
| 57 | - } |
|
| 58 | - |
|
| 59 | - // emit interaction for email attendees as well |
|
| 60 | - if (isset($event->getObjectData()['calendardata'])) { |
|
| 61 | - try { |
|
| 62 | - $calendar = Reader::read($event->getObjectData()['calendardata']); |
|
| 63 | - if ($calendar->VEVENT) { |
|
| 64 | - foreach ($calendar->VEVENT as $calendarEvent) { |
|
| 65 | - $this->emitFromObject($calendarEvent, $user); |
|
| 66 | - } |
|
| 67 | - } |
|
| 68 | - } catch (Throwable $e) { |
|
| 69 | - $this->logger->warning('Could not read calendar data for interaction events: ' . $e->getMessage(), [ |
|
| 70 | - 'exception' => $e, |
|
| 71 | - ]); |
|
| 72 | - } |
|
| 73 | - } |
|
| 74 | - } |
|
| 75 | - |
|
| 76 | - if ($event instanceof CalendarShareUpdatedEvent && !empty($event->getAdded())) { |
|
| 77 | - // group: href => principal:principals/groups/admin |
|
| 78 | - // users: href => principal:principals/users/admin |
|
| 79 | - foreach ($event->getAdded() as $added) { |
|
| 80 | - if (!isset($added['href'])) { |
|
| 81 | - // Nothing to work with |
|
| 82 | - continue; |
|
| 83 | - } |
|
| 84 | - $this->emitFromUri($added['href'], $user); |
|
| 85 | - } |
|
| 86 | - } |
|
| 87 | - } |
|
| 88 | - |
|
| 89 | - private function emitFromUri(string $uri, IUser $user): void { |
|
| 90 | - $principal = $this->principalConnector->findByUri( |
|
| 91 | - $uri, |
|
| 92 | - $this->principalConnector->getPrincipalPrefix() |
|
| 93 | - ); |
|
| 94 | - if ($principal === null) { |
|
| 95 | - // Invalid principal |
|
| 96 | - return; |
|
| 97 | - } |
|
| 98 | - if (!str_starts_with($principal, self::URI_USERS)) { |
|
| 99 | - // Not a user principal |
|
| 100 | - return; |
|
| 101 | - } |
|
| 102 | - |
|
| 103 | - $uid = substr($principal, strlen(self::URI_USERS)); |
|
| 104 | - $this->dispatcher->dispatchTyped( |
|
| 105 | - (new ContactInteractedWithEvent($user))->setUid($uid) |
|
| 106 | - ); |
|
| 107 | - } |
|
| 108 | - |
|
| 109 | - private function emitFromObject(VEvent $vevent, IUser $user): void { |
|
| 110 | - if (!$vevent->ATTENDEE) { |
|
| 111 | - // Nothing left to do |
|
| 112 | - return; |
|
| 113 | - } |
|
| 114 | - |
|
| 115 | - foreach ($vevent->ATTENDEE as $attendee) { |
|
| 116 | - if (!($attendee instanceof Property)) { |
|
| 117 | - continue; |
|
| 118 | - } |
|
| 119 | - |
|
| 120 | - $cuType = $attendee->offsetGet('CUTYPE'); |
|
| 121 | - if ($cuType instanceof Parameter && $cuType->getValue() !== 'INDIVIDUAL') { |
|
| 122 | - // Don't care about those |
|
| 123 | - continue; |
|
| 124 | - } |
|
| 125 | - |
|
| 126 | - $mailTo = $attendee->getValue(); |
|
| 127 | - if (!str_starts_with($mailTo, 'mailto:')) { |
|
| 128 | - // Doesn't look like an email |
|
| 129 | - continue; |
|
| 130 | - } |
|
| 131 | - $email = substr($mailTo, strlen('mailto:')); |
|
| 132 | - if (!$this->emailValidator->isValid($email)) { |
|
| 133 | - // This really isn't a valid email |
|
| 134 | - continue; |
|
| 135 | - } |
|
| 136 | - |
|
| 137 | - $this->dispatcher->dispatchTyped( |
|
| 138 | - (new ContactInteractedWithEvent($user))->setEmail($email) |
|
| 139 | - ); |
|
| 140 | - } |
|
| 141 | - } |
|
| 33 | + private const URI_USERS = 'principals/users/'; |
|
| 34 | + |
|
| 35 | + public function __construct( |
|
| 36 | + private IEventDispatcher $dispatcher, |
|
| 37 | + private IUserSession $userSession, |
|
| 38 | + private Principal $principalConnector, |
|
| 39 | + private IEmailValidator $emailValidator, |
|
| 40 | + private LoggerInterface $logger, |
|
| 41 | + ) { |
|
| 42 | + } |
|
| 43 | + |
|
| 44 | + public function handle(Event $event): void { |
|
| 45 | + if (($user = $this->userSession->getUser()) === null) { |
|
| 46 | + // Without user context we can't do anything |
|
| 47 | + return; |
|
| 48 | + } |
|
| 49 | + |
|
| 50 | + if ($event instanceof CalendarObjectCreatedEvent || $event instanceof CalendarObjectUpdatedEvent) { |
|
| 51 | + // users: href => principal:principals/users/admin |
|
| 52 | + foreach ($event->getShares() as $share) { |
|
| 53 | + if (!isset($share['href'])) { |
|
| 54 | + continue; |
|
| 55 | + } |
|
| 56 | + $this->emitFromUri($share['href'], $user); |
|
| 57 | + } |
|
| 58 | + |
|
| 59 | + // emit interaction for email attendees as well |
|
| 60 | + if (isset($event->getObjectData()['calendardata'])) { |
|
| 61 | + try { |
|
| 62 | + $calendar = Reader::read($event->getObjectData()['calendardata']); |
|
| 63 | + if ($calendar->VEVENT) { |
|
| 64 | + foreach ($calendar->VEVENT as $calendarEvent) { |
|
| 65 | + $this->emitFromObject($calendarEvent, $user); |
|
| 66 | + } |
|
| 67 | + } |
|
| 68 | + } catch (Throwable $e) { |
|
| 69 | + $this->logger->warning('Could not read calendar data for interaction events: ' . $e->getMessage(), [ |
|
| 70 | + 'exception' => $e, |
|
| 71 | + ]); |
|
| 72 | + } |
|
| 73 | + } |
|
| 74 | + } |
|
| 75 | + |
|
| 76 | + if ($event instanceof CalendarShareUpdatedEvent && !empty($event->getAdded())) { |
|
| 77 | + // group: href => principal:principals/groups/admin |
|
| 78 | + // users: href => principal:principals/users/admin |
|
| 79 | + foreach ($event->getAdded() as $added) { |
|
| 80 | + if (!isset($added['href'])) { |
|
| 81 | + // Nothing to work with |
|
| 82 | + continue; |
|
| 83 | + } |
|
| 84 | + $this->emitFromUri($added['href'], $user); |
|
| 85 | + } |
|
| 86 | + } |
|
| 87 | + } |
|
| 88 | + |
|
| 89 | + private function emitFromUri(string $uri, IUser $user): void { |
|
| 90 | + $principal = $this->principalConnector->findByUri( |
|
| 91 | + $uri, |
|
| 92 | + $this->principalConnector->getPrincipalPrefix() |
|
| 93 | + ); |
|
| 94 | + if ($principal === null) { |
|
| 95 | + // Invalid principal |
|
| 96 | + return; |
|
| 97 | + } |
|
| 98 | + if (!str_starts_with($principal, self::URI_USERS)) { |
|
| 99 | + // Not a user principal |
|
| 100 | + return; |
|
| 101 | + } |
|
| 102 | + |
|
| 103 | + $uid = substr($principal, strlen(self::URI_USERS)); |
|
| 104 | + $this->dispatcher->dispatchTyped( |
|
| 105 | + (new ContactInteractedWithEvent($user))->setUid($uid) |
|
| 106 | + ); |
|
| 107 | + } |
|
| 108 | + |
|
| 109 | + private function emitFromObject(VEvent $vevent, IUser $user): void { |
|
| 110 | + if (!$vevent->ATTENDEE) { |
|
| 111 | + // Nothing left to do |
|
| 112 | + return; |
|
| 113 | + } |
|
| 114 | + |
|
| 115 | + foreach ($vevent->ATTENDEE as $attendee) { |
|
| 116 | + if (!($attendee instanceof Property)) { |
|
| 117 | + continue; |
|
| 118 | + } |
|
| 119 | + |
|
| 120 | + $cuType = $attendee->offsetGet('CUTYPE'); |
|
| 121 | + if ($cuType instanceof Parameter && $cuType->getValue() !== 'INDIVIDUAL') { |
|
| 122 | + // Don't care about those |
|
| 123 | + continue; |
|
| 124 | + } |
|
| 125 | + |
|
| 126 | + $mailTo = $attendee->getValue(); |
|
| 127 | + if (!str_starts_with($mailTo, 'mailto:')) { |
|
| 128 | + // Doesn't look like an email |
|
| 129 | + continue; |
|
| 130 | + } |
|
| 131 | + $email = substr($mailTo, strlen('mailto:')); |
|
| 132 | + if (!$this->emailValidator->isValid($email)) { |
|
| 133 | + // This really isn't a valid email |
|
| 134 | + continue; |
|
| 135 | + } |
|
| 136 | + |
|
| 137 | + $this->dispatcher->dispatchTyped( |
|
| 138 | + (new ContactInteractedWithEvent($user))->setEmail($email) |
|
| 139 | + ); |
|
| 140 | + } |
|
| 141 | + } |
|
| 142 | 142 | } |
@@ -47,176 +47,176 @@ discard block |
||
| 47 | 47 | */ |
| 48 | 48 | class IMipPlugin extends SabreIMipPlugin { |
| 49 | 49 | |
| 50 | - private ?VCalendar $vCalendar = null; |
|
| 51 | - public const MAX_DATE = '2038-01-01'; |
|
| 52 | - public const METHOD_REQUEST = 'request'; |
|
| 53 | - public const METHOD_REPLY = 'reply'; |
|
| 54 | - public const METHOD_CANCEL = 'cancel'; |
|
| 55 | - public const IMIP_INDENT = 15; |
|
| 56 | - |
|
| 57 | - public function __construct( |
|
| 58 | - private IAppConfig $config, |
|
| 59 | - private IMailer $mailer, |
|
| 60 | - private LoggerInterface $logger, |
|
| 61 | - private ITimeFactory $timeFactory, |
|
| 62 | - private Defaults $defaults, |
|
| 63 | - private IUserSession $userSession, |
|
| 64 | - private IMipService $imipService, |
|
| 65 | - private EventComparisonService $eventComparisonService, |
|
| 66 | - private IMailManager $mailManager, |
|
| 67 | - private IEmailValidator $emailValidator, |
|
| 68 | - ) { |
|
| 69 | - parent::__construct(''); |
|
| 70 | - } |
|
| 71 | - |
|
| 72 | - public function initialize(DAV\Server $server): void { |
|
| 73 | - parent::initialize($server); |
|
| 74 | - $server->on('beforeWriteContent', [$this, 'beforeWriteContent'], 10); |
|
| 75 | - } |
|
| 76 | - |
|
| 77 | - /** |
|
| 78 | - * Check quota before writing content |
|
| 79 | - * |
|
| 80 | - * @param string $uri target file URI |
|
| 81 | - * @param INode $node Sabre Node |
|
| 82 | - * @param resource $data data |
|
| 83 | - * @param bool $modified modified |
|
| 84 | - */ |
|
| 85 | - public function beforeWriteContent($uri, INode $node, $data, $modified): void { |
|
| 86 | - if (!$node instanceof CalendarObject) { |
|
| 87 | - return; |
|
| 88 | - } |
|
| 89 | - /** @var VCalendar $vCalendar */ |
|
| 90 | - $vCalendar = Reader::read($node->get()); |
|
| 91 | - $this->setVCalendar($vCalendar); |
|
| 92 | - } |
|
| 93 | - |
|
| 94 | - /** |
|
| 95 | - * Event handler for the 'schedule' event. |
|
| 96 | - * |
|
| 97 | - * @param Message $iTipMessage |
|
| 98 | - * @return void |
|
| 99 | - */ |
|
| 100 | - public function schedule(Message $iTipMessage) { |
|
| 101 | - |
|
| 102 | - // Not sending any emails if the system considers the update insignificant |
|
| 103 | - if (!$iTipMessage->significantChange) { |
|
| 104 | - if (!$iTipMessage->scheduleStatus) { |
|
| 105 | - $iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email'; |
|
| 106 | - } |
|
| 107 | - return; |
|
| 108 | - } |
|
| 109 | - |
|
| 110 | - if (parse_url($iTipMessage->sender, PHP_URL_SCHEME) !== 'mailto' |
|
| 111 | - || parse_url($iTipMessage->recipient, PHP_URL_SCHEME) !== 'mailto') { |
|
| 112 | - return; |
|
| 113 | - } |
|
| 114 | - |
|
| 115 | - // don't send out mails for events that already took place |
|
| 116 | - $lastOccurrence = $this->imipService->getLastOccurrence($iTipMessage->message); |
|
| 117 | - $currentTime = $this->timeFactory->getTime(); |
|
| 118 | - if ($lastOccurrence < $currentTime) { |
|
| 119 | - return; |
|
| 120 | - } |
|
| 121 | - |
|
| 122 | - // Strip off mailto: |
|
| 123 | - $recipient = substr($iTipMessage->recipient, 7); |
|
| 124 | - if (!$this->emailValidator->isValid($recipient)) { |
|
| 125 | - // Nothing to send if the recipient doesn't have a valid email address |
|
| 126 | - $iTipMessage->scheduleStatus = '5.0; EMail delivery failed'; |
|
| 127 | - return; |
|
| 128 | - } |
|
| 129 | - $recipientName = $iTipMessage->recipientName ? (string)$iTipMessage->recipientName : null; |
|
| 130 | - |
|
| 131 | - $newEvents = $iTipMessage->message; |
|
| 132 | - $oldEvents = $this->getVCalendar(); |
|
| 133 | - |
|
| 134 | - $modified = $this->eventComparisonService->findModified($newEvents, $oldEvents); |
|
| 135 | - /** @var VEvent $vEvent */ |
|
| 136 | - $vEvent = array_pop($modified['new']); |
|
| 137 | - /** @var VEvent $oldVevent */ |
|
| 138 | - $oldVevent = !empty($modified['old']) && is_array($modified['old']) ? array_pop($modified['old']) : null; |
|
| 139 | - $isModified = isset($oldVevent); |
|
| 140 | - |
|
| 141 | - // No changed events after all - this shouldn't happen if there is significant change yet here we are |
|
| 142 | - // The scheduling status is debatable |
|
| 143 | - if (empty($vEvent)) { |
|
| 144 | - $this->logger->warning('iTip message said the change was significant but comparison did not detect any updated VEvents'); |
|
| 145 | - $iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email'; |
|
| 146 | - return; |
|
| 147 | - } |
|
| 148 | - |
|
| 149 | - // we (should) have one event component left |
|
| 150 | - // as the ITip\Broker creates one iTip message per change |
|
| 151 | - // and triggers the "schedule" event once per message |
|
| 152 | - // we also might not have an old event as this could be a new |
|
| 153 | - // invitation, or a new recurrence exception |
|
| 154 | - $attendee = $this->imipService->getCurrentAttendee($iTipMessage); |
|
| 155 | - if ($attendee === null) { |
|
| 156 | - $uid = $vEvent->UID ?? 'no UID found'; |
|
| 157 | - $this->logger->debug('Could not find recipient ' . $recipient . ' as attendee for event with UID ' . $uid); |
|
| 158 | - $iTipMessage->scheduleStatus = '5.0; EMail delivery failed'; |
|
| 159 | - return; |
|
| 160 | - } |
|
| 161 | - // Don't send emails to rooms, resources and circles |
|
| 162 | - if ($this->imipService->isRoomOrResource($attendee) |
|
| 163 | - || $this->imipService->isCircle($attendee)) { |
|
| 164 | - $this->logger->debug('No invitation sent as recipient is room, resource or circle', [ |
|
| 165 | - 'attendee' => $recipient, |
|
| 166 | - ]); |
|
| 167 | - $iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email'; |
|
| 168 | - return; |
|
| 169 | - } |
|
| 170 | - $this->imipService->setL10n($attendee); |
|
| 171 | - |
|
| 172 | - // Build the sender name. |
|
| 173 | - // Due to a bug in sabre, the senderName property for an iTIP message can actually also be a VObject Property |
|
| 174 | - // If the iTIP message senderName is null or empty use the user session name as the senderName |
|
| 175 | - if (($iTipMessage->senderName instanceof Parameter) && !empty(trim($iTipMessage->senderName->getValue()))) { |
|
| 176 | - $senderName = trim($iTipMessage->senderName->getValue()); |
|
| 177 | - } elseif (is_string($iTipMessage->senderName) && !empty(trim($iTipMessage->senderName))) { |
|
| 178 | - $senderName = trim($iTipMessage->senderName); |
|
| 179 | - } elseif ($this->userSession->getUser() !== null) { |
|
| 180 | - $senderName = trim($this->userSession->getUser()->getDisplayName()); |
|
| 181 | - } else { |
|
| 182 | - $senderName = ''; |
|
| 183 | - } |
|
| 184 | - |
|
| 185 | - $sender = substr($iTipMessage->sender, 7); |
|
| 186 | - |
|
| 187 | - $replyingAttendee = null; |
|
| 188 | - switch (strtolower($iTipMessage->method)) { |
|
| 189 | - case self::METHOD_REPLY: |
|
| 190 | - $method = self::METHOD_REPLY; |
|
| 191 | - $data = $this->imipService->buildReplyBodyData($vEvent); |
|
| 192 | - $replyingAttendee = $this->imipService->getReplyingAttendee($iTipMessage); |
|
| 193 | - break; |
|
| 194 | - case self::METHOD_CANCEL: |
|
| 195 | - $method = self::METHOD_CANCEL; |
|
| 196 | - $data = $this->imipService->buildCancelledBodyData($vEvent); |
|
| 197 | - break; |
|
| 198 | - default: |
|
| 199 | - $method = self::METHOD_REQUEST; |
|
| 200 | - $data = $this->imipService->buildBodyData($vEvent, $oldVevent); |
|
| 201 | - break; |
|
| 202 | - } |
|
| 203 | - |
|
| 204 | - $data['attendee_name'] = ($recipientName ?: $recipient); |
|
| 205 | - $data['invitee_name'] = ($senderName ?: $sender); |
|
| 206 | - |
|
| 207 | - $fromEMail = Util::getDefaultEmailAddress('invitations-noreply'); |
|
| 208 | - $fromName = $this->imipService->getFrom($senderName, $this->defaults->getName()); |
|
| 209 | - |
|
| 210 | - $template = $this->mailer->createEMailTemplate('dav.calendarInvite.' . $method, $data); |
|
| 211 | - $template->addHeader(); |
|
| 212 | - |
|
| 213 | - $this->imipService->addSubjectAndHeading($template, $method, $data['invitee_name'], $data['meeting_title'], $isModified, $replyingAttendee); |
|
| 214 | - $this->imipService->addBulletList($template, $vEvent, $data); |
|
| 215 | - |
|
| 216 | - // Only add response buttons to invitation requests: Fix Issue #11230 |
|
| 217 | - if (strcasecmp($method, self::METHOD_REQUEST) === 0 && $this->imipService->getAttendeeRsvpOrReqForParticipant($attendee)) { |
|
| 218 | - |
|
| 219 | - /* |
|
| 50 | + private ?VCalendar $vCalendar = null; |
|
| 51 | + public const MAX_DATE = '2038-01-01'; |
|
| 52 | + public const METHOD_REQUEST = 'request'; |
|
| 53 | + public const METHOD_REPLY = 'reply'; |
|
| 54 | + public const METHOD_CANCEL = 'cancel'; |
|
| 55 | + public const IMIP_INDENT = 15; |
|
| 56 | + |
|
| 57 | + public function __construct( |
|
| 58 | + private IAppConfig $config, |
|
| 59 | + private IMailer $mailer, |
|
| 60 | + private LoggerInterface $logger, |
|
| 61 | + private ITimeFactory $timeFactory, |
|
| 62 | + private Defaults $defaults, |
|
| 63 | + private IUserSession $userSession, |
|
| 64 | + private IMipService $imipService, |
|
| 65 | + private EventComparisonService $eventComparisonService, |
|
| 66 | + private IMailManager $mailManager, |
|
| 67 | + private IEmailValidator $emailValidator, |
|
| 68 | + ) { |
|
| 69 | + parent::__construct(''); |
|
| 70 | + } |
|
| 71 | + |
|
| 72 | + public function initialize(DAV\Server $server): void { |
|
| 73 | + parent::initialize($server); |
|
| 74 | + $server->on('beforeWriteContent', [$this, 'beforeWriteContent'], 10); |
|
| 75 | + } |
|
| 76 | + |
|
| 77 | + /** |
|
| 78 | + * Check quota before writing content |
|
| 79 | + * |
|
| 80 | + * @param string $uri target file URI |
|
| 81 | + * @param INode $node Sabre Node |
|
| 82 | + * @param resource $data data |
|
| 83 | + * @param bool $modified modified |
|
| 84 | + */ |
|
| 85 | + public function beforeWriteContent($uri, INode $node, $data, $modified): void { |
|
| 86 | + if (!$node instanceof CalendarObject) { |
|
| 87 | + return; |
|
| 88 | + } |
|
| 89 | + /** @var VCalendar $vCalendar */ |
|
| 90 | + $vCalendar = Reader::read($node->get()); |
|
| 91 | + $this->setVCalendar($vCalendar); |
|
| 92 | + } |
|
| 93 | + |
|
| 94 | + /** |
|
| 95 | + * Event handler for the 'schedule' event. |
|
| 96 | + * |
|
| 97 | + * @param Message $iTipMessage |
|
| 98 | + * @return void |
|
| 99 | + */ |
|
| 100 | + public function schedule(Message $iTipMessage) { |
|
| 101 | + |
|
| 102 | + // Not sending any emails if the system considers the update insignificant |
|
| 103 | + if (!$iTipMessage->significantChange) { |
|
| 104 | + if (!$iTipMessage->scheduleStatus) { |
|
| 105 | + $iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email'; |
|
| 106 | + } |
|
| 107 | + return; |
|
| 108 | + } |
|
| 109 | + |
|
| 110 | + if (parse_url($iTipMessage->sender, PHP_URL_SCHEME) !== 'mailto' |
|
| 111 | + || parse_url($iTipMessage->recipient, PHP_URL_SCHEME) !== 'mailto') { |
|
| 112 | + return; |
|
| 113 | + } |
|
| 114 | + |
|
| 115 | + // don't send out mails for events that already took place |
|
| 116 | + $lastOccurrence = $this->imipService->getLastOccurrence($iTipMessage->message); |
|
| 117 | + $currentTime = $this->timeFactory->getTime(); |
|
| 118 | + if ($lastOccurrence < $currentTime) { |
|
| 119 | + return; |
|
| 120 | + } |
|
| 121 | + |
|
| 122 | + // Strip off mailto: |
|
| 123 | + $recipient = substr($iTipMessage->recipient, 7); |
|
| 124 | + if (!$this->emailValidator->isValid($recipient)) { |
|
| 125 | + // Nothing to send if the recipient doesn't have a valid email address |
|
| 126 | + $iTipMessage->scheduleStatus = '5.0; EMail delivery failed'; |
|
| 127 | + return; |
|
| 128 | + } |
|
| 129 | + $recipientName = $iTipMessage->recipientName ? (string)$iTipMessage->recipientName : null; |
|
| 130 | + |
|
| 131 | + $newEvents = $iTipMessage->message; |
|
| 132 | + $oldEvents = $this->getVCalendar(); |
|
| 133 | + |
|
| 134 | + $modified = $this->eventComparisonService->findModified($newEvents, $oldEvents); |
|
| 135 | + /** @var VEvent $vEvent */ |
|
| 136 | + $vEvent = array_pop($modified['new']); |
|
| 137 | + /** @var VEvent $oldVevent */ |
|
| 138 | + $oldVevent = !empty($modified['old']) && is_array($modified['old']) ? array_pop($modified['old']) : null; |
|
| 139 | + $isModified = isset($oldVevent); |
|
| 140 | + |
|
| 141 | + // No changed events after all - this shouldn't happen if there is significant change yet here we are |
|
| 142 | + // The scheduling status is debatable |
|
| 143 | + if (empty($vEvent)) { |
|
| 144 | + $this->logger->warning('iTip message said the change was significant but comparison did not detect any updated VEvents'); |
|
| 145 | + $iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email'; |
|
| 146 | + return; |
|
| 147 | + } |
|
| 148 | + |
|
| 149 | + // we (should) have one event component left |
|
| 150 | + // as the ITip\Broker creates one iTip message per change |
|
| 151 | + // and triggers the "schedule" event once per message |
|
| 152 | + // we also might not have an old event as this could be a new |
|
| 153 | + // invitation, or a new recurrence exception |
|
| 154 | + $attendee = $this->imipService->getCurrentAttendee($iTipMessage); |
|
| 155 | + if ($attendee === null) { |
|
| 156 | + $uid = $vEvent->UID ?? 'no UID found'; |
|
| 157 | + $this->logger->debug('Could not find recipient ' . $recipient . ' as attendee for event with UID ' . $uid); |
|
| 158 | + $iTipMessage->scheduleStatus = '5.0; EMail delivery failed'; |
|
| 159 | + return; |
|
| 160 | + } |
|
| 161 | + // Don't send emails to rooms, resources and circles |
|
| 162 | + if ($this->imipService->isRoomOrResource($attendee) |
|
| 163 | + || $this->imipService->isCircle($attendee)) { |
|
| 164 | + $this->logger->debug('No invitation sent as recipient is room, resource or circle', [ |
|
| 165 | + 'attendee' => $recipient, |
|
| 166 | + ]); |
|
| 167 | + $iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email'; |
|
| 168 | + return; |
|
| 169 | + } |
|
| 170 | + $this->imipService->setL10n($attendee); |
|
| 171 | + |
|
| 172 | + // Build the sender name. |
|
| 173 | + // Due to a bug in sabre, the senderName property for an iTIP message can actually also be a VObject Property |
|
| 174 | + // If the iTIP message senderName is null or empty use the user session name as the senderName |
|
| 175 | + if (($iTipMessage->senderName instanceof Parameter) && !empty(trim($iTipMessage->senderName->getValue()))) { |
|
| 176 | + $senderName = trim($iTipMessage->senderName->getValue()); |
|
| 177 | + } elseif (is_string($iTipMessage->senderName) && !empty(trim($iTipMessage->senderName))) { |
|
| 178 | + $senderName = trim($iTipMessage->senderName); |
|
| 179 | + } elseif ($this->userSession->getUser() !== null) { |
|
| 180 | + $senderName = trim($this->userSession->getUser()->getDisplayName()); |
|
| 181 | + } else { |
|
| 182 | + $senderName = ''; |
|
| 183 | + } |
|
| 184 | + |
|
| 185 | + $sender = substr($iTipMessage->sender, 7); |
|
| 186 | + |
|
| 187 | + $replyingAttendee = null; |
|
| 188 | + switch (strtolower($iTipMessage->method)) { |
|
| 189 | + case self::METHOD_REPLY: |
|
| 190 | + $method = self::METHOD_REPLY; |
|
| 191 | + $data = $this->imipService->buildReplyBodyData($vEvent); |
|
| 192 | + $replyingAttendee = $this->imipService->getReplyingAttendee($iTipMessage); |
|
| 193 | + break; |
|
| 194 | + case self::METHOD_CANCEL: |
|
| 195 | + $method = self::METHOD_CANCEL; |
|
| 196 | + $data = $this->imipService->buildCancelledBodyData($vEvent); |
|
| 197 | + break; |
|
| 198 | + default: |
|
| 199 | + $method = self::METHOD_REQUEST; |
|
| 200 | + $data = $this->imipService->buildBodyData($vEvent, $oldVevent); |
|
| 201 | + break; |
|
| 202 | + } |
|
| 203 | + |
|
| 204 | + $data['attendee_name'] = ($recipientName ?: $recipient); |
|
| 205 | + $data['invitee_name'] = ($senderName ?: $sender); |
|
| 206 | + |
|
| 207 | + $fromEMail = Util::getDefaultEmailAddress('invitations-noreply'); |
|
| 208 | + $fromName = $this->imipService->getFrom($senderName, $this->defaults->getName()); |
|
| 209 | + |
|
| 210 | + $template = $this->mailer->createEMailTemplate('dav.calendarInvite.' . $method, $data); |
|
| 211 | + $template->addHeader(); |
|
| 212 | + |
|
| 213 | + $this->imipService->addSubjectAndHeading($template, $method, $data['invitee_name'], $data['meeting_title'], $isModified, $replyingAttendee); |
|
| 214 | + $this->imipService->addBulletList($template, $vEvent, $data); |
|
| 215 | + |
|
| 216 | + // Only add response buttons to invitation requests: Fix Issue #11230 |
|
| 217 | + if (strcasecmp($method, self::METHOD_REQUEST) === 0 && $this->imipService->getAttendeeRsvpOrReqForParticipant($attendee)) { |
|
| 218 | + |
|
| 219 | + /* |
|
| 220 | 220 | ** Only offer invitation accept/reject buttons, which link back to the |
| 221 | 221 | ** nextcloud server, to recipients who can access the nextcloud server via |
| 222 | 222 | ** their internet/intranet. Issue #12156 |
@@ -235,106 +235,106 @@ discard block |
||
| 235 | 235 | ** To suppress URLs entirely, set invitation_link_recipients to boolean "no". |
| 236 | 236 | */ |
| 237 | 237 | |
| 238 | - $recipientDomain = substr(strrchr($recipient, '@'), 1); |
|
| 239 | - $invitationLinkRecipients = explode(',', preg_replace('/\s+/', '', strtolower($this->config->getValueString('dav', 'invitation_link_recipients', 'yes')))); |
|
| 240 | - |
|
| 241 | - if (strcmp('yes', $invitationLinkRecipients[0]) === 0 |
|
| 242 | - || in_array(strtolower($recipient), $invitationLinkRecipients) |
|
| 243 | - || in_array(strtolower($recipientDomain), $invitationLinkRecipients)) { |
|
| 244 | - $token = $this->imipService->createInvitationToken($iTipMessage, $vEvent, $lastOccurrence); |
|
| 245 | - $this->imipService->addResponseButtons($template, $token); |
|
| 246 | - $this->imipService->addMoreOptionsButton($template, $token); |
|
| 247 | - } |
|
| 248 | - } |
|
| 249 | - |
|
| 250 | - $template->addFooter(); |
|
| 251 | - // convert iTip Message to string |
|
| 252 | - $itip_msg = $iTipMessage->message->serialize(); |
|
| 253 | - |
|
| 254 | - $mailService = null; |
|
| 255 | - |
|
| 256 | - try { |
|
| 257 | - if ($this->config->getValueBool('core', 'mail_providers_enabled', true)) { |
|
| 258 | - // retrieve user object |
|
| 259 | - $user = $this->userSession->getUser(); |
|
| 260 | - if ($user !== null) { |
|
| 261 | - // retrieve appropriate service with the same address as sender |
|
| 262 | - $mailService = $this->mailManager->findServiceByAddress($user->getUID(), $sender); |
|
| 263 | - } |
|
| 264 | - } |
|
| 265 | - |
|
| 266 | - // The display name in Nextcloud can use utf-8. |
|
| 267 | - // As the default charset for text/* is us-ascii, it's important to explicitly define it. |
|
| 268 | - // See https://www.rfc-editor.org/rfc/rfc6047.html#section-2.4. |
|
| 269 | - $contentType = 'text/calendar; method=' . $iTipMessage->method . '; charset="utf-8"'; |
|
| 270 | - |
|
| 271 | - // evaluate if a mail service was found and has sending capabilities |
|
| 272 | - if ($mailService instanceof IMessageSend) { |
|
| 273 | - // construct mail message and set required parameters |
|
| 274 | - $message = $mailService->initiateMessage(); |
|
| 275 | - $message->setFrom( |
|
| 276 | - (new Address($sender, $fromName)) |
|
| 277 | - ); |
|
| 278 | - $message->setTo( |
|
| 279 | - (new Address($recipient, $recipientName)) |
|
| 280 | - ); |
|
| 281 | - $message->setSubject($template->renderSubject()); |
|
| 282 | - $message->setBodyPlain($template->renderText()); |
|
| 283 | - $message->setBodyHtml($template->renderHtml()); |
|
| 284 | - // Adding name=event.ics is a trick to make the invitation also appear |
|
| 285 | - // as a file attachment in mail clients like Thunderbird or Evolution. |
|
| 286 | - $message->setAttachments((new Attachment( |
|
| 287 | - $itip_msg, |
|
| 288 | - null, |
|
| 289 | - $contentType . '; name=event.ics', |
|
| 290 | - true |
|
| 291 | - ))); |
|
| 292 | - // send message |
|
| 293 | - $mailService->sendMessage($message); |
|
| 294 | - } else { |
|
| 295 | - // construct symfony mailer message and set required parameters |
|
| 296 | - $message = $this->mailer->createMessage(); |
|
| 297 | - $message->setFrom([$fromEMail => $fromName]); |
|
| 298 | - $message->setTo( |
|
| 299 | - (($recipientName !== null) ? [$recipient => $recipientName] : [$recipient]) |
|
| 300 | - ); |
|
| 301 | - $message->setReplyTo( |
|
| 302 | - (($senderName !== null) ? [$sender => $senderName] : [$sender]) |
|
| 303 | - ); |
|
| 304 | - $message->useTemplate($template); |
|
| 305 | - // Using a different content type because Symfony Mailer/Mime will append the name to |
|
| 306 | - // the content type header and attachInline does not allow null. |
|
| 307 | - $message->attachInline( |
|
| 308 | - $itip_msg, |
|
| 309 | - 'event.ics', |
|
| 310 | - $contentType, |
|
| 311 | - ); |
|
| 312 | - $failed = $this->mailer->send($message); |
|
| 313 | - } |
|
| 314 | - |
|
| 315 | - $iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip'; |
|
| 316 | - if (!empty($failed)) { |
|
| 317 | - $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]); |
|
| 318 | - $iTipMessage->scheduleStatus = '5.0; EMail delivery failed'; |
|
| 319 | - } |
|
| 320 | - } catch (\Exception $ex) { |
|
| 321 | - $this->logger->error($ex->getMessage(), ['app' => 'dav', 'exception' => $ex]); |
|
| 322 | - $iTipMessage->scheduleStatus = '5.0; EMail delivery failed'; |
|
| 323 | - } |
|
| 324 | - } |
|
| 325 | - |
|
| 326 | - /** |
|
| 327 | - * @return ?VCalendar |
|
| 328 | - */ |
|
| 329 | - public function getVCalendar(): ?VCalendar { |
|
| 330 | - return $this->vCalendar; |
|
| 331 | - } |
|
| 332 | - |
|
| 333 | - /** |
|
| 334 | - * @param ?VCalendar $vCalendar |
|
| 335 | - */ |
|
| 336 | - public function setVCalendar(?VCalendar $vCalendar): void { |
|
| 337 | - $this->vCalendar = $vCalendar; |
|
| 338 | - } |
|
| 238 | + $recipientDomain = substr(strrchr($recipient, '@'), 1); |
|
| 239 | + $invitationLinkRecipients = explode(',', preg_replace('/\s+/', '', strtolower($this->config->getValueString('dav', 'invitation_link_recipients', 'yes')))); |
|
| 240 | + |
|
| 241 | + if (strcmp('yes', $invitationLinkRecipients[0]) === 0 |
|
| 242 | + || in_array(strtolower($recipient), $invitationLinkRecipients) |
|
| 243 | + || in_array(strtolower($recipientDomain), $invitationLinkRecipients)) { |
|
| 244 | + $token = $this->imipService->createInvitationToken($iTipMessage, $vEvent, $lastOccurrence); |
|
| 245 | + $this->imipService->addResponseButtons($template, $token); |
|
| 246 | + $this->imipService->addMoreOptionsButton($template, $token); |
|
| 247 | + } |
|
| 248 | + } |
|
| 249 | + |
|
| 250 | + $template->addFooter(); |
|
| 251 | + // convert iTip Message to string |
|
| 252 | + $itip_msg = $iTipMessage->message->serialize(); |
|
| 253 | + |
|
| 254 | + $mailService = null; |
|
| 255 | + |
|
| 256 | + try { |
|
| 257 | + if ($this->config->getValueBool('core', 'mail_providers_enabled', true)) { |
|
| 258 | + // retrieve user object |
|
| 259 | + $user = $this->userSession->getUser(); |
|
| 260 | + if ($user !== null) { |
|
| 261 | + // retrieve appropriate service with the same address as sender |
|
| 262 | + $mailService = $this->mailManager->findServiceByAddress($user->getUID(), $sender); |
|
| 263 | + } |
|
| 264 | + } |
|
| 265 | + |
|
| 266 | + // The display name in Nextcloud can use utf-8. |
|
| 267 | + // As the default charset for text/* is us-ascii, it's important to explicitly define it. |
|
| 268 | + // See https://www.rfc-editor.org/rfc/rfc6047.html#section-2.4. |
|
| 269 | + $contentType = 'text/calendar; method=' . $iTipMessage->method . '; charset="utf-8"'; |
|
| 270 | + |
|
| 271 | + // evaluate if a mail service was found and has sending capabilities |
|
| 272 | + if ($mailService instanceof IMessageSend) { |
|
| 273 | + // construct mail message and set required parameters |
|
| 274 | + $message = $mailService->initiateMessage(); |
|
| 275 | + $message->setFrom( |
|
| 276 | + (new Address($sender, $fromName)) |
|
| 277 | + ); |
|
| 278 | + $message->setTo( |
|
| 279 | + (new Address($recipient, $recipientName)) |
|
| 280 | + ); |
|
| 281 | + $message->setSubject($template->renderSubject()); |
|
| 282 | + $message->setBodyPlain($template->renderText()); |
|
| 283 | + $message->setBodyHtml($template->renderHtml()); |
|
| 284 | + // Adding name=event.ics is a trick to make the invitation also appear |
|
| 285 | + // as a file attachment in mail clients like Thunderbird or Evolution. |
|
| 286 | + $message->setAttachments((new Attachment( |
|
| 287 | + $itip_msg, |
|
| 288 | + null, |
|
| 289 | + $contentType . '; name=event.ics', |
|
| 290 | + true |
|
| 291 | + ))); |
|
| 292 | + // send message |
|
| 293 | + $mailService->sendMessage($message); |
|
| 294 | + } else { |
|
| 295 | + // construct symfony mailer message and set required parameters |
|
| 296 | + $message = $this->mailer->createMessage(); |
|
| 297 | + $message->setFrom([$fromEMail => $fromName]); |
|
| 298 | + $message->setTo( |
|
| 299 | + (($recipientName !== null) ? [$recipient => $recipientName] : [$recipient]) |
|
| 300 | + ); |
|
| 301 | + $message->setReplyTo( |
|
| 302 | + (($senderName !== null) ? [$sender => $senderName] : [$sender]) |
|
| 303 | + ); |
|
| 304 | + $message->useTemplate($template); |
|
| 305 | + // Using a different content type because Symfony Mailer/Mime will append the name to |
|
| 306 | + // the content type header and attachInline does not allow null. |
|
| 307 | + $message->attachInline( |
|
| 308 | + $itip_msg, |
|
| 309 | + 'event.ics', |
|
| 310 | + $contentType, |
|
| 311 | + ); |
|
| 312 | + $failed = $this->mailer->send($message); |
|
| 313 | + } |
|
| 314 | + |
|
| 315 | + $iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip'; |
|
| 316 | + if (!empty($failed)) { |
|
| 317 | + $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]); |
|
| 318 | + $iTipMessage->scheduleStatus = '5.0; EMail delivery failed'; |
|
| 319 | + } |
|
| 320 | + } catch (\Exception $ex) { |
|
| 321 | + $this->logger->error($ex->getMessage(), ['app' => 'dav', 'exception' => $ex]); |
|
| 322 | + $iTipMessage->scheduleStatus = '5.0; EMail delivery failed'; |
|
| 323 | + } |
|
| 324 | + } |
|
| 325 | + |
|
| 326 | + /** |
|
| 327 | + * @return ?VCalendar |
|
| 328 | + */ |
|
| 329 | + public function getVCalendar(): ?VCalendar { |
|
| 330 | + return $this->vCalendar; |
|
| 331 | + } |
|
| 332 | + |
|
| 333 | + /** |
|
| 334 | + * @param ?VCalendar $vCalendar |
|
| 335 | + */ |
|
| 336 | + public function setVCalendar(?VCalendar $vCalendar): void { |
|
| 337 | + $this->vCalendar = $vCalendar; |
|
| 338 | + } |
|
| 339 | 339 | |
| 340 | 340 | } |
@@ -31,414 +31,414 @@ |
||
| 31 | 31 | * @package OCA\DAV\CalDAV\Reminder\NotificationProvider |
| 32 | 32 | */ |
| 33 | 33 | class EmailProvider extends AbstractProvider { |
| 34 | - /** @var string */ |
|
| 35 | - public const NOTIFICATION_TYPE = 'EMAIL'; |
|
| 36 | - |
|
| 37 | - public function __construct( |
|
| 38 | - IConfig $config, |
|
| 39 | - private IMailer $mailer, |
|
| 40 | - LoggerInterface $logger, |
|
| 41 | - L10NFactory $l10nFactory, |
|
| 42 | - IURLGenerator $urlGenerator, |
|
| 43 | - private IEmailValidator $emailValidator, |
|
| 44 | - ) { |
|
| 45 | - parent::__construct($logger, $l10nFactory, $urlGenerator, $config); |
|
| 46 | - } |
|
| 47 | - |
|
| 48 | - /** |
|
| 49 | - * Send out notification via email |
|
| 50 | - * |
|
| 51 | - * @param VEvent $vevent |
|
| 52 | - * @param string|null $calendarDisplayName |
|
| 53 | - * @param string[] $principalEmailAddresses |
|
| 54 | - * @param array $users |
|
| 55 | - * @throws \Exception |
|
| 56 | - */ |
|
| 57 | - public function send(VEvent $vevent, |
|
| 58 | - ?string $calendarDisplayName, |
|
| 59 | - array $principalEmailAddresses, |
|
| 60 | - array $users = []):void { |
|
| 61 | - $fallbackLanguage = $this->getFallbackLanguage(); |
|
| 62 | - |
|
| 63 | - $organizerEmailAddress = null; |
|
| 64 | - if (isset($vevent->ORGANIZER)) { |
|
| 65 | - $organizerEmailAddress = $this->getEMailAddressOfAttendee($vevent->ORGANIZER); |
|
| 66 | - } |
|
| 67 | - |
|
| 68 | - $emailAddressesOfSharees = $this->getEMailAddressesOfAllUsersWithWriteAccessToCalendar($users); |
|
| 69 | - $emailAddressesOfAttendees = []; |
|
| 70 | - if (count($principalEmailAddresses) === 0 |
|
| 71 | - || ($organizerEmailAddress && in_array($organizerEmailAddress, $principalEmailAddresses, true)) |
|
| 72 | - ) { |
|
| 73 | - $emailAddressesOfAttendees = $this->getAllEMailAddressesFromEvent($vevent); |
|
| 74 | - } |
|
| 75 | - |
|
| 76 | - // Quote from php.net: |
|
| 77 | - // If the input arrays have the same string keys, then the later value for that key will overwrite the previous one. |
|
| 78 | - // => if there are duplicate email addresses, it will always take the system value |
|
| 79 | - $emailAddresses = array_merge( |
|
| 80 | - $emailAddressesOfAttendees, |
|
| 81 | - $emailAddressesOfSharees |
|
| 82 | - ); |
|
| 83 | - |
|
| 84 | - $sortedByLanguage = $this->sortEMailAddressesByLanguage($emailAddresses, $fallbackLanguage); |
|
| 85 | - $organizer = $this->getOrganizerEMailAndNameFromEvent($vevent); |
|
| 86 | - |
|
| 87 | - foreach ($sortedByLanguage as $lang => $emailAddresses) { |
|
| 88 | - if (!$this->hasL10NForLang($lang)) { |
|
| 89 | - $lang = $fallbackLanguage; |
|
| 90 | - } |
|
| 91 | - $l10n = $this->getL10NForLang($lang); |
|
| 92 | - $fromEMail = Util::getDefaultEmailAddress('reminders-noreply'); |
|
| 93 | - |
|
| 94 | - $template = $this->mailer->createEMailTemplate('dav.calendarReminder'); |
|
| 95 | - $template->addHeader(); |
|
| 96 | - $this->addSubjectAndHeading($template, $l10n, $vevent); |
|
| 97 | - $this->addBulletList($template, $l10n, $calendarDisplayName ?? $this->getCalendarDisplayNameFallback($lang), $vevent); |
|
| 98 | - $template->addFooter(); |
|
| 99 | - |
|
| 100 | - foreach ($emailAddresses as $emailAddress) { |
|
| 101 | - if (!$this->emailValidator->isValid($emailAddress)) { |
|
| 102 | - $this->logger->error('Email address {address} for reminder notification is incorrect', ['app' => 'dav', 'address' => $emailAddress]); |
|
| 103 | - continue; |
|
| 104 | - } |
|
| 105 | - |
|
| 106 | - $message = $this->mailer->createMessage(); |
|
| 107 | - $message->setFrom([$fromEMail]); |
|
| 108 | - if ($organizer) { |
|
| 109 | - $message->setReplyTo($organizer); |
|
| 110 | - } |
|
| 111 | - $message->setTo([$emailAddress]); |
|
| 112 | - $message->useTemplate($template); |
|
| 113 | - $message->setAutoSubmitted(AutoSubmitted::VALUE_AUTO_GENERATED); |
|
| 114 | - |
|
| 115 | - try { |
|
| 116 | - $failed = $this->mailer->send($message); |
|
| 117 | - if ($failed) { |
|
| 118 | - $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]); |
|
| 119 | - } |
|
| 120 | - } catch (\Exception $ex) { |
|
| 121 | - $this->logger->error($ex->getMessage(), ['app' => 'dav', 'exception' => $ex]); |
|
| 122 | - } |
|
| 123 | - } |
|
| 124 | - } |
|
| 125 | - } |
|
| 126 | - |
|
| 127 | - /** |
|
| 128 | - * @param IEMailTemplate $template |
|
| 129 | - * @param IL10N $l10n |
|
| 130 | - * @param VEvent $vevent |
|
| 131 | - */ |
|
| 132 | - private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n, VEvent $vevent):void { |
|
| 133 | - $template->setSubject('Notification: ' . $this->getTitleFromVEvent($vevent, $l10n)); |
|
| 134 | - $template->addHeading($this->getTitleFromVEvent($vevent, $l10n)); |
|
| 135 | - } |
|
| 136 | - |
|
| 137 | - /** |
|
| 138 | - * @param IEMailTemplate $template |
|
| 139 | - * @param IL10N $l10n |
|
| 140 | - * @param string $calendarDisplayName |
|
| 141 | - * @param array $eventData |
|
| 142 | - */ |
|
| 143 | - private function addBulletList(IEMailTemplate $template, |
|
| 144 | - IL10N $l10n, |
|
| 145 | - string $calendarDisplayName, |
|
| 146 | - VEvent $vevent):void { |
|
| 147 | - $template->addBodyListItem($calendarDisplayName, $l10n->t('Calendar:'), |
|
| 148 | - $this->getAbsoluteImagePath('actions/info.png')); |
|
| 149 | - |
|
| 150 | - $template->addBodyListItem($this->generateDateString($l10n, $vevent), $l10n->t('Date:'), |
|
| 151 | - $this->getAbsoluteImagePath('places/calendar.png')); |
|
| 152 | - |
|
| 153 | - if (isset($vevent->LOCATION)) { |
|
| 154 | - $template->addBodyListItem((string)$vevent->LOCATION, $l10n->t('Where:'), |
|
| 155 | - $this->getAbsoluteImagePath('actions/address.png')); |
|
| 156 | - } |
|
| 157 | - if (isset($vevent->DESCRIPTION)) { |
|
| 158 | - $template->addBodyListItem((string)$vevent->DESCRIPTION, $l10n->t('Description:'), |
|
| 159 | - $this->getAbsoluteImagePath('actions/more.png')); |
|
| 160 | - } |
|
| 161 | - } |
|
| 162 | - |
|
| 163 | - private function getAbsoluteImagePath(string $path):string { |
|
| 164 | - return $this->urlGenerator->getAbsoluteURL( |
|
| 165 | - $this->urlGenerator->imagePath('core', $path) |
|
| 166 | - ); |
|
| 167 | - } |
|
| 168 | - |
|
| 169 | - /** |
|
| 170 | - * @param VEvent $vevent |
|
| 171 | - * @return array|null |
|
| 172 | - */ |
|
| 173 | - private function getOrganizerEMailAndNameFromEvent(VEvent $vevent):?array { |
|
| 174 | - if (!$vevent->ORGANIZER) { |
|
| 175 | - return null; |
|
| 176 | - } |
|
| 177 | - |
|
| 178 | - $organizer = $vevent->ORGANIZER; |
|
| 179 | - if (strcasecmp($organizer->getValue(), 'mailto:') !== 0) { |
|
| 180 | - return null; |
|
| 181 | - } |
|
| 182 | - |
|
| 183 | - $organizerEMail = substr($organizer->getValue(), 7); |
|
| 184 | - |
|
| 185 | - if (!$this->emailValidator->isValid($organizerEMail)) { |
|
| 186 | - return null; |
|
| 187 | - } |
|
| 188 | - |
|
| 189 | - $name = $organizer->offsetGet('CN'); |
|
| 190 | - if ($name instanceof Parameter) { |
|
| 191 | - return [$organizerEMail => $name]; |
|
| 192 | - } |
|
| 193 | - |
|
| 194 | - return [$organizerEMail]; |
|
| 195 | - } |
|
| 196 | - |
|
| 197 | - /** |
|
| 198 | - * @param array<string, array{LANG?: string}> $emails |
|
| 199 | - * @return array<string, string[]> |
|
| 200 | - */ |
|
| 201 | - private function sortEMailAddressesByLanguage(array $emails, |
|
| 202 | - string $defaultLanguage):array { |
|
| 203 | - $sortedByLanguage = []; |
|
| 204 | - |
|
| 205 | - foreach ($emails as $emailAddress => $parameters) { |
|
| 206 | - if (isset($parameters['LANG'])) { |
|
| 207 | - $lang = $parameters['LANG']; |
|
| 208 | - } else { |
|
| 209 | - $lang = $defaultLanguage; |
|
| 210 | - } |
|
| 211 | - |
|
| 212 | - if (!isset($sortedByLanguage[$lang])) { |
|
| 213 | - $sortedByLanguage[$lang] = []; |
|
| 214 | - } |
|
| 215 | - |
|
| 216 | - $sortedByLanguage[$lang][] = $emailAddress; |
|
| 217 | - } |
|
| 218 | - |
|
| 219 | - return $sortedByLanguage; |
|
| 220 | - } |
|
| 221 | - |
|
| 222 | - /** |
|
| 223 | - * @param VEvent $vevent |
|
| 224 | - * @return array<string, array{LANG?: string}> |
|
| 225 | - */ |
|
| 226 | - private function getAllEMailAddressesFromEvent(VEvent $vevent):array { |
|
| 227 | - $emailAddresses = []; |
|
| 228 | - |
|
| 229 | - if (isset($vevent->ATTENDEE)) { |
|
| 230 | - foreach ($vevent->ATTENDEE as $attendee) { |
|
| 231 | - if (!($attendee instanceof VObject\Property)) { |
|
| 232 | - continue; |
|
| 233 | - } |
|
| 234 | - |
|
| 235 | - $cuType = $this->getCUTypeOfAttendee($attendee); |
|
| 236 | - if (\in_array($cuType, ['RESOURCE', 'ROOM', 'UNKNOWN'])) { |
|
| 237 | - // Don't send emails to things |
|
| 238 | - continue; |
|
| 239 | - } |
|
| 240 | - |
|
| 241 | - $partstat = $this->getPartstatOfAttendee($attendee); |
|
| 242 | - if ($partstat === 'DECLINED') { |
|
| 243 | - // Don't send out emails to people who declined |
|
| 244 | - continue; |
|
| 245 | - } |
|
| 246 | - if ($partstat === 'DELEGATED') { |
|
| 247 | - $delegates = $attendee->offsetGet('DELEGATED-TO'); |
|
| 248 | - if (!($delegates instanceof VObject\Parameter)) { |
|
| 249 | - continue; |
|
| 250 | - } |
|
| 251 | - |
|
| 252 | - $emailAddressesOfDelegates = $delegates->getParts(); |
|
| 253 | - foreach ($emailAddressesOfDelegates as $addressesOfDelegate) { |
|
| 254 | - if (strcasecmp($addressesOfDelegate, 'mailto:') === 0) { |
|
| 255 | - $delegateEmail = substr($addressesOfDelegate, 7); |
|
| 256 | - if ($this->emailValidator->isValid($delegateEmail)) { |
|
| 257 | - $emailAddresses[$delegateEmail] = []; |
|
| 258 | - } |
|
| 259 | - } |
|
| 260 | - } |
|
| 261 | - |
|
| 262 | - continue; |
|
| 263 | - } |
|
| 264 | - |
|
| 265 | - $emailAddressOfAttendee = $this->getEMailAddressOfAttendee($attendee); |
|
| 266 | - if ($emailAddressOfAttendee !== null) { |
|
| 267 | - $properties = []; |
|
| 268 | - |
|
| 269 | - $langProp = $attendee->offsetGet('LANG'); |
|
| 270 | - if ($langProp instanceof VObject\Parameter && $langProp->getValue() !== null) { |
|
| 271 | - $properties['LANG'] = $langProp->getValue(); |
|
| 272 | - } |
|
| 273 | - |
|
| 274 | - $emailAddresses[$emailAddressOfAttendee] = $properties; |
|
| 275 | - } |
|
| 276 | - } |
|
| 277 | - } |
|
| 278 | - |
|
| 279 | - if (isset($vevent->ORGANIZER) && $this->hasAttendeeMailURI($vevent->ORGANIZER)) { |
|
| 280 | - $organizerEmailAddress = $this->getEMailAddressOfAttendee($vevent->ORGANIZER); |
|
| 281 | - if ($organizerEmailAddress !== null) { |
|
| 282 | - $emailAddresses[$organizerEmailAddress] = []; |
|
| 283 | - } |
|
| 284 | - } |
|
| 285 | - |
|
| 286 | - return $emailAddresses; |
|
| 287 | - } |
|
| 288 | - |
|
| 289 | - private function getCUTypeOfAttendee(VObject\Property $attendee):string { |
|
| 290 | - $cuType = $attendee->offsetGet('CUTYPE'); |
|
| 291 | - if ($cuType instanceof VObject\Parameter) { |
|
| 292 | - return strtoupper($cuType->getValue()); |
|
| 293 | - } |
|
| 294 | - |
|
| 295 | - return 'INDIVIDUAL'; |
|
| 296 | - } |
|
| 297 | - |
|
| 298 | - private function getPartstatOfAttendee(VObject\Property $attendee):string { |
|
| 299 | - $partstat = $attendee->offsetGet('PARTSTAT'); |
|
| 300 | - if ($partstat instanceof VObject\Parameter) { |
|
| 301 | - return strtoupper($partstat->getValue()); |
|
| 302 | - } |
|
| 303 | - |
|
| 304 | - return 'NEEDS-ACTION'; |
|
| 305 | - } |
|
| 306 | - |
|
| 307 | - private function hasAttendeeMailURI(VObject\Property $attendee): bool { |
|
| 308 | - return stripos($attendee->getValue(), 'mailto:') === 0; |
|
| 309 | - } |
|
| 310 | - |
|
| 311 | - private function getEMailAddressOfAttendee(VObject\Property $attendee): ?string { |
|
| 312 | - if (!$this->hasAttendeeMailURI($attendee)) { |
|
| 313 | - return null; |
|
| 314 | - } |
|
| 315 | - $attendeeEMail = substr($attendee->getValue(), 7); |
|
| 316 | - if (!$this->emailValidator->isValid($attendeeEMail)) { |
|
| 317 | - return null; |
|
| 318 | - } |
|
| 319 | - |
|
| 320 | - return $attendeeEMail; |
|
| 321 | - } |
|
| 322 | - |
|
| 323 | - /** |
|
| 324 | - * @param IUser[] $users |
|
| 325 | - * @return array<string, array{LANG?: string}> |
|
| 326 | - */ |
|
| 327 | - private function getEMailAddressesOfAllUsersWithWriteAccessToCalendar(array $users):array { |
|
| 328 | - $emailAddresses = []; |
|
| 329 | - |
|
| 330 | - foreach ($users as $user) { |
|
| 331 | - $emailAddress = $user->getEMailAddress(); |
|
| 332 | - if ($emailAddress) { |
|
| 333 | - $lang = $this->l10nFactory->getUserLanguage($user); |
|
| 334 | - if ($lang) { |
|
| 335 | - $emailAddresses[$emailAddress] = [ |
|
| 336 | - 'LANG' => $lang, |
|
| 337 | - ]; |
|
| 338 | - } else { |
|
| 339 | - $emailAddresses[$emailAddress] = []; |
|
| 340 | - } |
|
| 341 | - } |
|
| 342 | - } |
|
| 343 | - |
|
| 344 | - return $emailAddresses; |
|
| 345 | - } |
|
| 346 | - |
|
| 347 | - /** |
|
| 348 | - * @throws \Exception |
|
| 349 | - */ |
|
| 350 | - private function generateDateString(IL10N $l10n, VEvent $vevent): string { |
|
| 351 | - $isAllDay = $vevent->DTSTART instanceof Property\ICalendar\Date; |
|
| 352 | - |
|
| 353 | - /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */ |
|
| 354 | - /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */ |
|
| 355 | - /** @var \DateTimeImmutable $dtstartDt */ |
|
| 356 | - $dtstartDt = $vevent->DTSTART->getDateTime(); |
|
| 357 | - /** @var \DateTimeImmutable $dtendDt */ |
|
| 358 | - $dtendDt = $this->getDTEndFromEvent($vevent)->getDateTime(); |
|
| 359 | - |
|
| 360 | - $diff = $dtstartDt->diff($dtendDt); |
|
| 361 | - |
|
| 362 | - $dtstartDt = new \DateTime($dtstartDt->format(\DateTimeInterface::ATOM)); |
|
| 363 | - $dtendDt = new \DateTime($dtendDt->format(\DateTimeInterface::ATOM)); |
|
| 364 | - |
|
| 365 | - if ($isAllDay) { |
|
| 366 | - // One day event |
|
| 367 | - if ($diff->days === 1) { |
|
| 368 | - return $this->getDateString($l10n, $dtstartDt); |
|
| 369 | - } |
|
| 370 | - |
|
| 371 | - return implode(' - ', [ |
|
| 372 | - $this->getDateString($l10n, $dtstartDt), |
|
| 373 | - $this->getDateString($l10n, $dtendDt), |
|
| 374 | - ]); |
|
| 375 | - } |
|
| 376 | - |
|
| 377 | - $startTimezone = $endTimezone = null; |
|
| 378 | - if (!$vevent->DTSTART->isFloating()) { |
|
| 379 | - $startTimezone = $vevent->DTSTART->getDateTime()->getTimezone()->getName(); |
|
| 380 | - $endTimezone = $this->getDTEndFromEvent($vevent)->getDateTime()->getTimezone()->getName(); |
|
| 381 | - } |
|
| 382 | - |
|
| 383 | - $localeStart = implode(', ', [ |
|
| 384 | - $this->getWeekDayName($l10n, $dtstartDt), |
|
| 385 | - $this->getDateTimeString($l10n, $dtstartDt) |
|
| 386 | - ]); |
|
| 387 | - |
|
| 388 | - // always show full date with timezone if timezones are different |
|
| 389 | - if ($startTimezone !== $endTimezone) { |
|
| 390 | - $localeEnd = implode(', ', [ |
|
| 391 | - $this->getWeekDayName($l10n, $dtendDt), |
|
| 392 | - $this->getDateTimeString($l10n, $dtendDt) |
|
| 393 | - ]); |
|
| 394 | - |
|
| 395 | - return $localeStart |
|
| 396 | - . ' (' . $startTimezone . ') ' |
|
| 397 | - . ' - ' |
|
| 398 | - . $localeEnd |
|
| 399 | - . ' (' . $endTimezone . ')'; |
|
| 400 | - } |
|
| 401 | - |
|
| 402 | - // Show only the time if the day is the same |
|
| 403 | - $localeEnd = $this->isDayEqual($dtstartDt, $dtendDt) |
|
| 404 | - ? $this->getTimeString($l10n, $dtendDt) |
|
| 405 | - : implode(', ', [ |
|
| 406 | - $this->getWeekDayName($l10n, $dtendDt), |
|
| 407 | - $this->getDateTimeString($l10n, $dtendDt) |
|
| 408 | - ]); |
|
| 409 | - |
|
| 410 | - return $localeStart |
|
| 411 | - . ' - ' |
|
| 412 | - . $localeEnd |
|
| 413 | - . ' (' . $startTimezone . ')'; |
|
| 414 | - } |
|
| 415 | - |
|
| 416 | - private function isDayEqual(DateTime $dtStart, |
|
| 417 | - DateTime $dtEnd):bool { |
|
| 418 | - return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d'); |
|
| 419 | - } |
|
| 420 | - |
|
| 421 | - private function getWeekDayName(IL10N $l10n, DateTime $dt):string { |
|
| 422 | - return (string)$l10n->l('weekdayName', $dt, ['width' => 'abbreviated']); |
|
| 423 | - } |
|
| 424 | - |
|
| 425 | - private function getDateString(IL10N $l10n, DateTime $dt):string { |
|
| 426 | - return (string)$l10n->l('date', $dt, ['width' => 'medium']); |
|
| 427 | - } |
|
| 428 | - |
|
| 429 | - private function getDateTimeString(IL10N $l10n, DateTime $dt):string { |
|
| 430 | - return (string)$l10n->l('datetime', $dt, ['width' => 'medium|short']); |
|
| 431 | - } |
|
| 432 | - |
|
| 433 | - private function getTimeString(IL10N $l10n, DateTime $dt):string { |
|
| 434 | - return (string)$l10n->l('time', $dt, ['width' => 'short']); |
|
| 435 | - } |
|
| 436 | - |
|
| 437 | - private function getTitleFromVEvent(VEvent $vevent, IL10N $l10n):string { |
|
| 438 | - if (isset($vevent->SUMMARY)) { |
|
| 439 | - return (string)$vevent->SUMMARY; |
|
| 440 | - } |
|
| 441 | - |
|
| 442 | - return $l10n->t('Untitled event'); |
|
| 443 | - } |
|
| 34 | + /** @var string */ |
|
| 35 | + public const NOTIFICATION_TYPE = 'EMAIL'; |
|
| 36 | + |
|
| 37 | + public function __construct( |
|
| 38 | + IConfig $config, |
|
| 39 | + private IMailer $mailer, |
|
| 40 | + LoggerInterface $logger, |
|
| 41 | + L10NFactory $l10nFactory, |
|
| 42 | + IURLGenerator $urlGenerator, |
|
| 43 | + private IEmailValidator $emailValidator, |
|
| 44 | + ) { |
|
| 45 | + parent::__construct($logger, $l10nFactory, $urlGenerator, $config); |
|
| 46 | + } |
|
| 47 | + |
|
| 48 | + /** |
|
| 49 | + * Send out notification via email |
|
| 50 | + * |
|
| 51 | + * @param VEvent $vevent |
|
| 52 | + * @param string|null $calendarDisplayName |
|
| 53 | + * @param string[] $principalEmailAddresses |
|
| 54 | + * @param array $users |
|
| 55 | + * @throws \Exception |
|
| 56 | + */ |
|
| 57 | + public function send(VEvent $vevent, |
|
| 58 | + ?string $calendarDisplayName, |
|
| 59 | + array $principalEmailAddresses, |
|
| 60 | + array $users = []):void { |
|
| 61 | + $fallbackLanguage = $this->getFallbackLanguage(); |
|
| 62 | + |
|
| 63 | + $organizerEmailAddress = null; |
|
| 64 | + if (isset($vevent->ORGANIZER)) { |
|
| 65 | + $organizerEmailAddress = $this->getEMailAddressOfAttendee($vevent->ORGANIZER); |
|
| 66 | + } |
|
| 67 | + |
|
| 68 | + $emailAddressesOfSharees = $this->getEMailAddressesOfAllUsersWithWriteAccessToCalendar($users); |
|
| 69 | + $emailAddressesOfAttendees = []; |
|
| 70 | + if (count($principalEmailAddresses) === 0 |
|
| 71 | + || ($organizerEmailAddress && in_array($organizerEmailAddress, $principalEmailAddresses, true)) |
|
| 72 | + ) { |
|
| 73 | + $emailAddressesOfAttendees = $this->getAllEMailAddressesFromEvent($vevent); |
|
| 74 | + } |
|
| 75 | + |
|
| 76 | + // Quote from php.net: |
|
| 77 | + // If the input arrays have the same string keys, then the later value for that key will overwrite the previous one. |
|
| 78 | + // => if there are duplicate email addresses, it will always take the system value |
|
| 79 | + $emailAddresses = array_merge( |
|
| 80 | + $emailAddressesOfAttendees, |
|
| 81 | + $emailAddressesOfSharees |
|
| 82 | + ); |
|
| 83 | + |
|
| 84 | + $sortedByLanguage = $this->sortEMailAddressesByLanguage($emailAddresses, $fallbackLanguage); |
|
| 85 | + $organizer = $this->getOrganizerEMailAndNameFromEvent($vevent); |
|
| 86 | + |
|
| 87 | + foreach ($sortedByLanguage as $lang => $emailAddresses) { |
|
| 88 | + if (!$this->hasL10NForLang($lang)) { |
|
| 89 | + $lang = $fallbackLanguage; |
|
| 90 | + } |
|
| 91 | + $l10n = $this->getL10NForLang($lang); |
|
| 92 | + $fromEMail = Util::getDefaultEmailAddress('reminders-noreply'); |
|
| 93 | + |
|
| 94 | + $template = $this->mailer->createEMailTemplate('dav.calendarReminder'); |
|
| 95 | + $template->addHeader(); |
|
| 96 | + $this->addSubjectAndHeading($template, $l10n, $vevent); |
|
| 97 | + $this->addBulletList($template, $l10n, $calendarDisplayName ?? $this->getCalendarDisplayNameFallback($lang), $vevent); |
|
| 98 | + $template->addFooter(); |
|
| 99 | + |
|
| 100 | + foreach ($emailAddresses as $emailAddress) { |
|
| 101 | + if (!$this->emailValidator->isValid($emailAddress)) { |
|
| 102 | + $this->logger->error('Email address {address} for reminder notification is incorrect', ['app' => 'dav', 'address' => $emailAddress]); |
|
| 103 | + continue; |
|
| 104 | + } |
|
| 105 | + |
|
| 106 | + $message = $this->mailer->createMessage(); |
|
| 107 | + $message->setFrom([$fromEMail]); |
|
| 108 | + if ($organizer) { |
|
| 109 | + $message->setReplyTo($organizer); |
|
| 110 | + } |
|
| 111 | + $message->setTo([$emailAddress]); |
|
| 112 | + $message->useTemplate($template); |
|
| 113 | + $message->setAutoSubmitted(AutoSubmitted::VALUE_AUTO_GENERATED); |
|
| 114 | + |
|
| 115 | + try { |
|
| 116 | + $failed = $this->mailer->send($message); |
|
| 117 | + if ($failed) { |
|
| 118 | + $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]); |
|
| 119 | + } |
|
| 120 | + } catch (\Exception $ex) { |
|
| 121 | + $this->logger->error($ex->getMessage(), ['app' => 'dav', 'exception' => $ex]); |
|
| 122 | + } |
|
| 123 | + } |
|
| 124 | + } |
|
| 125 | + } |
|
| 126 | + |
|
| 127 | + /** |
|
| 128 | + * @param IEMailTemplate $template |
|
| 129 | + * @param IL10N $l10n |
|
| 130 | + * @param VEvent $vevent |
|
| 131 | + */ |
|
| 132 | + private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n, VEvent $vevent):void { |
|
| 133 | + $template->setSubject('Notification: ' . $this->getTitleFromVEvent($vevent, $l10n)); |
|
| 134 | + $template->addHeading($this->getTitleFromVEvent($vevent, $l10n)); |
|
| 135 | + } |
|
| 136 | + |
|
| 137 | + /** |
|
| 138 | + * @param IEMailTemplate $template |
|
| 139 | + * @param IL10N $l10n |
|
| 140 | + * @param string $calendarDisplayName |
|
| 141 | + * @param array $eventData |
|
| 142 | + */ |
|
| 143 | + private function addBulletList(IEMailTemplate $template, |
|
| 144 | + IL10N $l10n, |
|
| 145 | + string $calendarDisplayName, |
|
| 146 | + VEvent $vevent):void { |
|
| 147 | + $template->addBodyListItem($calendarDisplayName, $l10n->t('Calendar:'), |
|
| 148 | + $this->getAbsoluteImagePath('actions/info.png')); |
|
| 149 | + |
|
| 150 | + $template->addBodyListItem($this->generateDateString($l10n, $vevent), $l10n->t('Date:'), |
|
| 151 | + $this->getAbsoluteImagePath('places/calendar.png')); |
|
| 152 | + |
|
| 153 | + if (isset($vevent->LOCATION)) { |
|
| 154 | + $template->addBodyListItem((string)$vevent->LOCATION, $l10n->t('Where:'), |
|
| 155 | + $this->getAbsoluteImagePath('actions/address.png')); |
|
| 156 | + } |
|
| 157 | + if (isset($vevent->DESCRIPTION)) { |
|
| 158 | + $template->addBodyListItem((string)$vevent->DESCRIPTION, $l10n->t('Description:'), |
|
| 159 | + $this->getAbsoluteImagePath('actions/more.png')); |
|
| 160 | + } |
|
| 161 | + } |
|
| 162 | + |
|
| 163 | + private function getAbsoluteImagePath(string $path):string { |
|
| 164 | + return $this->urlGenerator->getAbsoluteURL( |
|
| 165 | + $this->urlGenerator->imagePath('core', $path) |
|
| 166 | + ); |
|
| 167 | + } |
|
| 168 | + |
|
| 169 | + /** |
|
| 170 | + * @param VEvent $vevent |
|
| 171 | + * @return array|null |
|
| 172 | + */ |
|
| 173 | + private function getOrganizerEMailAndNameFromEvent(VEvent $vevent):?array { |
|
| 174 | + if (!$vevent->ORGANIZER) { |
|
| 175 | + return null; |
|
| 176 | + } |
|
| 177 | + |
|
| 178 | + $organizer = $vevent->ORGANIZER; |
|
| 179 | + if (strcasecmp($organizer->getValue(), 'mailto:') !== 0) { |
|
| 180 | + return null; |
|
| 181 | + } |
|
| 182 | + |
|
| 183 | + $organizerEMail = substr($organizer->getValue(), 7); |
|
| 184 | + |
|
| 185 | + if (!$this->emailValidator->isValid($organizerEMail)) { |
|
| 186 | + return null; |
|
| 187 | + } |
|
| 188 | + |
|
| 189 | + $name = $organizer->offsetGet('CN'); |
|
| 190 | + if ($name instanceof Parameter) { |
|
| 191 | + return [$organizerEMail => $name]; |
|
| 192 | + } |
|
| 193 | + |
|
| 194 | + return [$organizerEMail]; |
|
| 195 | + } |
|
| 196 | + |
|
| 197 | + /** |
|
| 198 | + * @param array<string, array{LANG?: string}> $emails |
|
| 199 | + * @return array<string, string[]> |
|
| 200 | + */ |
|
| 201 | + private function sortEMailAddressesByLanguage(array $emails, |
|
| 202 | + string $defaultLanguage):array { |
|
| 203 | + $sortedByLanguage = []; |
|
| 204 | + |
|
| 205 | + foreach ($emails as $emailAddress => $parameters) { |
|
| 206 | + if (isset($parameters['LANG'])) { |
|
| 207 | + $lang = $parameters['LANG']; |
|
| 208 | + } else { |
|
| 209 | + $lang = $defaultLanguage; |
|
| 210 | + } |
|
| 211 | + |
|
| 212 | + if (!isset($sortedByLanguage[$lang])) { |
|
| 213 | + $sortedByLanguage[$lang] = []; |
|
| 214 | + } |
|
| 215 | + |
|
| 216 | + $sortedByLanguage[$lang][] = $emailAddress; |
|
| 217 | + } |
|
| 218 | + |
|
| 219 | + return $sortedByLanguage; |
|
| 220 | + } |
|
| 221 | + |
|
| 222 | + /** |
|
| 223 | + * @param VEvent $vevent |
|
| 224 | + * @return array<string, array{LANG?: string}> |
|
| 225 | + */ |
|
| 226 | + private function getAllEMailAddressesFromEvent(VEvent $vevent):array { |
|
| 227 | + $emailAddresses = []; |
|
| 228 | + |
|
| 229 | + if (isset($vevent->ATTENDEE)) { |
|
| 230 | + foreach ($vevent->ATTENDEE as $attendee) { |
|
| 231 | + if (!($attendee instanceof VObject\Property)) { |
|
| 232 | + continue; |
|
| 233 | + } |
|
| 234 | + |
|
| 235 | + $cuType = $this->getCUTypeOfAttendee($attendee); |
|
| 236 | + if (\in_array($cuType, ['RESOURCE', 'ROOM', 'UNKNOWN'])) { |
|
| 237 | + // Don't send emails to things |
|
| 238 | + continue; |
|
| 239 | + } |
|
| 240 | + |
|
| 241 | + $partstat = $this->getPartstatOfAttendee($attendee); |
|
| 242 | + if ($partstat === 'DECLINED') { |
|
| 243 | + // Don't send out emails to people who declined |
|
| 244 | + continue; |
|
| 245 | + } |
|
| 246 | + if ($partstat === 'DELEGATED') { |
|
| 247 | + $delegates = $attendee->offsetGet('DELEGATED-TO'); |
|
| 248 | + if (!($delegates instanceof VObject\Parameter)) { |
|
| 249 | + continue; |
|
| 250 | + } |
|
| 251 | + |
|
| 252 | + $emailAddressesOfDelegates = $delegates->getParts(); |
|
| 253 | + foreach ($emailAddressesOfDelegates as $addressesOfDelegate) { |
|
| 254 | + if (strcasecmp($addressesOfDelegate, 'mailto:') === 0) { |
|
| 255 | + $delegateEmail = substr($addressesOfDelegate, 7); |
|
| 256 | + if ($this->emailValidator->isValid($delegateEmail)) { |
|
| 257 | + $emailAddresses[$delegateEmail] = []; |
|
| 258 | + } |
|
| 259 | + } |
|
| 260 | + } |
|
| 261 | + |
|
| 262 | + continue; |
|
| 263 | + } |
|
| 264 | + |
|
| 265 | + $emailAddressOfAttendee = $this->getEMailAddressOfAttendee($attendee); |
|
| 266 | + if ($emailAddressOfAttendee !== null) { |
|
| 267 | + $properties = []; |
|
| 268 | + |
|
| 269 | + $langProp = $attendee->offsetGet('LANG'); |
|
| 270 | + if ($langProp instanceof VObject\Parameter && $langProp->getValue() !== null) { |
|
| 271 | + $properties['LANG'] = $langProp->getValue(); |
|
| 272 | + } |
|
| 273 | + |
|
| 274 | + $emailAddresses[$emailAddressOfAttendee] = $properties; |
|
| 275 | + } |
|
| 276 | + } |
|
| 277 | + } |
|
| 278 | + |
|
| 279 | + if (isset($vevent->ORGANIZER) && $this->hasAttendeeMailURI($vevent->ORGANIZER)) { |
|
| 280 | + $organizerEmailAddress = $this->getEMailAddressOfAttendee($vevent->ORGANIZER); |
|
| 281 | + if ($organizerEmailAddress !== null) { |
|
| 282 | + $emailAddresses[$organizerEmailAddress] = []; |
|
| 283 | + } |
|
| 284 | + } |
|
| 285 | + |
|
| 286 | + return $emailAddresses; |
|
| 287 | + } |
|
| 288 | + |
|
| 289 | + private function getCUTypeOfAttendee(VObject\Property $attendee):string { |
|
| 290 | + $cuType = $attendee->offsetGet('CUTYPE'); |
|
| 291 | + if ($cuType instanceof VObject\Parameter) { |
|
| 292 | + return strtoupper($cuType->getValue()); |
|
| 293 | + } |
|
| 294 | + |
|
| 295 | + return 'INDIVIDUAL'; |
|
| 296 | + } |
|
| 297 | + |
|
| 298 | + private function getPartstatOfAttendee(VObject\Property $attendee):string { |
|
| 299 | + $partstat = $attendee->offsetGet('PARTSTAT'); |
|
| 300 | + if ($partstat instanceof VObject\Parameter) { |
|
| 301 | + return strtoupper($partstat->getValue()); |
|
| 302 | + } |
|
| 303 | + |
|
| 304 | + return 'NEEDS-ACTION'; |
|
| 305 | + } |
|
| 306 | + |
|
| 307 | + private function hasAttendeeMailURI(VObject\Property $attendee): bool { |
|
| 308 | + return stripos($attendee->getValue(), 'mailto:') === 0; |
|
| 309 | + } |
|
| 310 | + |
|
| 311 | + private function getEMailAddressOfAttendee(VObject\Property $attendee): ?string { |
|
| 312 | + if (!$this->hasAttendeeMailURI($attendee)) { |
|
| 313 | + return null; |
|
| 314 | + } |
|
| 315 | + $attendeeEMail = substr($attendee->getValue(), 7); |
|
| 316 | + if (!$this->emailValidator->isValid($attendeeEMail)) { |
|
| 317 | + return null; |
|
| 318 | + } |
|
| 319 | + |
|
| 320 | + return $attendeeEMail; |
|
| 321 | + } |
|
| 322 | + |
|
| 323 | + /** |
|
| 324 | + * @param IUser[] $users |
|
| 325 | + * @return array<string, array{LANG?: string}> |
|
| 326 | + */ |
|
| 327 | + private function getEMailAddressesOfAllUsersWithWriteAccessToCalendar(array $users):array { |
|
| 328 | + $emailAddresses = []; |
|
| 329 | + |
|
| 330 | + foreach ($users as $user) { |
|
| 331 | + $emailAddress = $user->getEMailAddress(); |
|
| 332 | + if ($emailAddress) { |
|
| 333 | + $lang = $this->l10nFactory->getUserLanguage($user); |
|
| 334 | + if ($lang) { |
|
| 335 | + $emailAddresses[$emailAddress] = [ |
|
| 336 | + 'LANG' => $lang, |
|
| 337 | + ]; |
|
| 338 | + } else { |
|
| 339 | + $emailAddresses[$emailAddress] = []; |
|
| 340 | + } |
|
| 341 | + } |
|
| 342 | + } |
|
| 343 | + |
|
| 344 | + return $emailAddresses; |
|
| 345 | + } |
|
| 346 | + |
|
| 347 | + /** |
|
| 348 | + * @throws \Exception |
|
| 349 | + */ |
|
| 350 | + private function generateDateString(IL10N $l10n, VEvent $vevent): string { |
|
| 351 | + $isAllDay = $vevent->DTSTART instanceof Property\ICalendar\Date; |
|
| 352 | + |
|
| 353 | + /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */ |
|
| 354 | + /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */ |
|
| 355 | + /** @var \DateTimeImmutable $dtstartDt */ |
|
| 356 | + $dtstartDt = $vevent->DTSTART->getDateTime(); |
|
| 357 | + /** @var \DateTimeImmutable $dtendDt */ |
|
| 358 | + $dtendDt = $this->getDTEndFromEvent($vevent)->getDateTime(); |
|
| 359 | + |
|
| 360 | + $diff = $dtstartDt->diff($dtendDt); |
|
| 361 | + |
|
| 362 | + $dtstartDt = new \DateTime($dtstartDt->format(\DateTimeInterface::ATOM)); |
|
| 363 | + $dtendDt = new \DateTime($dtendDt->format(\DateTimeInterface::ATOM)); |
|
| 364 | + |
|
| 365 | + if ($isAllDay) { |
|
| 366 | + // One day event |
|
| 367 | + if ($diff->days === 1) { |
|
| 368 | + return $this->getDateString($l10n, $dtstartDt); |
|
| 369 | + } |
|
| 370 | + |
|
| 371 | + return implode(' - ', [ |
|
| 372 | + $this->getDateString($l10n, $dtstartDt), |
|
| 373 | + $this->getDateString($l10n, $dtendDt), |
|
| 374 | + ]); |
|
| 375 | + } |
|
| 376 | + |
|
| 377 | + $startTimezone = $endTimezone = null; |
|
| 378 | + if (!$vevent->DTSTART->isFloating()) { |
|
| 379 | + $startTimezone = $vevent->DTSTART->getDateTime()->getTimezone()->getName(); |
|
| 380 | + $endTimezone = $this->getDTEndFromEvent($vevent)->getDateTime()->getTimezone()->getName(); |
|
| 381 | + } |
|
| 382 | + |
|
| 383 | + $localeStart = implode(', ', [ |
|
| 384 | + $this->getWeekDayName($l10n, $dtstartDt), |
|
| 385 | + $this->getDateTimeString($l10n, $dtstartDt) |
|
| 386 | + ]); |
|
| 387 | + |
|
| 388 | + // always show full date with timezone if timezones are different |
|
| 389 | + if ($startTimezone !== $endTimezone) { |
|
| 390 | + $localeEnd = implode(', ', [ |
|
| 391 | + $this->getWeekDayName($l10n, $dtendDt), |
|
| 392 | + $this->getDateTimeString($l10n, $dtendDt) |
|
| 393 | + ]); |
|
| 394 | + |
|
| 395 | + return $localeStart |
|
| 396 | + . ' (' . $startTimezone . ') ' |
|
| 397 | + . ' - ' |
|
| 398 | + . $localeEnd |
|
| 399 | + . ' (' . $endTimezone . ')'; |
|
| 400 | + } |
|
| 401 | + |
|
| 402 | + // Show only the time if the day is the same |
|
| 403 | + $localeEnd = $this->isDayEqual($dtstartDt, $dtendDt) |
|
| 404 | + ? $this->getTimeString($l10n, $dtendDt) |
|
| 405 | + : implode(', ', [ |
|
| 406 | + $this->getWeekDayName($l10n, $dtendDt), |
|
| 407 | + $this->getDateTimeString($l10n, $dtendDt) |
|
| 408 | + ]); |
|
| 409 | + |
|
| 410 | + return $localeStart |
|
| 411 | + . ' - ' |
|
| 412 | + . $localeEnd |
|
| 413 | + . ' (' . $startTimezone . ')'; |
|
| 414 | + } |
|
| 415 | + |
|
| 416 | + private function isDayEqual(DateTime $dtStart, |
|
| 417 | + DateTime $dtEnd):bool { |
|
| 418 | + return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d'); |
|
| 419 | + } |
|
| 420 | + |
|
| 421 | + private function getWeekDayName(IL10N $l10n, DateTime $dt):string { |
|
| 422 | + return (string)$l10n->l('weekdayName', $dt, ['width' => 'abbreviated']); |
|
| 423 | + } |
|
| 424 | + |
|
| 425 | + private function getDateString(IL10N $l10n, DateTime $dt):string { |
|
| 426 | + return (string)$l10n->l('date', $dt, ['width' => 'medium']); |
|
| 427 | + } |
|
| 428 | + |
|
| 429 | + private function getDateTimeString(IL10N $l10n, DateTime $dt):string { |
|
| 430 | + return (string)$l10n->l('datetime', $dt, ['width' => 'medium|short']); |
|
| 431 | + } |
|
| 432 | + |
|
| 433 | + private function getTimeString(IL10N $l10n, DateTime $dt):string { |
|
| 434 | + return (string)$l10n->l('time', $dt, ['width' => 'short']); |
|
| 435 | + } |
|
| 436 | + |
|
| 437 | + private function getTitleFromVEvent(VEvent $vevent, IL10N $l10n):string { |
|
| 438 | + if (isset($vevent->SUMMARY)) { |
|
| 439 | + return (string)$vevent->SUMMARY; |
|
| 440 | + } |
|
| 441 | + |
|
| 442 | + return $l10n->t('Untitled event'); |
|
| 443 | + } |
|
| 444 | 444 | } |