Added basic ownership transfer for shared documents
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import '/imports/api/creature/creatures/methods/insertCreature.js';
|
||||
import '/imports/api/creature/creatures/methods/removeCreature.js';
|
||||
import '/imports/api/creature/creatures/methods/restCreature.js';
|
||||
import '/imports/api/creature/creatures/methods/transferCreatureOwnership.js';
|
||||
import '/imports/api/creature/creatures/methods/updateCreature.js';
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import { assertOwnership } from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||
import { getUserTier } from '/imports/api/users/patreon/tiers.js';
|
||||
|
||||
const transferCreatureOwnership = new ValidatedMethod({
|
||||
|
||||
name: 'creatures.methods.transferOwnership',
|
||||
|
||||
validate: new SimpleSchema({
|
||||
creatureId: {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
},
|
||||
userId: {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
},
|
||||
}).validator(),
|
||||
|
||||
mixins: [RateLimiterMixin],
|
||||
rateLimit: {
|
||||
numRequests: 5,
|
||||
timeInterval: 5000,
|
||||
},
|
||||
|
||||
run({creatureId, userId}) {
|
||||
assertOwnership(creatureId, this.userId);
|
||||
|
||||
let tier = getUserTier(userId);
|
||||
let currentCharacterCount = Creatures.find({
|
||||
owner: userId,
|
||||
}, {
|
||||
fields: {_id: 1},
|
||||
}).count();
|
||||
|
||||
if (
|
||||
tier.characterSlots !== -1 &&
|
||||
currentCharacterCount >= tier.characterSlots
|
||||
){
|
||||
throw new Meteor.Error('Creatures.methods.transferOwnership.denied',
|
||||
'The new owner is already at their character limit')
|
||||
}
|
||||
|
||||
Creatures.update(creatureId, {
|
||||
$set: {owner: userId},
|
||||
});
|
||||
|
||||
return creatureId;
|
||||
},
|
||||
});
|
||||
|
||||
export default transferCreatureOwnership;
|
||||
@@ -5,6 +5,7 @@ import getCollectionByName from '/imports/api/parenting/getCollectionByName.js';
|
||||
import { RefSchema } from '/imports/api/parenting/ChildSchema.js';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import { getUserTier } from '/imports/api/users/patreon/tiers.js';
|
||||
|
||||
const setPublic = new ValidatedMethod({
|
||||
name: 'sharing.setPublic',
|
||||
@@ -47,7 +48,7 @@ const updateUserSharePermissions = new ValidatedMethod({
|
||||
run({docRef, userId, role}){
|
||||
let doc = fetchDocByRef(docRef);
|
||||
if (role === 'none'){
|
||||
// only asser ownership if you aren't removing yourself
|
||||
// only assert ownership if you aren't removing yourself
|
||||
if (this.userId !== userId){
|
||||
assertOwnership(doc, this.userId);
|
||||
}
|
||||
@@ -74,4 +75,58 @@ const updateUserSharePermissions = new ValidatedMethod({
|
||||
},
|
||||
});
|
||||
|
||||
export { setPublic, updateUserSharePermissions };
|
||||
const transferOwnership = new ValidatedMethod({
|
||||
name: 'sharing.transferOwnership',
|
||||
validate: new SimpleSchema({
|
||||
docRef: RefSchema,
|
||||
userId: {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
},
|
||||
}).validator(),
|
||||
mixins: [RateLimiterMixin],
|
||||
rateLimit: {
|
||||
numRequests: 5,
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({docRef, userId}){
|
||||
let doc = fetchDocByRef(docRef);
|
||||
assertOwnership(doc, this.userId);
|
||||
|
||||
let collection = getCollectionByName(docRef.collection);
|
||||
|
||||
let tier = getUserTier(userId);
|
||||
if (docRef.collection === 'creatures'){
|
||||
let currentCharacterCount = collection.find({
|
||||
owner: userId,
|
||||
}, {
|
||||
fields: {_id: 1},
|
||||
}).count();
|
||||
|
||||
if (
|
||||
tier.characterSlots !== -1 &&
|
||||
currentCharacterCount >= tier.characterSlots
|
||||
){
|
||||
throw new Meteor.Error('Sharing.methods.transferOwnership.denied',
|
||||
'The new owner is already at their character limit')
|
||||
}
|
||||
} else if (docRef.collection === 'libraries'){
|
||||
if (!tier.paidBenefits){
|
||||
throw new Meteor.Error('Sharing.methods.transferOwnership.denied',
|
||||
'The new owner\'s Patreon tier does not have access to library ownership');
|
||||
}
|
||||
}
|
||||
|
||||
// First remove current permissions for the user
|
||||
collection.update(docRef.id, {
|
||||
$pullAll: { writers: userId, readers: userId },
|
||||
});
|
||||
// Then make the user the owner and the current owner a writer
|
||||
return collection.update(docRef.id, {
|
||||
$set: {owner: userId},
|
||||
$addToSet: { writers: this.userId },
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export { setPublic, updateUserSharePermissions, transferOwnership };
|
||||
|
||||
@@ -21,6 +21,7 @@ import ShareDialog from '/imports/ui/sharing/ShareDialog.vue';
|
||||
import SlotDetailsDialog from '/imports/ui/creature/slots/SlotDetailsDialog.vue';
|
||||
import SlotFillDialog from '/imports/ui/creature/slots/SlotFillDialog.vue';
|
||||
import TierTooLowDialog from '/imports/ui/user/TierTooLowDialog.vue';
|
||||
import TransferOwnershipDialog from '/imports/ui/sharing/TransferOwnershipDialog.vue';
|
||||
import UsernameDialog from '/imports/ui/user/UsernameDialog.vue';
|
||||
|
||||
export default {
|
||||
@@ -47,5 +48,6 @@ export default {
|
||||
SlotDetailsDialog,
|
||||
SlotFillDialog,
|
||||
TierTooLowDialog,
|
||||
TransferOwnershipDialog,
|
||||
UsernameDialog,
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
v-if="model.public && docRef.collection === 'libraries'"
|
||||
readonly
|
||||
label="Link"
|
||||
:value="'https://beta.dicecloud.com' + this.$router.resolve({
|
||||
:value="'https://beta.dicecloud.com' + $router.resolve({
|
||||
name: 'singleLibrary',
|
||||
params: { id: model._id },
|
||||
}).href"
|
||||
@@ -56,6 +56,7 @@
|
||||
<v-menu
|
||||
bottom
|
||||
left
|
||||
:data-id="'menu-' + user._id"
|
||||
>
|
||||
<template #activator="{ on }">
|
||||
<v-btn
|
||||
@@ -84,6 +85,15 @@
|
||||
</v-list-item-action>
|
||||
<v-list-item-title>View only</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item
|
||||
v-if="user.permission === 'writer'"
|
||||
@click="makeOwner(user)"
|
||||
>
|
||||
<v-list-item-action>
|
||||
<v-icon>mdi-signature</v-icon>
|
||||
</v-list-item-action>
|
||||
<v-list-item-title>Transfer Onwership</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="updateSharing(user._id, 'none')">
|
||||
<v-list-item-action>
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
@@ -181,6 +191,16 @@ export default {
|
||||
userId,
|
||||
role,
|
||||
});
|
||||
},
|
||||
makeOwner(user){
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'transfer-ownership-dialog',
|
||||
elementId: 'menu-' + user._id,
|
||||
data: {
|
||||
docRef: this.docRef,
|
||||
user,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
meteor: {
|
||||
|
||||
79
app/imports/ui/sharing/TransferOwnershipDialog.vue
Normal file
79
app/imports/ui/sharing/TransferOwnershipDialog.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
<template lang="html">
|
||||
<dialog-base>
|
||||
<v-toolbar-title slot="toolbar">
|
||||
Transfer Ownership
|
||||
</v-toolbar-title>
|
||||
<v-alert
|
||||
type="error"
|
||||
outlined
|
||||
>
|
||||
<template v-if="error">
|
||||
<p>
|
||||
{{ error }}
|
||||
</p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<p>
|
||||
Are you sure you want to transfer ownership to {{ user.username || user._id }}?
|
||||
</p><p>
|
||||
This can only be undone by the user you are transferring ownership to.
|
||||
</p><p>
|
||||
You will still have edit permission.
|
||||
</p>
|
||||
</template>
|
||||
</v-alert>
|
||||
<v-layout justify-center>
|
||||
<v-btn
|
||||
color="accent"
|
||||
@click="transfer"
|
||||
>
|
||||
Transfer
|
||||
<template v-if="user.username">
|
||||
to {{ user.username }}
|
||||
</template>
|
||||
</v-btn>
|
||||
</v-layout>
|
||||
</dialog-base>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||
import { transferOwnership } from '/imports/api/sharing/sharing.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DialogBase,
|
||||
},
|
||||
props: {
|
||||
docRef: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
user: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data(){ return {
|
||||
error: undefined,
|
||||
}},
|
||||
methods: {
|
||||
transfer(){
|
||||
transferOwnership.call({
|
||||
docRef: this.docRef,
|
||||
userId: this.user._id
|
||||
}, error => {
|
||||
if (!error){
|
||||
this.error = undefined;
|
||||
this.$store.dispatch('popDialogStack')
|
||||
return;
|
||||
}
|
||||
this.error = error.reason || error.message || error.toString();
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
Reference in New Issue
Block a user