Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swik 907 delete deck and transfer ownership #1073

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ca07914
[SWIK-907_delete_deck_and_transfer_ownership] Started to work on feat…
TBoonX Oct 26, 2018
c45ab8a
[SWIK-907_delete_deck_and_transfer_ownership] Add of remove deck acti…
TBoonX Oct 29, 2018
c8a8c19
[SWIK-907_delete_deck_and_transfer_ownership] Did the transfer owners…
TBoonX Oct 29, 2018
0f6ef0a
Add some API calls code for new deck service delete/change owner APIs
kprist Oct 30, 2018
1f41092
[SWIK-907_delete_deck_and_transfer_ownership] First improvements
TBoonX Oct 30, 2018
d6b9e11
Merge branch 'master' of github.com:slidewiki/slidewiki-platform into…
TBoonX Oct 30, 2018
359ea46
[SWIK-907_delete_deck_and_transfer_ownership] SHowing delete buton in…
TBoonX Oct 30, 2018
191ea37
[SWIK-907_delete_deck_and_transfer_ownership] Fixed indentation
TBoonX Oct 30, 2018
a4a8c10
Merge branch 'master' of github.com:slidewiki/slidewiki-platform into…
TBoonX Oct 30, 2018
45e4c90
[SWIK-907_delete_deck_and_transfer_ownership] Removed removeDeck acti…
TBoonX Oct 30, 2018
ffe23a2
Fix some delete subdeck flows and messages
kprist Oct 31, 2018
8373c39
Merge branch 'master' into SWIK-907_delete_deck_and_transfer_ownership
kprist Nov 13, 2018
f10d4a6
Simplify subdeck remove flow: don't provide option to delete
kprist Nov 13, 2018
e6b695d
Move deck delete button to the right; add checks after it's clicked
kprist Nov 13, 2018
2941b4f
Include "Delete" button in Transfer Ownership; close modal on delete
kprist Nov 13, 2018
4710dc6
Merge branch 'master' into SWIK-907_delete_deck_and_transfer_ownership
abijames Nov 22, 2018
41bdb71
[SWIK-907_delete_deck_and_transfer_ownership] Changed texts like Abi …
TBoonX Nov 23, 2018
7782915
Update delete deck buttons; add trash icon to delete, make "OK" green
kprist Nov 29, 2018
9ae3f31
Make indication of user to transfer deck to more visible; use keyboard
kprist Nov 29, 2018
9902fe8
Fix linting errors
kprist Nov 29, 2018
9d059c1
Improve how checking for deck usage before deleting works
kprist Nov 29, 2018
2afa9d5
[SWIK-907_delete_deck_and_transfer_ownership] Tooltip of deck deletio…
TBoonX Nov 30, 2018
bfa4b0c
Fix fork origin info when origin was deleted or its owner has changed
kprist Nov 30, 2018
ef58f77
Merge branch 'master' into SWIK-907_delete_deck_and_transfer_ownership
kprist Feb 26, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions actions/deckedit/deckDeletion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import log from '../log/clog';

import hideTransferOwnershipModal from './hideTransferOwnershipModal';

import UserProfileStore from '../../stores/UserProfileStore';

export default function deckDeletion(context, payload, done) {
log.info(context);

context.executeAction(hideTransferOwnershipModal, {} , () => {
context.dispatch('START_DELETE_DECK');

payload.jwt = context.getStore(UserProfileStore).jwt;

context.service.delete('deck.delete', payload, { timeout: 20 * 1000 }, (err, res) => {
if (err) {
context.dispatch('DELETE_DECK_ERROR', err);
}
else {
context.dispatch('DELETE_DECK_SUCCESS', res);
}
done();
});
});
}
7 changes: 7 additions & 0 deletions actions/deckedit/hideTransferOwnershipModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const log = require('../log/clog');

export default function hideTransferOwnershipModal(context, payload, done) {
log.info(context);
context.dispatch('HIDE_TRANSFER_OWNERSHIP_MODAL', {});
done();
}
41 changes: 41 additions & 0 deletions actions/deckedit/loadEditors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import log from '../log/clog';
import UserProfileStore from '../../stores/UserProfileStore';

export default function loadEditors(context, payload, done) {
log.info(context);
context.dispatch('START_TRANSFER_OWNERSHIP');

let userid = context.getStore(UserProfileStore).userid;
const groupids = payload.groups.map((group) => parseInt(group.id, 10));

if (groupids.length < 1) {
context.dispatch('LOAD_EDITORS_LIST_SUCCESS', payload.users);
return done();
}

context.service.read('usergroup.getList', {groupids}, { timeout: 20 * 1000 }, (err, res) => { // payload needs groupids
if (err) {
context.dispatch('LOAD_EDITORS_LIST_ERROR', err);
}
else {
let users = payload.users;
users = users.concat(res.reduce((ret, group) => {
ret.push(group.creator);
return ret.concat(group.members);
}, []));

// purge duplicates and current user
users = users.reduce((ret, user) => {
let found = ret.find((u) => ((u.id || u.userid) === (user.id || user.userid)));
if (!found && (user.id || user.userid) !== userid) {
ret.push(user);
}
return ret;
}, []);

// console.log('got all editors', users);
context.dispatch('LOAD_EDITORS_LIST_SUCCESS', users);
}
done();
});
}
20 changes: 20 additions & 0 deletions actions/deckedit/transferOwnership.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import log from '../log/clog';

