From 8cfd77a612204cdc8abd148acdc55a00fb80af31 Mon Sep 17 00:00:00 2001 From: hamza221 Date: Sat, 26 Nov 2022 21:12:47 +0100 Subject: [PATCH] transfer ownership of a form Signed-off-by: hamza221 --- appinfo/routes.php | 8 ++ lib/Controller/ApiController.php | 45 ++++++++++++ lib/Db/ShareMapper.php | 21 ++++++ src/Forms.vue | 73 ++++++++++++++++++- .../SidebarTabs/SettingsSidebarTab.vue | 19 +++++ src/views/Sidebar.vue | 6 +- 6 files changed, 169 insertions(+), 3 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index 8ebe6478b..e6a1e6c58 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -131,6 +131,14 @@ 'apiVersion' => 'v2' ] ], + [ + 'name' => 'api#ownerTransfer', + 'url' => '/api/{apiVersion}/form/transfer', + 'verb' => 'POST', + 'requirements' => [ + 'apiVersion' => 'v2' + ] + ], // Questions [ diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php index 9706b1970..fd1956c52 100644 --- a/lib/Controller/ApiController.php +++ b/lib/Controller/ApiController.php @@ -1136,4 +1136,49 @@ public function exportSubmissionsToCloud(string $hash, string $path) { return new DataResponse($fileName); } + /** + * @NoAdminRequired + * + * Transfer ownership of a form to another user + * + * @param int $formId id of the form to update + * @param string $uid id of the new owner + * @return DataResponse + * @throws OCSBadRequestException + * @throws OCSForbiddenException + */ + public function ownerTransfer(int $formId, string $uid): DataResponse { + $this->logger->debug('Updating owner: formId: {formId}, userId: {uid}', [ + 'formId' => $formId, + 'uid' => $uid + ]); + + try { + $form = $this->formMapper->findById($formId); + } catch (IMapperException $e) { + $this->logger->debug('Could not find form'); + throw new OCSBadRequestException('Could not find form'); + } + + if ($form->getOwnerId() !== $this->currentUser->getUID()) { + $this->logger->debug('This form is not owned by the current user'); + throw new OCSForbiddenException(); + } + + // update form owner + $form->setOwnerId($uid); + + // Update changed Columns in Db. + $this->formMapper->update($form); + + //delete this form from shares for the new owner + try { + $share = $this->shareMapper->findPublicShareByFormIdAndUid($formId, $uid); + $this->shareMapper->deleteById($share->getId()); + } catch (IMapperException $e) { + $this->logger->debug('No shares found'); + } + + return new DataResponse($form->getOwnerId()); + } } diff --git a/lib/Db/ShareMapper.php b/lib/Db/ShareMapper.php index 726fbdacc..7693ff972 100644 --- a/lib/Db/ShareMapper.php +++ b/lib/Db/ShareMapper.php @@ -100,7 +100,28 @@ public function findPublicShareByHash(string $hash): Share { return $this->findEntity($qb); } + /** + * Find Share by formId and user id + * @param int $formId + * @param string $uid + * @return Share + * @throws MultipleObjectsReturnedException if more than one result + * @throws DoesNotExistException if not found + */ + public function findPublicShareByFormIdAndUid(int $formId, string $uid): Share { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()) + ->where( + $qb->expr()->eq('form_id', $qb->createNamedParameter($formId, IQueryBuilder::PARAM_INT)) + ) + ->andWhere( + $qb->expr()->eq('share_with', $qb->createNamedParameter($uid, IQueryBuilder::PARAM_STR)) + ); + + return $this->findEntity($qb); + } /** * Delete a share * @param int $id of the share. diff --git a/src/Forms.vue b/src/Forms.vue index fe9393ac2..8f6a9176e 100644 --- a/src/Forms.vue +++ b/src/Forms.vue @@ -96,8 +96,27 @@ :form="selectedForm" :opened.sync="sidebarOpened" :active.sync="sidebarActive" - name="sidebar" /> + name="sidebar" + @transfer:ownership="openModal" /> + + + @@ -105,8 +124,9 @@ import { emit } from '@nextcloud/event-bus' import { generateOcsUrl } from '@nextcloud/router' import { loadState } from '@nextcloud/initial-state' -import { showError } from '@nextcloud/dialogs' +import { showError, showSuccess } from '@nextcloud/dialogs' import axios from '@nextcloud/axios' +import Router from 'vue-router' import NcAppContent from '@nextcloud/vue/dist/Components/NcAppContent.js' import NcAppNavigation from '@nextcloud/vue/dist/Components/NcAppNavigation.js' @@ -117,6 +137,7 @@ import NcContent from '@nextcloud/vue/dist/Components/NcContent.js' import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js' import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js' import isMobile from '@nextcloud/vue/dist/Mixins/isMobile.js' +import NcModal from '@nextcloud/vue/dist/Components/NcModal.js' import IconPlus from 'vue-material-design-icons/Plus.vue' @@ -141,6 +162,7 @@ export default { NcContent, NcEmptyContent, NcLoadingIcon, + NcModal, }, mixins: [isMobile, PermissionTypes], @@ -148,10 +170,12 @@ export default { data() { return { loading: true, + modal: false, sidebarOpened: false, sidebarActive: 'forms-sharing', forms: [], sharedForms: [], + tranferData: { formId: null, userId: null, displayName: null }, canCreateForms: loadState(appName, 'appConfig').canCreateForms, } @@ -221,6 +245,40 @@ export default { }, methods: { + closeModal() { + this.modal = false + showError(t('forms', 'Ownership transfer was Cancelled')) + + }, + openModal(share, id) { + this.modal = true + this.tranferData.formId = id + this.tranferData.userId = share?.shareWith + this.tranferData.displayName = share?.displayName + + }, + async onOwnershipTransfer() { + this.modal = false + if (this.tranferData.formId && this.tranferData.userId) { + try { + await axios.post(generateOcsUrl('apps/forms/api/v2/form/transfer'), { + formId: this.tranferData.formId, + uid: this.tranferData.userId, + }) + showSuccess(`${t('forms', 'This form is now owned by')} ${this.tranferData.displayName}`) + Router.push('/') + location.reload() + + } catch (error) { + logger.error('Error while transfering form ownership', { error }) + showError(t('forms', 'An error occurred while transfering ownership')) + } + + } else { + logger.error('Null parameters while transfering form ownership', { transferData: this.tranferData }) + showError(t('forms', 'An error occurred while transfering ownership')) + } + }, /** * Closes the App-Navigation on mobile-devices */ @@ -348,3 +406,14 @@ export default { }, } + diff --git a/src/components/SidebarTabs/SettingsSidebarTab.vue b/src/components/SidebarTabs/SettingsSidebarTab.vue index d43500616..2c3285f61 100644 --- a/src/components/SidebarTabs/SettingsSidebarTab.vue +++ b/src/components/SidebarTabs/SettingsSidebarTab.vue @@ -23,6 +23,15 @@ + @update:formProp="onPropertyChange" + @transfer:ownership="onOwnershipTransfer" /> @@ -97,6 +98,9 @@ export default { onUpdateActive(active) { this.$emit('update:active', active) }, + onOwnershipTransfer(share) { + this.$emit('transfer:ownership', share, this.form.id) + }, /** * Save Form-Properties