Skip to content

Commit

Permalink
transfer ownership of a form
Browse files Browse the repository at this point in the history
Signed-off-by: hamza221 <hamzamahjoubi221@gmail.com>
  • Loading branch information
hamza221 committed Jul 24, 2023
1 parent d0fcb95 commit 865f6f5
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 1 deletion.
9 changes: 8 additions & 1 deletion appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,14 @@
'apiVersion' => 'v2(\.1)?'
]
],
[
'name' => 'api#ownerTransfer',
'url' => '/api/{apiVersion}/form/transfer',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v2(\.1)?'
]
],
[
'name' => 'api#deleteForm',
'url' => '/api/{apiVersion}/form/{id}',
Expand All @@ -141,7 +149,6 @@
'apiVersion' => 'v2(\.1)?'
]
],

// Questions
[
'name' => 'api#newQuestion',
Expand Down
38 changes: 38 additions & 0 deletions lib/Controller/ApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,44 @@ public function updateForm(int $id, array $keyValuePairs): DataResponse {
return new DataResponse($form->getId());
}

/**
* @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
]);

Check warning on line 423 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L419-L423

Added lines #L419 - L423 were not covered by tests

try {
$form = $this->formMapper->findById($formId);
} catch (IMapperException $e) {
$this->logger->debug('Could not find form');
throw new OCSBadRequestException('Could not find form');

Check warning on line 429 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L426-L429

Added lines #L426 - L429 were not covered by tests
}

if ($form->getOwnerId() !== $this->currentUser->getUID()) {
$this->logger->debug('This form is not owned by the current user');
throw new OCSForbiddenException();

Check warning on line 434 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L432-L434

Added lines #L432 - L434 were not covered by tests
}

// update form owner
$form->setOwnerId($uid);

Check warning on line 438 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L438

Added line #L438 was not covered by tests

// Update changed Columns in Db.
$this->formMapper->update($form);

Check warning on line 441 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L441

Added line #L441 was not covered by tests

return new DataResponse($form->getOwnerId());

Check warning on line 443 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L443

Added line #L443 was not covered by tests
}

/**
* @CORS
* @NoAdminRequired
Expand Down
21 changes: 21 additions & 0 deletions lib/Db/ShareMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,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();

Check warning on line 115 in lib/Db/ShareMapper.php

View check run for this annotation

Codecov / codecov/patch

lib/Db/ShareMapper.php#L114-L115

Added lines #L114 - L115 were not covered by tests

$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))
);

Check warning on line 124 in lib/Db/ShareMapper.php

View check run for this annotation

Codecov / codecov/patch

lib/Db/ShareMapper.php#L117-L124

Added lines #L117 - L124 were not covered by tests

return $this->findEntity($qb);

Check warning on line 126 in lib/Db/ShareMapper.php

View check run for this annotation

Codecov / codecov/patch

lib/Db/ShareMapper.php#L126

Added line #L126 was not covered by tests
}
/**
* Delete a share
* @param int $id of the share.
Expand Down
3 changes: 3 additions & 0 deletions src/components/SidebarTabs/SettingsSidebarTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
{{ t('forms', 'Show expiration date on form') }}
</NcCheckboxRadioSwitch>
</div>
<TransferOwnership :form="form" />
</div>
</template>

Expand All @@ -67,11 +68,13 @@ import moment from '@nextcloud/moment'
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
import NcDatetimePicker from '@nextcloud/vue/dist/Components/NcDatetimePicker.js'
import ShareTypes from '../../mixins/ShareTypes.js'
import TransferOwnership from './TransferOwnership.vue'
export default {
components: {
NcCheckboxRadioSwitch,
NcDatetimePicker,
TransferOwnership,
},
mixins: [ShareTypes],
Expand Down
169 changes: 169 additions & 0 deletions src/components/SidebarTabs/TransferOwnership.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<!--
- @copyright Copyright (c) 2022 Hamza Mahjoubi <hamzamahjoubi221@gmail.com>
-
- @author Hamza Mahjoubi <hamzamahjoubi221@gmail.com>
-
- @license AGPL-3.0-or-later
-
- 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/>.
-
-->

<template>
<div>
<NcButton @click="openModal">
{{ t('forms', 'Transfer ownership') }}
</NcButton>
<NcModal v-if="showModal"
:title="'Transfer ' + form.title"
:out-transition="true"
@close="closeModal">
<div class="modal-content">
<h1 class="modal-content__section">
{{ t('forms', 'Transfer ') }} {{ form.title }}
</h1>
<div class="modal-content__section">
<p class="modal-content__text">
{{ t('forms', 'Account to transfer to') }}
</p>
<SharingSearchDiv :is-ownership-transfer="true" @add-share="setNewOwner" />
<div v-if="transferData.displayName.length > 0" class="modal-content__user">
<p>{{ transferData.displayName }}</p>
<NcButton type="tertiary-no-background" @click="clearSelected">
X
</NcButton>
</div>
</div>
<div class="modal-content__section">
<p class="modal-content__text">
{{ t('forms', 'Type ') }} <strong> {{ confirmationString }} </strong>{{ t('forms', ' to confirm') }}
</p>
<NcTextField :value.sync="confirmationInput"
:success="confirmationInput === confirmationString"/>

Check failure on line 53 in src/components/SidebarTabs/TransferOwnership.vue

View workflow job for this annotation

GitHub Actions / eslint node

Expected a space before '/>', but not found
</div>
<NcButton :disabled="confirmationInput !== confirmationString" type="error" @click="onOwnershipTransfer">
{{ t('forms', 'I understand, transfer this form') }}
</NcButton>
</div>
</NcModal>
</div>
</template>

<script>
import { showSuccess } from '@nextcloud/dialogs'
import { generateOcsUrl } from '@nextcloud/router'
import axios from '@nextcloud/axios'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcModal from '@nextcloud/vue/dist/Components/NcModal.js'
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
import logger from '../../utils/Logger.js'
import SharingSearchDiv from './SharingSearchDiv.vue'
export default {
components: {
NcButton,
NcModal,
NcTextField,
SharingSearchDiv,
},
props: {
form: {
type: Object,
required: true,
},
},
data() {
return {
showModal: false,
transferData: { formId: null, userId: null, displayName: '' },
confirmationInput: '',
loading: false,
}
},
computed: {
confirmationString() {
return `${this.form.ownerId}/${this.form.title}`
},
},
methods: {
clearText() {
this.confirmationInput = ''
},
closeModal() {
this.showModal = false
},
setNewOwner(share) {
this.transferData.userId = share.shareWith
this.transferData.formId = this.form.id
this.transferData.displayName = share.displayName
},
openModal() {
this.showModal = true
},
async onOwnershipTransfer() {
this.showModal = false
if (this.transferData.formId && this.transferData.userId) {
try {
await axios.post(generateOcsUrl('apps/forms/api/v2/form/transfer'), {
formId: this.transferData.formId,
uid: this.transferData.userId,
})
showSuccess(`${t('forms', 'This form is now owned by')} ${this.transferData.displayName}`)
this.$router.push({ name: 'root' })
location.reload()
} catch (error) {
logger.error('Error while transfering form ownership', { error })
showError(t('forms', 'An error occurred while transfering ownership'))

Check failure on line 134 in src/components/SidebarTabs/TransferOwnership.vue

View workflow job for this annotation

GitHub Actions / eslint node

'showError' is not defined
}
} else {
logger.error('Null parameters while transfering form ownership', { transferData: this.tranferData })
showError(t('forms', 'An error occurred while transfering ownership'))

Check failure on line 139 in src/components/SidebarTabs/TransferOwnership.vue

View workflow job for this annotation

GitHub Actions / eslint node

'showError' is not defined
}
},
clearSelected() {
this.transferData = { formId: null, userId: null, displayName: '' }
},
},
}
</script>

<style lang="scss" scoped>
.modal-content {
margin: 50px;

Check failure on line 153 in src/components/SidebarTabs/TransferOwnership.vue

View workflow job for this annotation

GitHub Actions / eslint node

Trailing spaces not allowed
&__text{
text-align: start;
margin-bottom: 10px;

Check failure on line 156 in src/components/SidebarTabs/TransferOwnership.vue

View workflow job for this annotation

GitHub Actions / eslint node

Mixed spaces and tabs
}
&__section{
text-align: start;
margin-bottom: 10px;

Check failure on line 161 in src/components/SidebarTabs/TransferOwnership.vue

View workflow job for this annotation

GitHub Actions / eslint node

Mixed spaces and tabs
}
&__user {
display: flex;

Check failure on line 165 in src/components/SidebarTabs/TransferOwnership.vue

View workflow job for this annotation

GitHub Actions / eslint node

Mixed spaces and tabs
align-items: center;

Check failure on line 166 in src/components/SidebarTabs/TransferOwnership.vue

View workflow job for this annotation

GitHub Actions / eslint node

Mixed spaces and tabs
}
}
</style>

0 comments on commit 865f6f5

Please sign in to comment.