import UserProfileStore from '../../stores/UserProfileStore';

export default function transferOwnership(context, payload, done) {
log.info(context);
context.dispatch('TRY_TRANSFER_OWNERSHIP');

payload.jwt = context.getStore(UserProfileStore).jwt;

context.service.update('deck.transferOwnership', payload, { timeout: 20 * 1000 }, (err, res) => {
if (err) {
context.dispatch('TRANSFER_OWNERSHIP_ERROR', err);
}
else {
context.dispatch('TRANSFER_OWNERSHIP_SUCCESS', res);
}
done();
});
}
3 changes: 2 additions & 1 deletion actions/decktree/deleteTreeNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import serviceUnavailable from '../error/serviceUnavailable';
export default function deleteTreeNode(context, payload, done) {
log.info(context);
let userid = context.getStore(UserProfileStore).userid;

if (userid != null && userid !== '') {
//enrich with jwt
payload.jwt = context.getStore(UserProfileStore).jwt;
Expand Down Expand Up @@ -51,7 +52,7 @@ export default function deleteTreeNode(context, payload, done) {
if (!isEmpty(contentRootId)) {
activity.content_root_id = contentRootId;
}

context.executeAction(addActivity, {activity: activity});
}
done(null, res);
Expand Down
82 changes: 47 additions & 35 deletions actions/decktree/deleteTreeNodeAndNavigate.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,65 @@
import async from 'async';
import DeckTreeStore from '../../stores/DeckTreeStore';
import deleteTreeNode from './deleteTreeNode';
const log = require('../log/clog');
import log from '../log/clog';
import {navigateAction} from 'fluxible-router';
import Util from '../../components/common/Util';
import serviceUnavailable from '../error/serviceUnavailable';

export default function deleteTreeNodeAndNavigate(context, payload, done) {
log.info(context);

let callback = (accepted) => {
//load all required actions in parallel
async.parallel([
(callback) => {
context.executeAction(deleteTreeNode, payload, callback);
}
],
// final callback
(err, results) => {
if (!err) {
//the logic for retrieving the parent node is handles in the stores
//therefore, we need to get access to the selector from the store
let currentState = context.getStore(DeckTreeStore).getState();
let selector = {
id: currentState.selector.get('id'),
stype: currentState.selector.get('stype'),
sid: currentState.selector.get('sid'),
spath: currentState.selector.get('spath')
};
context.executeAction(navigateAction, {
url: Util.makeNodeURL(selector, 'deck', 'view', undefined, undefined, true)
});
} else {
log.error(context, {
filepath: __filename
});
//context.executeAction(serviceUnavailable, payload, done);
}
done();
});
};

// skip swal as a previous modal already got approval
console.log(payload);
if (payload.confirmed) {
return callback(true);
}

let elementTitle = payload.stype;
if(elementTitle === 'deck')
elementTitle = 'sub' + elementTitle;
let elementTitle = payload.stype, html;
if (elementTitle === 'deck') {
elementTitle = 'subdeck';
html = 'This subdeck will become a proper deck after removing it. It will be available in its creator\'s "My Decks" page.';
}
swal({
title: 'Delete '+elementTitle+'. Are you sure?',
title: 'Remove ' + elementTitle + '. Are you sure?',
html,
type: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Yes, delete it!'
confirmButtonText: 'Yes, remove it!'

}).then((accepted) => {
//load all required actions in parallel
async.parallel([
(callback) => {
context.executeAction(deleteTreeNode, payload, callback);
}
],
// final callback
(err, results) => {
if (!err) {
//the logic for retrieving the parent node is handles in the stores
//therefore, we need to get access to the selector from the store
let currentState = context.getStore(DeckTreeStore).getState();
let selector = {
id: currentState.selector.get('id'),
stype: currentState.selector.get('stype'),
sid: currentState.selector.get('sid'),
spath: currentState.selector.get('spath')
};
context.executeAction(navigateAction, {
url: Util.makeNodeURL(selector, 'deck', 'view', undefined, undefined, true)
});
} else {
log.error(context, {filepath: __filename});
//context.executeAction(serviceUnavailable, payload, done);
}
done();
});
}, (reason) => {/*do nothing*/}).catch(swal.noop);
}).then(callback, (reason) => { /*do nothing*/ }).catch(swal.noop);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import DeckTranslationsModal from '../Translation/DeckTranslationsModal';
import SlideTranslationsModal from '../Translation/SlideTranslationsModal';
import addDeckTranslation from '../../../../actions/translation/addDeckTranslation';
import addSlideTranslation from '../../../../actions/translation/addSlideTranslation';
import DeckViewStore from '../../../../stores/DeckViewStore';

class ContentActionsHeader extends React.Component {
constructor(props){
Expand Down Expand Up @@ -66,6 +67,10 @@ class ContentActionsHeader extends React.Component {
id: 'ContentActionsHeader.deleteAriaText',
defaultMessage:'Delete slide'
},
deleteDeckAriaText:{
id: 'ContentActionsHeader.deleteDeckAriaText',
defaultMessage:'Delete deck'
},
language:{
id: 'ContentActionsHeader.language',
defaultMessage:'Language'
Expand Down Expand Up @@ -123,6 +128,58 @@ class ContentActionsHeader extends React.Component {
this.context.executeAction(deleteTreeNodeAndNavigate, selector);
}

// TODO Remove this unused code once it is decided we don't ever need to provide this option
handleDeleteNodeWithCheck(selector) {
// plain remove for slides
if (selector.stype !== 'deck') {
return this.context.executeAction(deleteTreeNodeAndNavigate, selector);
}

// plain remove for decks with subdecks
if (this.props.DeckViewStore.deckData.contentItems.find((i) => i.kind === 'deck')) {
return this.context.executeAction(deleteTreeNodeAndNavigate, selector);
}

// plain remove for shared subdecks
if (this.props.DeckViewStore.deckData.usage.length > 0) {
// TODO make this test more strict: check for actual ids in usage matching current deck parent
const otherParents = this.props.DeckViewStore.deckData.usage;
if (otherParents.length > 1) {
return this.context.executeAction(deleteTreeNodeAndNavigate, selector);
}
}

swal({
title: 'Remove subdeck',
html: 'You have the option to simply remove this subdeck and keep it in "My Decks" or delete this subdeck completely.',
type: 'question',
showCancelButton: true,
confirmButtonText: 'Remove',
confirmButtonClass: 'ui button',
cancelButtonText: 'Delete',
cancelButtonClass: 'negative ui button',
allowEscapeKey: true,
allowOutsideClick: true,
buttonsStyling: false
})
.then((result) => {
// confirm btn
// remove deck as node from the parent deck
selector.confirmed = true;
selector.purge = false;
this.context.executeAction(deleteTreeNodeAndNavigate, selector);
}, (action) => {
if (action === 'cancel') {
selector.confirmed = true;
selector.purge = true;
this.context.executeAction(deleteTreeNodeAndNavigate, selector);
}
})
.catch((e) => {
console.log(e);
});
}

handleSaveButtonClick(){
this.context.executeAction(saveClick, {});
}
Expand Down Expand Up @@ -431,7 +488,7 @@ class ContentActionsHeader extends React.Component {
<button className={deleteItemClass} onClick={this.handleDeleteNode.bind(this, selector)}
type="button" key="deleteItem"
aria-label={this.context.intl.formatMessage(this.messages.deleteAriaText)}
data-tooltip={this.context.intl.formatMessage(this.messages.deleteAriaText)}
data-tooltip={this.context.intl.formatMessage((contentDetails.selector.stype==='deck') ? this.messages.deleteDeckAriaText : this.messages.deleteAriaText)}
tabIndex={contentDetails.selector.id === contentDetails.selector.sid || this.props.PermissionsStore.permissions.readOnly || !this.props.PermissionsStore.permissions.edit || contentDetails.mode ==='edit' || contentDetails.mode ==='markdownEdit' ?-1:0}
disabled={deleteItemDisabled}>
<i className="large icons">
Expand Down Expand Up @@ -472,13 +529,14 @@ ContentActionsHeader.contextTypes = {
intl: PropTypes.object.isRequired
};
//it should listen to decktree store in order to handle adding slides/decks
ContentActionsHeader = connectToStores(ContentActionsHeader, [DeckTreeStore, UserProfileStore, PermissionsStore, ContentStore, TranslationStore], (context, props) => {
ContentActionsHeader = connectToStores(ContentActionsHeader, [DeckTreeStore, UserProfileStore, PermissionsStore, ContentStore, TranslationStore, DeckViewStore], (context, props) => {
return {
DeckTreeStore: context.getStore(DeckTreeStore).getState(),
UserProfileStore: context.getStore(UserProfileStore).getState(),
PermissionsStore: context.getStore(PermissionsStore).getState(),
ContentStore: context.getStore(ContentStore).getState(),
TranslationStore: context.getStore(TranslationStore).getState()
TranslationStore: context.getStore(TranslationStore).getState(),
DeckViewStore: context.getStore(DeckViewStore).getState()
};
});
export default ContentActionsHeader;
Loading