migrate therinaldos.com data
Build & Deploy to DigitalOcean Space / build (push) Failing after 2m38s

This commit is contained in:
2024-05-05 15:50:45 -04:00
commit ef1ff240d4
23182 changed files with 3801898 additions and 0 deletions
@@ -0,0 +1,66 @@
<?php
/**
* @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
*
* @author Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Comments\Activity;
use OCP\Activity\IFilter;
use OCP\IL10N;
use OCP\IURLGenerator;
class Filter implements IFilter {
public function __construct(
protected IL10N $l,
protected IURLGenerator $url,
) {
}
public function getIdentifier(): string {
return 'comments';
}
public function getName(): string {
return $this->l->t('Comments');
}
public function getPriority(): int {
return 40;
}
public function getIcon(): string {
return $this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/comment.svg'));
}
/**
* @param string[] $types
* @return string[] An array of allowed apps from which activities should be displayed
*/
public function filterTypes(array $types): array {
return $types;
}
/**
* @return string[] An array of allowed apps from which activities should be displayed
*/
public function allowedApps(): array {
return ['comments'];
}
}
@@ -0,0 +1,105 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Joas Schilling <coding@schilljs.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\Comments\Activity;
use OCP\Activity\IManager;
use OCP\App\IAppManager;
use OCP\Comments\CommentsEvent;
use OCP\Files\Config\IMountProviderCollection;
use OCP\Files\IRootFolder;
use OCP\Files\Node;
use OCP\IUser;
use OCP\IUserSession;
use OCP\Share\IShareHelper;
class Listener {
public function __construct(
protected IManager $activityManager,
protected IUserSession $session,
protected IAppManager $appManager,
protected IMountProviderCollection $mountCollection,
protected IRootFolder $rootFolder,
protected IShareHelper $shareHelper,
) {
}
public function commentEvent(CommentsEvent $event): void {
if ($event->getComment()->getObjectType() !== 'files'
|| $event->getEvent() !== CommentsEvent::EVENT_ADD
|| !$this->appManager->isInstalled('activity')) {
// Comment not for file, not adding a comment or no activity-app enabled (save the energy)
return;
}
// Get all mount point owners
$cache = $this->mountCollection->getMountCache();
$mounts = $cache->getMountsForFileId((int)$event->getComment()->getObjectId());
if (empty($mounts)) {
return;
}
$users = [];
foreach ($mounts as $mount) {
$owner = $mount->getUser()->getUID();
$ownerFolder = $this->rootFolder->getUserFolder($owner);
$nodes = $ownerFolder->getById((int)$event->getComment()->getObjectId());
if (!empty($nodes)) {
/** @var Node $node */
$node = array_shift($nodes);
$al = $this->shareHelper->getPathsForAccessList($node);
$users += $al['users'];
}
}
$actor = $this->session->getUser();
if ($actor instanceof IUser) {
$actor = $actor->getUID();
} else {
$actor = '';
}
$activity = $this->activityManager->generateEvent();
$activity->setApp('comments')
->setType('comments')
->setAuthor($actor)
->setObject($event->getComment()->getObjectType(), (int) $event->getComment()->getObjectId())
->setMessage('add_comment_message', [
'commentId' => $event->getComment()->getId(),
]);
foreach ($users as $user => $path) {
// numerical user ids end up as integers from array keys, but string
// is required
$activity->setAffectedUser((string)$user);
$activity->setSubject('add_comment_subject', [
'actor' => $actor,
'fileId' => (int) $event->getComment()->getObjectId(),
'filePath' => trim($path, '/'),
]);
$this->activityManager->publish($activity);
}
}
}
@@ -0,0 +1,212 @@
<?php
/**
* @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
* @author Morris Jobke <hey@morrisjobke.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Comments\Activity;
use OCP\Activity\IEvent;
use OCP\Activity\IManager;
use OCP\Activity\IProvider;
use OCP\Comments\ICommentsManager;
use OCP\Comments\NotFoundException;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\L10N\IFactory;
class Provider implements IProvider {
protected ?IL10N $l = null;
public function __construct(
protected IFactory $languageFactory,
protected IURLGenerator $url,
protected ICommentsManager $commentsManager,
protected IUserManager $userManager,
protected IManager $activityManager,
) {
}
/**
* @param string $language
* @param IEvent $event
* @param IEvent|null $previousEvent
* @return IEvent
* @throws \InvalidArgumentException
* @since 11.0.0
*/
public function parse($language, IEvent $event, IEvent $previousEvent = null): IEvent {
if ($event->getApp() !== 'comments') {
throw new \InvalidArgumentException();
}
$this->l = $this->languageFactory->get('comments', $language);
if ($event->getSubject() === 'add_comment_subject') {
$this->parseMessage($event);
if ($this->activityManager->getRequirePNG()) {
$event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/comment.png')));
} else {
$event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/comment.svg')));
}
if ($this->activityManager->isFormattingFilteredObject()) {
try {
return $this->parseShortVersion($event);
} catch (\InvalidArgumentException $e) {
// Ignore and simply use the long version...
}
}
return $this->parseLongVersion($event);
} else {
throw new \InvalidArgumentException();
}
}
/**
* @throws \InvalidArgumentException
*/
protected function parseShortVersion(IEvent $event): IEvent {
$subjectParameters = $this->getSubjectParameters($event);
if ($event->getSubject() === 'add_comment_subject') {
if ($subjectParameters['actor'] === $this->activityManager->getCurrentUserId()) {
$event->setRichSubject($this->l->t('You commented'), []);
} else {
$author = $this->generateUserParameter($subjectParameters['actor']);
$event->setRichSubject($this->l->t('{author} commented'), [
'author' => $author,
]);
}
} else {
throw new \InvalidArgumentException();
}
return $event;
}
/**
* @throws \InvalidArgumentException
*/
protected function parseLongVersion(IEvent $event): IEvent {
$subjectParameters = $this->getSubjectParameters($event);
if ($event->getSubject() === 'add_comment_subject') {
if ($subjectParameters['actor'] === $this->activityManager->getCurrentUserId()) {
$event->setParsedSubject($this->l->t('You commented on %1$s', [
$subjectParameters['filePath'],
]))
->setRichSubject($this->l->t('You commented on {file}'), [
'file' => $this->generateFileParameter($subjectParameters['fileId'], $subjectParameters['filePath']),
]);
} else {
$author = $this->generateUserParameter($subjectParameters['actor']);
$event->setParsedSubject($this->l->t('%1$s commented on %2$s', [
$author['name'],
$subjectParameters['filePath'],
]))
->setRichSubject($this->l->t('{author} commented on {file}'), [
'author' => $author,
'file' => $this->generateFileParameter($subjectParameters['fileId'], $subjectParameters['filePath']),
]);
}
} else {
throw new \InvalidArgumentException();
}
return $event;
}
protected function getSubjectParameters(IEvent $event): array {
$subjectParameters = $event->getSubjectParameters();
if (isset($subjectParameters['fileId'])) {
return $subjectParameters;
}
// Fix subjects from 12.0.3 and older
//
// Do NOT Remove unless necessary
// Removing this will break parsing of activities that were created on
// Nextcloud 12, so we should keep this as long as it's acceptable.
// Otherwise if people upgrade over multiple releases in a short period,
// they will get the dead entries in their stream.
return [
'actor' => $subjectParameters[0],
'fileId' => $event->getObjectId(),
'filePath' => trim($subjectParameters[1], '/'),
];
}
protected function parseMessage(IEvent $event): void {
$messageParameters = $event->getMessageParameters();
if (empty($messageParameters)) {
// Email
return;
}
$commentId = $messageParameters['commentId'] ?? $messageParameters[0];
try {
$comment = $this->commentsManager->get((string) $commentId);
$message = $comment->getMessage();
$mentionCount = 1;
$mentions = [];
foreach ($comment->getMentions() as $mention) {
if ($mention['type'] !== 'user') {
continue;
}
$message = str_replace('@"' . $mention['id'] . '"', '{mention' . $mentionCount . '}', $message);
if (!str_contains($mention['id'], ' ') && !str_starts_with($mention['id'], 'guest/')) {
$message = str_replace('@' . $mention['id'], '{mention' . $mentionCount . '}', $message);
}
$mentions['mention' . $mentionCount] = $this->generateUserParameter($mention['id']);
$mentionCount++;
}
$event->setParsedMessage($comment->getMessage())
->setRichMessage($message, $mentions);
} catch (NotFoundException $e) {
}
}
protected function generateFileParameter(int $id, string $path): array {
return [
'type' => 'file',
'id' => $id,
'name' => basename($path),
'path' => $path,
'link' => $this->url->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $id]),
];
}
protected function generateUserParameter(string $uid): array {
return [
'type' => 'user',
'id' => $uid,
'name' => $this->userManager->getDisplayName($uid) ?? $uid,
];
}
}
@@ -0,0 +1,61 @@
<?php
/**
* @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
*
* @author Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Comments\Activity;
use OCP\Activity\ISetting;
use OCP\IL10N;
class Setting implements ISetting {
public function __construct(
protected IL10N $l,
) {
}
public function getIdentifier(): string {
return 'comments';
}
public function getName(): string {
return $this->l->t('<strong>Comments</strong> for files');
}
public function getPriority(): int {
return 50;
}
public function canChangeStream(): bool {
return true;
}
public function isDefaultEnabledStream(): bool {
return true;
}
public function canChangeMail(): bool {
return true;
}
public function isDefaultEnabledMail(): bool {
return false;
}
}
@@ -0,0 +1,90 @@
<?php
/**
* @copyright Copyright (c) 2016, Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
* @author John Molakvoæ <skjnldsv@protonmail.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Comments\AppInfo;
use Closure;
use OCA\Comments\Capabilities;
use OCA\Comments\EventHandler;
use OCA\Comments\Listener\CommentsEntityEventListener;
use OCA\Comments\Listener\LoadAdditionalScripts;
use OCA\Comments\Listener\LoadSidebarScripts;
use OCA\Comments\MaxAutoCompleteResultsInitialState;
use OCA\Comments\Notification\Notifier;
use OCA\Comments\Search\CommentsSearchProvider;
use OCA\Comments\Search\LegacyProvider;
use OCA\Files\Event\LoadAdditionalScriptsEvent;
use OCA\Files\Event\LoadSidebar;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\Comments\CommentsEntityEvent;
use OCP\Comments\ICommentsManager;
use OCP\ISearch;
use OCP\IServerContainer;
class Application extends App implements IBootstrap {
public const APP_ID = 'comments';
public function __construct(array $urlParams = []) {
parent::__construct(self::APP_ID, $urlParams);
}
public function register(IRegistrationContext $context): void {
$context->registerCapability(Capabilities::class);
$context->registerEventListener(
LoadAdditionalScriptsEvent::class,
LoadAdditionalScripts::class
);
$context->registerEventListener(
LoadSidebar::class,
LoadSidebarScripts::class
);
$context->registerEventListener(
CommentsEntityEvent::class,
CommentsEntityEventListener::class
);
$context->registerSearchProvider(CommentsSearchProvider::class);
$context->registerInitialStateProvider(MaxAutoCompleteResultsInitialState::class);
$context->registerNotifierService(Notifier::class);
}
public function boot(IBootContext $context): void {
$context->injectFn(Closure::fromCallable([$this, 'registerCommentsEventHandler']));
$context->getServerContainer()->get(ISearch::class)->registerProvider(LegacyProvider::class, ['apps' => ['files']]);
}
protected function registerCommentsEventHandler(IServerContainer $container): void {
$container->get(ICommentsManager::class)->registerEventHandler(function (): EventHandler {
return $this->getContainer()->get(EventHandler::class);
});
}
}
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com>
*
* @author Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Comments;
use OCP\Capabilities\ICapability;
class Capabilities implements ICapability {
/**
* @return array{files: array{comments: bool}}
*/
public function getCapabilities(): array {
return [
'files' => [
'comments' => true,
]
];
}
}
@@ -0,0 +1,109 @@
<?php
/**
* @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Comments\Collaboration;
use OCP\Collaboration\AutoComplete\ISorter;
use OCP\Comments\ICommentsManager;
class CommentersSorter implements ISorter {
public function __construct(
private ICommentsManager $commentsManager,
) {
}
public function getId(): string {
return 'commenters';
}
/**
* Sorts people who commented on the given item atop (descelating) of the
* others
*
* @param array &$sortArray
* @param array $context
*/
public function sort(array &$sortArray, array $context): void {
$commenters = $this->retrieveCommentsInformation($context['itemType'], $context['itemId']);
if (count($commenters) === 0) {
return;
}
foreach ($sortArray as $type => &$byType) {
if (!isset($commenters[$type])) {
continue;
}
// at least on PHP 5.6 usort turned out to be not stable. So we add
// the current index to the value and compare it on a draw
$i = 0;
$workArray = array_map(function ($element) use (&$i) {
return [$i++, $element];
}, $byType);
usort($workArray, function ($a, $b) use ($commenters, $type) {
$r = $this->compare($a[1], $b[1], $commenters[$type]);
if ($r === 0) {
$r = $a[0] - $b[0];
}
return $r;
});
// and remove the index values again
$byType = array_column($workArray, 1);
}
}
/**
* @return array<string, array<string, int>>
*/
protected function retrieveCommentsInformation(string $type, string $id): array {
$comments = $this->commentsManager->getForObject($type, $id);
if (count($comments) === 0) {
return [];
}
$actors = [];
foreach ($comments as $comment) {
if (!isset($actors[$comment->getActorType()])) {
$actors[$comment->getActorType()] = [];
}
if (!isset($actors[$comment->getActorType()][$comment->getActorId()])) {
$actors[$comment->getActorType()][$comment->getActorId()] = 1;
} else {
$actors[$comment->getActorType()][$comment->getActorId()]++;
}
}
return $actors;
}
protected function compare(array $a, array $b, array $commenters): int {
$a = $a['value']['shareWith'];
$b = $b['value']['shareWith'];
$valueA = $commenters[$a] ?? 0;
$valueB = $commenters[$b] ?? 0;
return $valueB - $valueA;
}
}
@@ -0,0 +1,120 @@
<?php
/**
* @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Comments\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\NotFoundResponse;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\Comments\IComment;
use OCP\Comments\ICommentsManager;
use OCP\Files\IRootFolder;
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\IUserSession;
use OCP\Notification\IManager;
/**
* @package OCA\Comments\Controller
*/
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
class NotificationsController extends Controller {
public function __construct(
string $appName,
IRequest $request,
protected ICommentsManager $commentsManager,
protected IRootFolder $rootFolder,
protected IURLGenerator $urlGenerator,
protected IManager $notificationManager,
protected IUserSession $userSession
) {
parent::__construct($appName, $request);
}
/**
* @PublicPage
* @NoCSRFRequired
*
* View a notification
*
* @param string $id ID of the notification
*
* @return RedirectResponse<Http::STATUS_SEE_OTHER, array{}>|NotFoundResponse<Http::STATUS_NOT_FOUND, array{}>
*
* 303: Redirected to notification
* 404: Notification not found
*/
public function view(string $id): RedirectResponse|NotFoundResponse {
$currentUser = $this->userSession->getUser();
if (!$currentUser instanceof IUser) {
return new RedirectResponse(
$this->urlGenerator->linkToRoute('core.login.showLoginForm', [
'redirect_url' => $this->urlGenerator->linkToRoute(
'comments.Notifications.view',
['id' => $id]
),
])
);
}
try {
$comment = $this->commentsManager->get($id);
if ($comment->getObjectType() !== 'files') {
return new NotFoundResponse();
}
$userFolder = $this->rootFolder->getUserFolder($currentUser->getUID());
$files = $userFolder->getById((int)$comment->getObjectId());
$this->markProcessed($comment, $currentUser);
if (empty($files)) {
return new NotFoundResponse();
}
$url = $this->urlGenerator->linkToRouteAbsolute(
'files.viewcontroller.showFile',
[ 'fileid' => $comment->getObjectId() ]
);
return new RedirectResponse($url);
} catch (\Exception $e) {
return new NotFoundResponse();
}
}
/**
* Marks the notification about a comment as processed
*/
protected function markProcessed(IComment $comment, IUser $currentUser): void {
$notification = $this->notificationManager->createNotification();
$notification->setApp('comments')
->setObject('comment', $comment->getId())
->setSubject('mention')
->setUser($currentUser->getUID());
$this->notificationManager->markProcessed($notification);
}
}
@@ -0,0 +1,75 @@
<?php
/**
* @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Comments;
use OCA\Comments\Activity\Listener as ActivityListener;
use OCA\Comments\Notification\Listener as NotificationListener;
use OCP\Comments\CommentsEvent;
use OCP\Comments\ICommentsEventHandler;
/**
* Class EventHandler
*
* @package OCA\Comments
*/
class EventHandler implements ICommentsEventHandler {
public function __construct(
private ActivityListener $activityListener,
private NotificationListener $notificationListener,
) {
}
public function handle(CommentsEvent $event): void {
if ($event->getComment()->getObjectType() !== 'files') {
// this is a 'files'-specific Handler
return;
}
$eventType = $event->getEvent();
if ($eventType === CommentsEvent::EVENT_ADD
) {
$this->notificationHandler($event);
$this->activityHandler($event);
return;
}
$applicableEvents = [
CommentsEvent::EVENT_PRE_UPDATE,
CommentsEvent::EVENT_UPDATE,
CommentsEvent::EVENT_DELETE,
];
if (in_array($eventType, $applicableEvents)) {
$this->notificationHandler($event);
return;
}
}
private function activityHandler(CommentsEvent $event): void {
$this->activityListener->commentEvent($event);
}
private function notificationHandler(CommentsEvent $event): void {
$this->notificationListener->evaluate($event);
}
}
@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
/**
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Comments\Listener;
use OCP\Comments\CommentsEntityEvent;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Files\IRootFolder;
class CommentsEntityEventListener implements IEventListener {
public function __construct(
private IRootFolder $rootFolder,
private ?string $userId = null,
) {
}
public function handle(Event $event): void {
if (!($event instanceof CommentsEntityEvent)) {
// Unrelated
return;
}
$event->addEntityCollection('files', function ($name): bool {
$nodes = $this->rootFolder->getUserFolder($this->userId)->getById((int)$name);
return !empty($nodes);
});
}
}
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author John Molakvoæ <skjnldsv@protonmail.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Comments\Listener;
use OCA\Comments\AppInfo\Application;
use OCA\Files\Event\LoadAdditionalScriptsEvent;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Util;
class LoadAdditionalScripts implements IEventListener {
public function handle(Event $event): void {
if (!($event instanceof LoadAdditionalScriptsEvent)) {
return;
}
// TODO: make sure to only include the sidebar script when
// we properly split it between files list and sidebar
Util::addScript(Application::APP_ID, 'comments');
Util::addInitScript(Application::APP_ID, 'init');
}
}
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Comments\Listener;
use OCA\Comments\AppInfo\Application;
use OCA\Files\Event\LoadSidebar;
use OCP\App\IAppManager;
use OCP\AppFramework\Services\IInitialState;
use OCP\Comments\ICommentsManager;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Util;
class LoadSidebarScripts implements IEventListener {
public function __construct(
private ICommentsManager $commentsManager,
private IInitialState $initialState,
private IAppManager $appManager,
) {
}
public function handle(Event $event): void {
if (!($event instanceof LoadSidebar)) {
return;
}
$this->commentsManager->load();
$this->initialState->provideInitialState('activityEnabled', $this->appManager->isEnabledForUser('activity'));
// TODO: make sure to only include the sidebar script when
// we properly split it between files list and sidebar
Util::addScript(Application::APP_ID, 'comments');
Util::addScript(Application::APP_ID, 'comments-tab', 'files');
}
}
@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2021 Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Comments;
use OCP\AppFramework\Services\InitialStateProvider;
use OCP\IConfig;
class MaxAutoCompleteResultsInitialState extends InitialStateProvider {
public function __construct(
private IConfig $config,
) {
}
public function getKey(): string {
return 'maxAutoCompleteResults';
}
public function getData(): int {
return (int)$this->config->getAppValue('comments', 'maxAutoCompleteResults', '10');
}
}
@@ -0,0 +1,100 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\Comments\Notification;
use OCP\Comments\CommentsEvent;
use OCP\Comments\IComment;
use OCP\IUserManager;
use OCP\Notification\IManager;
use OCP\Notification\INotification;
class Listener {
public function __construct(
protected IManager $notificationManager,
protected IUserManager $userManager
) {
}
public function evaluate(CommentsEvent $event): void {
$comment = $event->getComment();
$mentions = $this->extractMentions($comment->getMentions());
if (empty($mentions)) {
// no one to notify
return;
}
$notification = $this->instantiateNotification($comment);
foreach ($mentions as $uid) {
if (($comment->getActorType() === 'users' && $uid === $comment->getActorId())
|| !$this->userManager->userExists($uid)
) {
// do not notify unknown users or yourself
continue;
}
$notification->setUser($uid);
if ($event->getEvent() === CommentsEvent::EVENT_DELETE
|| $event->getEvent() === CommentsEvent::EVENT_PRE_UPDATE) {
$this->notificationManager->markProcessed($notification);
} else {
$this->notificationManager->notify($notification);
}
}
}
/**
* Creates a notification instance and fills it with comment data
*/
public function instantiateNotification(IComment $comment): INotification {
$notification = $this->notificationManager->createNotification();
$notification
->setApp('comments')
->setObject('comment', $comment->getId())
->setSubject('mention', [ $comment->getObjectType(), $comment->getObjectId() ])
->setDateTime($comment->getCreationDateTime());
return $notification;
}
/**
* Flattens the mention array returned from comments to a list of user ids.
*
* @param array $mentions
* @return list<string> containing the mentions, e.g. ['alice', 'bob']
*/
public function extractMentions(array $mentions): array {
if (empty($mentions)) {
return [];
}
$uids = [];
foreach ($mentions as $mention) {
if ($mention['type'] === 'user') {
$uids[] = $mention['id'];
}
}
return $uids;
}
}
@@ -0,0 +1,193 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\Comments\Notification;
use OCP\Comments\IComment;
use OCP\Comments\ICommentsManager;
use OCP\Comments\NotFoundException;
use OCP\Files\IRootFolder;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\L10N\IFactory;
use OCP\Notification\AlreadyProcessedException;
use OCP\Notification\INotification;
use OCP\Notification\INotifier;
class Notifier implements INotifier {
public function __construct(
protected IFactory $l10nFactory,
protected IRootFolder $rootFolder,
protected ICommentsManager $commentsManager,
protected IURLGenerator $url,
protected IUserManager $userManager
) {
}
/**
* Identifier of the notifier, only use [a-z0-9_]
*
* @return string
* @since 17.0.0
*/
public function getID(): string {
return 'comments';
}
/**
* Human readable name describing the notifier
*
* @return string
* @since 17.0.0
*/
public function getName(): string {
return $this->l10nFactory->get('comments')->t('Comments');
}
/**
* @param INotification $notification
* @param string $languageCode The code of the language that should be used to prepare the notification
* @return INotification
* @throws \InvalidArgumentException When the notification was not prepared by a notifier
* @throws AlreadyProcessedException When the notification is not needed anymore and should be deleted
* @since 9.0.0
*/
public function prepare(INotification $notification, string $languageCode): INotification {
if ($notification->getApp() !== 'comments') {
throw new \InvalidArgumentException();
}
try {
$comment = $this->commentsManager->get($notification->getObjectId());
} catch (NotFoundException $e) {
// needs to be converted to InvalidArgumentException, otherwise none Notifications will be shown at all
throw new \InvalidArgumentException('Comment not found', 0, $e);
}
$l = $this->l10nFactory->get('comments', $languageCode);
$displayName = $comment->getActorId();
$isDeletedActor = $comment->getActorType() === ICommentsManager::DELETED_USER;
if ($comment->getActorType() === 'users') {
$commenter = $this->userManager->getDisplayName($comment->getActorId());
if ($commenter !== null) {
$displayName = $commenter;
}
}
switch ($notification->getSubject()) {
case 'mention':
$parameters = $notification->getSubjectParameters();
if ($parameters[0] !== 'files') {
throw new \InvalidArgumentException('Unsupported comment object');
}
$userFolder = $this->rootFolder->getUserFolder($notification->getUser());
$nodes = $userFolder->getById((int)$parameters[1]);
if (empty($nodes)) {
throw new AlreadyProcessedException();
}
$node = $nodes[0];
$path = rtrim($node->getPath(), '/');
if (str_starts_with($path, '/' . $notification->getUser() . '/files/')) {
// Remove /user/files/...
$fullPath = $path;
[,,, $path] = explode('/', $fullPath, 4);
}
$subjectParameters = [
'file' => [
'type' => 'file',
'id' => $comment->getObjectId(),
'name' => $node->getName(),
'path' => $path,
'link' => $this->url->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $comment->getObjectId()]),
],
];
if ($isDeletedActor) {
$subject = $l->t('You were mentioned on "{file}", in a comment by a user that has since been deleted');
} else {
$subject = $l->t('{user} mentioned you in a comment on "{file}"');
$subjectParameters['user'] = [
'type' => 'user',
'id' => $comment->getActorId(),
'name' => $displayName,
];
}
[$message, $messageParameters] = $this->commentToRichMessage($comment);
$notification->setRichSubject($subject, $subjectParameters)
->setRichMessage($message, $messageParameters)
->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/comment.svg')))
->setLink($this->url->linkToRouteAbsolute(
'comments.Notifications.view',
['id' => $comment->getId()])
);
return $notification;
break;
default:
throw new \InvalidArgumentException('Invalid subject');
}
}
public function commentToRichMessage(IComment $comment): array {
$message = $comment->getMessage();
$messageParameters = [];
$mentionTypeCount = [];
$mentions = $comment->getMentions();
foreach ($mentions as $mention) {
if ($mention['type'] === 'user') {
$userDisplayName = $this->userManager->getDisplayName($mention['id']);
if ($userDisplayName === null) {
continue;
}
}
if (!array_key_exists($mention['type'], $mentionTypeCount)) {
$mentionTypeCount[$mention['type']] = 0;
}
$mentionTypeCount[$mention['type']]++;
// To keep a limited character set in parameter IDs ([a-zA-Z0-9-])
// the mention parameter ID does not include the mention ID (which
// could contain characters like '@' for user IDs) but a one-based
// index of the mentions of that type.
$mentionParameterId = 'mention-' . $mention['type'] . $mentionTypeCount[$mention['type']];
$message = str_replace('@"' . $mention['id'] . '"', '{' . $mentionParameterId . '}', $message);
if (!str_contains($mention['id'], ' ') && !str_starts_with($mention['id'], 'guest/')) {
$message = str_replace('@' . $mention['id'], '{' . $mentionParameterId . '}', $message);
}
try {
$displayName = $this->commentsManager->resolveDisplayName($mention['type'], $mention['id']);
} catch (\OutOfBoundsException $e) {
// There is no registered display name resolver for the mention
// type, so the client decides what to display.
$displayName = '';
}
$messageParameters[$mentionParameterId] = [
'type' => $mention['type'],
'id' => $mention['id'],
'name' => $displayName
];
}
return [$message, $messageParameters];
}
}
@@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
/**
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Comments\Search;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\IUserManager;
use OCP\Search\IProvider;
use OCP\Search\ISearchQuery;
use OCP\Search\SearchResult;
use OCP\Search\SearchResultEntry;
use function array_map;
use function pathinfo;
class CommentsSearchProvider implements IProvider {
public function __construct(
private IUserManager $userManager,
private IL10N $l10n,
private IURLGenerator $urlGenerator,
private LegacyProvider $legacyProvider,
) {
}
public function getId(): string {
return 'comments';
}
public function getName(): string {
return $this->l10n->t('Comments');
}
public function getOrder(string $route, array $routeParameters): int {
if ($route === 'files.View.index') {
// Files first
return 0;
}
return 10;
}
public function search(IUser $user, ISearchQuery $query): SearchResult {
return SearchResult::complete(
$this->l10n->t('Comments'),
array_map(function (Result $result) {
$path = $result->path;
$pathInfo = pathinfo($path);
$isUser = $this->userManager->userExists($result->authorId);
$avatarUrl = $isUser
? $this->urlGenerator->linkToRouteAbsolute('core.avatar.getAvatar', ['userId' => $result->authorId, 'size' => 42])
: $this->urlGenerator->linkToRouteAbsolute('core.GuestAvatar.getAvatar', ['guestName' => $result->authorId, 'size' => 42]);
return new SearchResultEntry(
$avatarUrl,
$result->name,
$path,
$this->urlGenerator->linkToRouteAbsolute('files.view.index', [
'dir' => $pathInfo['dirname'],
'scrollto' => $pathInfo['basename'],
]),
'',
true
);
}, $this->legacyProvider->search($query->getTerm()))
);
}
}
@@ -0,0 +1,112 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018 Joas Schilling <coding@schilljs.com>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Comments\Search;
use OCP\Comments\IComment;
use OCP\Comments\ICommentsManager;
use OCP\Files\Folder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\IUser;
use OCP\Search\Provider;
use function count;
class LegacyProvider extends Provider {
/**
* Search for $query
*
* @param string $query
* @return array An array of OCP\Search\Result's
* @since 7.0.0
*/
public function search($query): array {
$cm = \OC::$server->get(ICommentsManager::class);
$us = \OC::$server->getUserSession();
$user = $us->getUser();
if (!$user instanceof IUser) {
return [];
}
$uf = \OC::$server->getUserFolder($user->getUID());
if ($uf === null) {
return [];
}
$result = [];
$numComments = 50;
$offset = 0;
while (count($result) < $numComments) {
/** @var IComment[] $comments */
$comments = $cm->search($query, 'files', '', 'comment', $offset, $numComments);
foreach ($comments as $comment) {
if ($comment->getActorType() !== 'users') {
continue;
}
$displayName = $cm->resolveDisplayName('user', $comment->getActorId());
try {
$file = $this->getFileForComment($uf, $comment);
$result[] = new Result($query,
$comment,
$displayName,
$file->getPath()
);
} catch (NotFoundException $e) {
continue;
}
}
if (count($comments) < $numComments) {
// Didn't find more comments when we tried to get, so there are no more comments.
return $result;
}
$offset += $numComments;
$numComments = 50 - count($result);
}
return $result;
}
/**
* @param Folder $userFolder
* @param IComment $comment
* @return Node
* @throws NotFoundException
*/
protected function getFileForComment(Folder $userFolder, IComment $comment): Node {
$nodes = $userFolder->getById((int) $comment->getObjectId());
if (empty($nodes)) {
throw new NotFoundException('File not found');
}
return array_shift($nodes);
}
}
@@ -0,0 +1,125 @@
<?php
/**
* @copyright Copyright (c) 2018 Joas Schilling <coding@schilljs.com>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
* @author Michał Węgrzynek <michal.wegrzynek@malloc.com.pl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Comments\Search;
use OCP\Comments\IComment;
use OCP\Files\NotFoundException;
use OCP\Search\Result as BaseResult;
/**
* @deprecated 20.0.0
*/
class Result extends BaseResult {
/**
* @deprecated 20.0.0
*/
public $type = 'comment';
/**
* @deprecated 20.0.0
*/
public $comment;
/**
* @deprecated 20.0.0
*/
public $authorId;
/**
* @deprecated 20.0.0
*/
public string $authorName;
/**
* @deprecated 20.0.0
*/
public $path;
/**
* @deprecated 20.0.0
*/
public $fileName;
/**
* @throws NotFoundException
* @deprecated 20.0.0
*/
public function __construct(
string $search,
IComment $comment,
string $authorName,
string $path,
) {
parent::__construct(
$comment->getId(),
$comment->getMessage()
/* @todo , [link to file] */
);
$this->comment = $this->getRelevantMessagePart($comment->getMessage(), $search);
$this->authorId = $comment->getActorId();
$this->authorName = $authorName;
$this->fileName = basename($path);
$this->path = $this->getVisiblePath($path);
}
/**
* @throws NotFoundException
*/
protected function getVisiblePath(string $path): string {
$segments = explode('/', trim($path, '/'), 3);
if (!isset($segments[2])) {
throw new NotFoundException('Path not inside visible section');
}
return $segments[2];
}
/**
* @throws NotFoundException
*/
protected function getRelevantMessagePart(string $message, string $search): string {
$start = mb_stripos($message, $search);
if ($start === false) {
throw new NotFoundException('Comment section not found');
}
$end = $start + mb_strlen($search);
if ($start <= 25) {
$start = 0;
$prefix = '';
} else {
$start -= 25;
$prefix = '…';
}
if ((mb_strlen($message) - $end) <= 25) {
$end = mb_strlen($message);
$suffix = '';
} else {
$end += 25;
$suffix = '…';
}
return $prefix . mb_substr($message, $start, $end - $start) . $suffix;
}
}