mirror of
https://github.com/LibreSign/libresign.git
synced 2025-12-17 21:12:16 +01:00
Implement activity
Signed-off-by: Vitor Mattos <vitor@php.rio>
This commit is contained in:
parent
1af890de63
commit
863508e8e4
8 changed files with 301 additions and 2 deletions
BIN
img/app-dark.png
Normal file
BIN
img/app-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 896 B |
106
lib/Activity/Listener.php
Normal file
106
lib/Activity/Listener.php
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
78
lib/Activity/Provider/SignRequest.php
Normal file
78
lib/Activity/Provider/SignRequest.php
Normal 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
81
lib/Activity/Setting.php
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue