Implement activity

Signed-off-by: Vitor Mattos <vitor@php.rio>
This commit is contained in:
Vitor Mattos 2024-03-04 20:38:44 -03:00
parent 1af890de63
commit 863508e8e4
No known key found for this signature in database
GPG key ID: B7AB4B76A7CA7318
8 changed files with 301 additions and 2 deletions

BIN
img/app-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 B

106
lib/Activity/Listener.php Normal file
View file

@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2024 Vitor Mattos <vitor@php.rio>
*
* @author Vitor Mattos <vitor@php.rio>
*
* @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\Libresign\Activity;
use OCA\Libresign\AppInfo\Application;
use OCA\Libresign\Db\SignRequest;
use OCA\Libresign\Events\SendSignNotificationEvent;
use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod;
use OCP\Activity\IManager;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\IUser;
use OCP\IUserSession;
use Psr\Log\LoggerInterface;
/**
* @template-implements IEventListener<Event>
*/
class Listener implements IEventListener {
public function __construct(
protected IManager $activityManager,
protected IUserSession $userSession,
protected LoggerInterface $logger,
protected ITimeFactory $timeFactory,
) {
}
public function handle(Event $event): void {
match (get_class($event)) {
SendSignNotificationEvent::class => $this->generateNewSignNotificationActivity(
$event->getSignRequest(),
$event->getIdentifyMethod(),
$event->isNew()
),
};
}
/**
* Invitation activity: "{actor} invited you to {call}"
*
* @param Room $room
* @param Attendee[] $attendees
*/
protected function generateNewSignNotificationActivity(
SignRequest $signRequest,
IIdentifyMethod $identifyMethod,
bool $isNew
): void {
$actor = $this->userSession->getUser();
if (!$actor instanceof IUser) {
return;
}
$actorId = $actor->getUID();
$event = $this->activityManager->generateEvent();
try {
$event
->setApp(Application::APP_ID)
->setType(Application::APP_ID)
->setAuthor($actorId)
->setObject('sign', $signRequest->getId(), 'signRequest')
->setTimestamp($this->timeFactory->getTime())
->setAffectedUser($identifyMethod->getEntity()->getIdentifierValue());
if ($isNew) {
$event->setSubject('new_sign_request', [
'from' => $actor->getUID(),
'signer' => $identifyMethod->getEntity()->getIdentifierValue(),
'signRequest' => $signRequest->getId(),
]);
} else {
$event->setSubject('update_sign_request', [
'from' => $actor->getUID(),
'signer' => $identifyMethod->getEntity()->getIdentifierValue(),
'signRequest' => $signRequest->getId(),
]);
}
$this->activityManager->publish($event);
} catch (\InvalidArgumentException $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
return;
}
}
}

View file

@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2024 Vitor Mattos <vitor@php.rio>
*
* @author Vitor Mattos <vitor@php.rio>
*
* @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\Libresign\Activity\Provider;
use OCA\Libresign\AppInfo\Application;
use OCP\Activity\IEvent;
use OCP\Activity\IManager;
use OCP\Activity\IProvider;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\L10N\IFactory;
class SignRequest implements IProvider {
public function __construct(
protected IFactory $languageFactory,
protected IURLGenerator $url,
protected IManager $activityManager,
protected IUserManager $userManager,
) {
}
public function parse($language, IEvent $event, ?IEvent $previousEvent = null): IEvent {
if ($event->getApp() !== Application::APP_ID) {
throw new \InvalidArgumentException('Wrong app');
}
if ($this->activityManager->getRequirePNG()) {
$event->setIcon($this->url->getAbsoluteURL($this->url->imagePath(Application::APP_ID, 'app-dark.png')));
} else {
$event->setIcon($this->url->getAbsoluteURL($this->url->imagePath(Application::APP_ID, 'app-dark.svg')));
}
if ($event->getSubject() === 'new_sign_request') {
$l = $this->languageFactory->get(Application::APP_ID, $language);
$parameters = $event->getSubjectParameters();
$subject = $l->t('{from} invited you to sign a file');
$event->setParsedSubject(
str_replace(
'{from}',
$this->userManager->getDisplayName($parameters['from']) ?? $parameters['from'],
$subject
));
}
return $event;
}
protected function getUser(string $uid): array {
return [
'type' => 'user',
'id' => $uid,
'name' => $this->userManager->getDisplayName($uid) ?? $uid,
];
}
}

81
lib/Activity/Setting.php Normal file
View file

