diff --git a/CHANGELOG.md b/CHANGELOG.md index 084163922..dd597ac2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,8 @@ All notable changes to this project will be documented in this file. - Reveal hidden voters if hidden in case of performance concerns - Support better readability of vote page - Added revoking of shares - - Shares can now be revoked which works as a read only share mechanism. Revoked shares can still enter the poll, but all interaction (voting and commenting) is disabled. - - Deletion of unsent shares have no more a redo timer - - Deletion of revoked shares deletes the users votes as well + - Shares can now be locked which works as a read only share mechanism. Locked shares can still enter the poll, but every interaction (voting and commenting) is disabled. + - Deletion of locked shares deletes the users votes as well ### Changes - Improved username check for public polls with a large number of groups in the backend ## [5.3.2] - 2023-09-11 diff --git a/appinfo/info.xml b/appinfo/info.xml index f3c47ab04..81060de48 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -4,7 +4,7 @@ Polls A polls app, similar to Doodle/Dudle with the possibility to restrict access. A polls app, similar to Doodle/Dudle with the possibility to restrict access (members, certain groups/users, hidden and public). - 5.4.0-beta5 + 5.4.0-beta6 agpl Vinzenz Rosenkranz René Gieling diff --git a/appinfo/routes.php b/appinfo/routes.php index 86e393ad3..648996c17 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -98,8 +98,8 @@ ['name' => 'share#admin_to_user', 'url' => '/share/{token}/user', 'verb' => 'PUT'], ['name' => 'share#set_label', 'url' => '/share/{token}/setlabel', 'verb' => 'PUT'], ['name' => 'share#set_public_poll_email', 'url' => '/share/{token}/publicpollemail/{value}', 'verb' => 'PUT'], - ['name' => 'share#revoke', 'url' => '/share/{token}/revoke', 'verb' => 'PUT'], - ['name' => 'share#re_revoke', 'url' => '/share/{token}/rerevoke', 'verb' => 'PUT'], + ['name' => 'share#lock', 'url' => '/share/{token}/lock', 'verb' => 'PUT'], + ['name' => 'share#unlock', 'url' => '/share/{token}/unlock', 'verb' => 'PUT'], ['name' => 'settings#getAppSettings', 'url' => '/settings/app', 'verb' => 'GET'], ['name' => 'settings#writeAppSettings', 'url' => '/settings/app', 'verb' => 'POST'], diff --git a/lib/Controller/ShareController.php b/lib/Controller/ShareController.php index ff12770b3..157182ce9 100644 --- a/lib/Controller/ShareController.php +++ b/lib/Controller/ShareController.php @@ -118,8 +118,8 @@ public function delete(string $token): JSONResponse { * @NoAdminRequired */ - public function revoke(string $token): JSONResponse { - return $this->responseDeleteTolerant(fn () => ['share' => $this->shareService->revoke(token: $token)]); + public function lock(string $token): JSONResponse { + return $this->responseDeleteTolerant(fn () => ['share' => $this->shareService->lock(token: $token)]); } /** @@ -127,8 +127,8 @@ public function revoke(string $token): JSONResponse { * @NoAdminRequired */ - public function reRevoke(string $token): JSONResponse { - return $this->responseDeleteTolerant(fn () => ['share' => $this->shareService->reRevoke(token: $token)]); + public function unlock(string $token): JSONResponse { + return $this->responseDeleteTolerant(fn () => ['share' => $this->shareService->unlock(token: $token)]); } /** diff --git a/lib/Db/Share.php b/lib/Db/Share.php index 89195fe03..8fb45633f 100644 --- a/lib/Db/Share.php +++ b/lib/Db/Share.php @@ -47,8 +47,8 @@ * @method void setInvitationSent(integer $value) * @method int getReminderSent() * @method void setReminderSent(integer $value) - * @method int getRevoked() - * @method void setRevoked(integer $value) + * @method int getLocked() + * @method void setLocked(integer $value) * @method string getDisplayName() * @method void setDisplayName(string $value) * @method string getMiscSettings() @@ -123,7 +123,7 @@ class Share extends Entity implements JsonSerializable { protected ?string $emailAddress = null; protected int $invitationSent = 0; protected int $reminderSent = 0; - protected int $revoked = 0; + protected int $locked = 0; protected ?string $displayName = null; protected ?string $miscSettings = ''; protected int $voted = 0; @@ -131,7 +131,7 @@ class Share extends Entity implements JsonSerializable { public function __construct() { $this->addType('pollId', 'int'); $this->addType('invitationSent', 'int'); - $this->addType('Revoked', 'int'); + $this->addType('Locked', 'int'); $this->addType('reminderSent', 'int'); $this->urlGenerator = Container::queryClass(IURLGenerator::class); $this->appSettings = new AppSettings; @@ -150,7 +150,7 @@ public function jsonSerialize(): array { 'emailAddress' => $this->getEmailAddress(), 'invitationSent' => $this->getInvitationSent(), 'reminderSent' => $this->getReminderSent(), - 'revoked' => $this->getRevoked(), + 'locked' => $this->getLocked(), 'displayName' => $this->getDisplayName(), 'isNoUser' => !(in_array($this->getType(), [self::TYPE_USER, self::TYPE_ADMIN], true)), 'URL' => $this->getURL(), diff --git a/lib/Event/ShareEvent.php b/lib/Event/ShareEvent.php index bc1f71ef9..ba891a3a0 100644 --- a/lib/Event/ShareEvent.php +++ b/lib/Event/ShareEvent.php @@ -35,7 +35,7 @@ abstract class ShareEvent extends BaseEvent { public const CHANGE_REG_CONSTR = 'share_change_reg_const'; public const REGISTRATION = 'share_registration'; public const DELETE = 'share_delete'; - public const REVOKED = 'share_revoked'; + public const LOCKED = 'share_locked'; private Share $share; // protected UserBase $sharee = null; diff --git a/lib/Event/ShareRevokedEvent.php b/lib/Event/ShareLockedEvent.php similarity index 92% rename from lib/Event/ShareRevokedEvent.php rename to lib/Event/ShareLockedEvent.php index 465dc52e6..f204d2c46 100644 --- a/lib/Event/ShareRevokedEvent.php +++ b/lib/Event/ShareLockedEvent.php @@ -25,9 +25,9 @@ use OCA\Polls\Db\Share; -class ShareRevokedEvent extends ShareEvent { +class ShareLockedEvent extends ShareEvent { public function __construct(Share $share) { parent::__construct($share); - $this->eventId = self::REVOKED; + $this->eventId = self::LOCKED; } } diff --git a/lib/Migration/TableSchema.php b/lib/Migration/TableSchema.php index 7de5fb0fa..d86944b95 100644 --- a/lib/Migration/TableSchema.php +++ b/lib/Migration/TableSchema.php @@ -143,6 +143,7 @@ abstract class TableSchema { Share::TABLE => [ 'user', // dropped in 1.01 'user_email', // dropped in 1.06 and migrated to email_address + 'revoked', // introduced in 5.4.0-beta3 and replaced with column 'locked' in 5.4.0-beta5, no migration ], Log::TABLE => [ 'message', // dropped in 1.07, orphaned @@ -218,7 +219,7 @@ abstract class TableSchema { 'display_name' => ['type' => Types::STRING, 'options' => ['notnull' => false, 'default' => null, 'length' => 256]], 'email_address' => ['type' => Types::STRING, 'options' => ['notnull' => false, 'default' => null, 'length' => 256]], 'invitation_sent' => ['type' => Types::BIGINT, 'options' => ['notnull' => true, 'default' => 0, 'length' => 20]], - 'revoked' => ['type' => Types::BIGINT, 'options' => ['notnull' => true, 'default' => 0, 'length' => 20]], + 'locked' => ['type' => Types::BIGINT, 'options' => ['notnull' => true, 'default' => 0, 'length' => 20]], 'reminder_sent' => ['type' => Types::BIGINT, 'options' => ['notnull' => true, 'default' => 0, 'length' => 20]], 'misc_settings' => ['type' => Types::TEXT, 'options' => ['notnull' => false, 'default' => null, 'length' => 65535]], ], diff --git a/lib/Migration/Version050400Date20231011211202.php b/lib/Migration/Version050400Date20231011211203.php similarity index 98% rename from lib/Migration/Version050400Date20231011211202.php rename to lib/Migration/Version050400Date20231011211203.php index 594983734..e5cfe04e2 100644 --- a/lib/Migration/Version050400Date20231011211202.php +++ b/lib/Migration/Version050400Date20231011211203.php @@ -36,7 +36,7 @@ * Changed class naming: Version[jjmmpp]Date[YYYYMMDDHHMMSS] * Version: jj = major version, mm = minor, pp = patch */ -class Version050400Date20231011211202 extends SimpleMigrationStep { +class Version050400Date20231011211203 extends SimpleMigrationStep { private ISchemaWrapper $schema; public function __construct( diff --git a/lib/Model/Acl.php b/lib/Model/Acl.php index 2dd8a78fd..b5593c661 100644 --- a/lib/Model/Acl.php +++ b/lib/Model/Acl.php @@ -369,7 +369,7 @@ private function getIsDelegatedAdmin(): bool { } if ($this->loadShare()) { // load share, if not loaded - return $this->share->getType() === Share::TYPE_ADMIN && !$this->share->getRevoked(); + return $this->share->getType() === Share::TYPE_ADMIN && !$this->share->getLocked(); }; return false; } @@ -480,7 +480,7 @@ private function getAllowAddOptions(): bool { return false; // Request for option proposals is expired, deny } - if ($this->share?->getRevoked()) { + if ($this->share?->getLocked()) { return false; // Request for option proposals is expired, deny } @@ -499,7 +499,7 @@ private function getAllowComment(): bool { return false; // public shares are not allowed to comment } - if ($this->share?->getRevoked()) { + if ($this->share?->getLocked()) { return false; // public shares are not allowed to comment } @@ -518,7 +518,7 @@ private function getAllowVote(): bool { return false; // public shares are not allowed to vote } - if ($this->share?->getRevoked()) { + if ($this->share?->getLocked()) { return false; // public shares are not allowed to vote } diff --git a/lib/Service/ShareService.php b/lib/Service/ShareService.php index d5a14418b..2b27acbf4 100644 --- a/lib/Service/ShareService.php +++ b/lib/Service/ShareService.php @@ -31,8 +31,8 @@ use OCA\Polls\Event\ShareChangedRegistrationConstraintEvent; use OCA\Polls\Event\ShareCreateEvent; use OCA\Polls\Event\ShareDeletedEvent; +use OCA\Polls\Event\ShareLockedEvent; use OCA\Polls\Event\ShareRegistrationEvent; -use OCA\Polls\Event\ShareRevokedEvent; use OCA\Polls\Event\ShareTypeChangedEvent; use OCA\Polls\Exceptions\ForbiddenException; use OCA\Polls\Exceptions\InvalidShareTypeException; @@ -327,31 +327,31 @@ public function delete(Share $share = null, string $token = null): string { } /** - * Revoke share + * Lock share */ - public function revoke(Share $share = null, string $token = null): string { + public function lock(Share $share = null, string $token = null): string { if ($token) { $share = $this->shareMapper->findByToken($token); } $this->acl->setPollId($share->getPollId(), Acl::PERMISSION_POLL_EDIT); - $share->setRevoked(time()); + $share->setLocked(time()); $this->shareMapper->update($share); - $this->eventDispatcher->dispatchTyped(new ShareRevokedEvent($share)); + $this->eventDispatcher->dispatchTyped(new ShareLockedEvent($share)); return $share->getToken(); } /** - * Re-revoke share + * Unlock share */ - public function reRevoke(Share $share = null, string $token = null): string { + public function unlock(Share $share = null, string $token = null): string { if ($token) { $share = $this->shareMapper->findByToken($token); } $this->acl->setPollId($share->getPollId(), Acl::PERMISSION_POLL_EDIT); - $share->setRevoked(0); + $share->setLocked(0); $this->shareMapper->update($share); $this->eventDispatcher->dispatchTyped(new ShareCreateEvent($share)); diff --git a/src/js/Api/modules/shares.js b/src/js/Api/modules/shares.js index b45c1015d..d5cd08f96 100644 --- a/src/js/Api/modules/shares.js +++ b/src/js/Api/modules/shares.js @@ -95,19 +95,19 @@ const shares = { }) }, - revokeShare(shareToken) { + lockShare(shareToken) { return httpInstance.request({ method: 'PUT', - url: `share/${shareToken}/revoke`, - cancelToken: cancelTokenHandlerObject[this.revokeShare.name].handleRequestCancellation().token, + url: `share/${shareToken}/lock`, + cancelToken: cancelTokenHandlerObject[this.lockShare.name].handleRequestCancellation().token, }) }, - reRevokeShare(shareToken) { + unlockShare(shareToken) { return httpInstance.request({ method: 'PUT', - url: `share/${shareToken}/rerevoke`, - cancelToken: cancelTokenHandlerObject[this.reRevokeShare.name].handleRequestCancellation().token, + url: `share/${shareToken}/unlock`, + cancelToken: cancelTokenHandlerObject[this.unlockShare.name].handleRequestCancellation().token, }) }, diff --git a/src/js/components/Actions/modules/ActionDelete.vue b/src/js/components/Actions/modules/ActionDelete.vue index bf6c914ff..375151899 100644 --- a/src/js/components/Actions/modules/ActionDelete.vue +++ b/src/js/components/Actions/modules/ActionDelete.vue @@ -29,7 +29,7 @@ - import { NcButton } from '@nextcloud/vue' import DeleteIcon from 'vue-material-design-icons/Delete.vue' -import RevokeIcon from 'vue-material-design-icons/Close.vue' +import LockIcon from 'vue-material-design-icons/Lock.vue' import UndoIcon from 'vue-material-design-icons/ArrowULeftTop.vue' export default { name: 'ActionDelete', components: { DeleteIcon, - RevokeIcon, + LockIcon, UndoIcon, NcButton, }, @@ -69,7 +69,7 @@ export default { type: Number, default: 20, }, - revoke: { + lock: { type: Boolean, default: false, }, diff --git a/src/js/components/Shares/ShareItem.vue b/src/js/components/Shares/ShareItem.vue index 904ce5e9e..05e53cb46 100644 --- a/src/js/components/Shares/ShareItem.vue +++ b/src/js/components/Shares/ShareItem.vue @@ -24,7 +24,6 @@ - + {{ t('polls', 'Do not ask for an email address') }} - - - - + - {{ t('polls', 'Re-Revoke share') }} + {{ share.locked ? t('polls', 'Unlock share') : t('polls', 'Lock share') }} - @@ -143,7 +138,8 @@ import EditIcon from 'vue-material-design-icons/Pencil.vue' import WithdrawAdminIcon from 'vue-material-design-icons/ShieldCrownOutline.vue' import ClippyIcon from 'vue-material-design-icons/ClipboardArrowLeftOutline.vue' import QrIcon from 'vue-material-design-icons/Qrcode.vue' -import ReRevokeIcon from 'vue-material-design-icons/Recycle.vue' +import LockIcon from 'vue-material-design-icons/Lock.vue' +import UnlockIcon from 'vue-material-design-icons/LockOpenVariant.vue' export default { name: 'ShareItem', @@ -164,7 +160,8 @@ export default { NcActionRadio, ActionDelete, ResolveGroupIcon, - ReRevokeIcon, + LockIcon, + UnlockIcon, }, props: { @@ -183,15 +180,12 @@ export default { this.$store.commit('shares/setShareProperty', { id: this.share.id, displayName: value }) }, }, + deleteButtonCaption() { - if (this.share.voted && this.share.revoked) { + if (this.share.voted && this.share.locked) { return t('polls', 'Delete share and remove user from poll') } - if (this.share.voted && !this.share.revoked) { - return t('polls', 'Revoke share') - } - return t('polls', 'Delete share') }, @@ -200,23 +194,35 @@ export default { methods: { ...mapActions({ deleteShare: 'shares/delete', - revokeShare: 'shares/revoke', - reRevokeShare: 'shares/reRevoke', + lockShare: 'shares/lock', + unlockShare: 'shares/unlock', switchAdmin: 'shares/switchAdmin', setPublicPollEmail: 'shares/setPublicPollEmail', setLabel: 'shares/writeLabel', deleteUser: 'votes/deleteUser', }), + async switchLocked(share) { + try { + if (share.locked) { + this.unlockShare({ share }) + showSuccess(t('polls', 'Share for user {displayName} unlocked', { displayName: share.displayName })) + } else { + this.lockShare({ share }) + showSuccess(t('polls', 'Share for user {displayName} locked', { displayName: share.displayName })) + } + } catch (e) { + showError(t('polls', 'Error deleting or revoking share for user {displayName}', { displayName: share.displayName })) + console.error('Error deleting or revoking share', { share }, e.response) + } + }, + async clickDeleted(share) { try { - if (share.voted && share.revoked) { + if (share.voted && share.locked) { this.deleteShare({ share }) this.deleteUser({ userId: share.userId }) showSuccess(t('polls', 'Deleted share and votes for {displayName}', { displayName: share.displayName })) - } else if (share.voted && !share.revoked) { - this.revokeShare({ share }) - showSuccess(t('polls', 'Share for user {displayName} revoked', { displayName: share.displayName })) } else { this.deleteShare({ share }) showSuccess(t('polls', 'Deleted share for user {displayName}', { displayName: share.displayName })) diff --git a/src/js/components/Shares/SharesListRevoked.vue b/src/js/components/Shares/SharesListLocked.vue similarity index 83% rename from src/js/components/Shares/SharesListRevoked.vue rename to src/js/components/Shares/SharesListLocked.vue index bc89f86b0..32fc2e0db 100644 --- a/src/js/components/Shares/SharesListRevoked.vue +++ b/src/js/components/Shares/SharesListLocked.vue @@ -21,12 +21,12 @@ -->