@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2024 Vitor Mattos <vitor@php.rio>
*
* @author Vitor Mattos <vitor@php.rio>
*
* @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\Libresign\Activity;
use OCA\Libresign\AppInfo\Application;
use OCP\Activity\ActivitySettings;
use OCP\IL10N;
class Setting extends ActivitySettings {
public function __construct(
protected IL10N $l,
) {
}
public function getIdentifier(): string {
return Application::APP_ID;
}
/**
* @return string A translated string
* @since 11.0.0
*/
public function getName(): string {
return $this->l->t('You have a <strong>file to sign</strong>');
}
/**
* {@inheritdoc}
*/
public function getGroupIdentifier(): string {
return 'other';
}
/**
* {@inheritdoc}
*/
public function getGroupName(): string {
return $this->l->t('Other activities');
}
/**
* {@inheritdoc}
*/
public function getPriority(): int {
return 51;
}
/**
* {@inheritdoc}
*/
public function canChangeNotification(): bool {
return false;
}
/**
* {@inheritdoc}
*/
public function isDefaultEnabledNotification(): bool {
return false;
}
}

View file

@ -25,6 +25,7 @@ declare(strict_types=1);
namespace OCA\Libresign\AppInfo;
use OCA\Files\Event\LoadSidebar;
use OCA\Libresign\Activity\Listener as ActivityListener;
use OCA\Libresign\Events\SendSignNotificationEvent;
use OCA\Libresign\Events\SignedEvent;
use OCA\Libresign\Files\TemplateLoader as FilesTemplateLoader;
@ -70,6 +71,11 @@ class Application extends App implements IBootstrap {
$context->registerEventListener(LoadSidebar::class, LoadSidebarListener::class);
$context->registerEventListener(BeforeNodeDeletedEvent::class, BeforeNodeDeletedListener::class);
$context->registerEventListener(SignedEvent::class, SignedListener::class);
// Activity listeners
$context->registerEventListener(SendSignNotificationEvent::class, ActivityListener::class);
// Notification listeners
$context->registerEventListener(SendSignNotificationEvent::class, NotificationListener::class);
}
}

View file

@ -62,6 +62,12 @@ class Reset extends Base {
mode: InputOption::VALUE_OPTIONAL,
description: 'Reset notifications'
)
->addOption(
name: 'activity',
shortcut: null,
mode: InputOption::VALUE_OPTIONAL,
description: 'Reset activity'
)
->addOption(
name: 'identify',
shortcut: null,
@ -110,6 +116,10 @@ class Reset extends Base {
$this->resetNotifications((string) $input->getOption('notifications'));
$ok = true;
}
if ($input->getOption('activity') || $all) {
$this->resetActivity((string) $input->getOption('activity'));
$ok = true;
}
if ($input->getOption('identify') || $all) {
$this->resetIdentifyMethods();
$ok = true;
@ -161,6 +171,19 @@ class Reset extends Base {
}
}
private function resetActivity(string $user): void {
try {
$delete = $this->db->getQueryBuilder();
$delete->delete('activity_mq')
->where($delete->expr()->eq('amq_appid', $delete->createNamedParameter(Application::APP_ID)));
if ($user) {
$delete->andWhere($delete->expr()->eq('amq_affecteduser', $delete->createNamedParameter($user)));
}
$delete->executeStatement();
} catch (\Throwable $e) {
}
}
private function resetIdentifyMethods(): void {
try {
$delete = $this->db->getQueryBuilder();

View file

@ -185,6 +185,7 @@ Feature: request-signature
And set the email of user "signer1" to "signer1@domain.test"
And reset notifications of user "signer1"
And my inbox is empty
And run the command "user:setting signer1 activity notify_email_libresign 1"
When sending "post" to ocs "/apps/libresign/api/v1/request-signature"
| file | {"url":"<BASE_URL>/apps/libresign/develop/pdf"} |
| users | [{"identify":{"account":"signer1"}}] |
@ -193,7 +194,10 @@ Feature: request-signature
And user signer1 has the following notifications
| app | object_type | object_id | subject |
| libresign | sign | document | There is a file for you to sign |
And there should be 0 emails in my inbox
And wait for 1 second
And run the command "activity:send-mails"
And there should be 1 emails in my inbox
And I open the latest email to "signer1@domain.test" with subject "Activity at Nextcloud"
Scenario: Request to sign with error using account as identifier with invalid email
Given as user "admin"

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="4.30.0@d0bc6e25d89f649e4f36a534f330f8bb4643dd69">
<file src="lib/AppInfo/Application.php">
<InvalidArgument occurrences="3">
<InvalidArgument occurrences="4">
<code>registerEventListener</code>
<code>registerEventListener</code>
<code>registerEventListener</code>
<code>registerEventListener</code>