@ -1,68 +1,46 @@
/// Abstract root for all import popup screens.
/// Descendants must define:
/// - getMethodName(): return the Meteor method to call for import, passing json
/// data decoded as object and additional data (see below);
/// - getAdditionalData(): return object containing additional data passed to
/// Meteor method (like list ID and position for a card import);
/// - getLabel(): i18n key for the text displayed in the popup, usually to
/// explain how to get the data out of the source system.
const ImportPopup = BlazeComponent . extendComponent ( {
jsonText ( ) {
return Session . get ( 'import.text' ) ;
} ,
membersMapping ( ) {
return Session . get ( 'import.membersToMap' ) ;
} ,
BlazeComponent . extendComponent ( {
onCreated ( ) {
this . error = new ReactiveVar ( '' ) ;
this . dataToImport = '' ;
this . steps = [ 'importTextarea' , 'importMapMembers' ] ;
this . _currentStepIndex = new ReactiveVar ( 0 ) ;
this . importedData = new ReactiveVar ( ) ;
this . membersToMap = new ReactiveVar ( [ ] ) ;
} ,
onFinish ( ) {
Popup . close ( ) ;
currentTemplate ( ) {
return this . steps [ this . _currentStepIndex . get ( ) ] ;
} ,
onShowMapping ( evt ) {
this . _storeText ( evt ) ;
Popup . open ( 'mapMembers' ) ( evt ) ;
nextStep ( ) {
const nextStepIndex = this . _currentStepIndex . get ( ) + 1 ;
if ( nextStepIndex >= this . steps . length ) {
this . finishImport ( ) ;
} else {
this . _currentStepIndex . set ( nextStepIndex ) ;
}
} ,
onSubmit ( evt ) {
importData ( evt ) {
evt . preventDefault ( ) ;
const dataJson = this . _storeText ( evt ) ;
let dataObject ;
const dataJson = this . find ( '.js-import-json' ) . value ;
try {
dataObject = JSON . parse ( dataJson ) ;
const dataObject = JSON . parse ( dataJson ) ;
this . setError ( '' ) ;
this . importedData . set ( dataObject ) ;
this . _prepareAdditionalData ( dataObject ) ;
this . nextStep ( ) ;
} catch ( e ) {
this . setError ( 'error-json-malformed' ) ;
return ;
}
if ( this . _hasAllNeededData ( dataObject ) ) {
this . _import ( dataObject ) ;
} else {
this . _prepareAdditionalData ( dataObject ) ;
Popup . open ( this . _screenAdditionalData ( ) ) ( evt ) ;
}
} ,
events ( ) {
return [ {
submit : this . onSubmit ,
'click .show-mapping' : this . onShowMapping ,
} ] ;
} ,
setError ( error ) {
this . error . set ( error ) ;
} ,
_import ( dataObject ) {
const additionalData = this . getAdditionalData ( ) ;
const membersMapping = this . membersMapping ( ) ;
finishImport ( ) {
const additionalData = { } ;
const membersMapping = this . membersToMap . get ( ) ;
if ( membersMapping ) {
const mappingById = { } ;
membersMapping . forEach ( ( member ) => {
@ -72,99 +50,75 @@ const ImportPopup = BlazeComponent.extendComponent({
} ) ;
additionalData . membersMapping = mappingById ;
}
Session . set ( 'import.membersToMap' , null ) ;
Session . set ( 'import.text' , null ) ;
Meteor . call ( this . getMethodName ( ) , dataObject , additionalData ,
( error , response ) => {
if ( error ) {
this . setError ( error . error ) ;
this . membersToMap . set ( [ ] ) ;
Meteor . call ( 'importTrelloBoard' , this . importedData . get ( ) , additionalData ,
( err , res ) => {
if ( err ) {
this . setError ( err . error ) ;
} else {
// ensure will display what we just imported
Filter . addException ( response ) ;
this . onFinish ( response ) ;
Utils . goBoardId ( res ) ;
}
}
) ;
} ,
_hasAllNeededData ( dataObject ) {
// import has no members or they are already mapped
return dataObject . members . length === 0 || this . membersMapping ( ) ;
} ,
_prepareAdditionalData ( dataObject ) {
// we will work on the list itself (an ordered array of objects)
// when a mapping is done, we add a 'wekan' field to the object representing the imported member
// we will work on the list itself (an ordered array of objects) when a
// mapping is done, we add a 'wekan' field to the object representing the
// imported member
const membersToMap = dataObject . members ;
// auto-map based on username
membersToMap . forEach ( ( importedMember ) => {
const wekanUser = Users . findOne ( { username : importedMember . username } ) ;
if ( wekanUser ) {
const wekanUser = Users . findOne ( { username : importedMember . username } ) ;
if ( wekanUser ) {
importedMember . wekan = wekanUser ;
}
} ) ;
// store members data and mapping in Session
// (we go deep and 2-way, so storing in data context is not a viable option)
Session . set ( 'import.membersToMap' , membersToMap ) ;
this . membersToMap . set ( membersToMap ) ;
return membersToMap ;
} ,
_screenAdditionalData ( ) {
return 'mapMembers' ;
} ,
} ) . register ( 'import' ) ;
_storeText ( ) {
const dataJson = this . $ ( '.js-import-json' ) . val ( ) ;
Session . set ( 'import.text' , dataJson ) ;
return dataJson ;
} ,
} ) ;
ImportPopup . extendComponent ( {
getAdditionalData ( ) {
const listId = this . currentData ( ) . _id ;
const selector = ` #js-list- ${ this . currentData ( ) . _id } .js-minicard:first ` ;
const firstCardDom = $ ( selector ) . get ( 0 ) ;
const sortIndex = Utils . calculateIndex ( null , firstCardDom ) . base ;
const result = { listId , sortIndex } ;
return result ;
} ,
getMethodName ( ) {
return 'importTrelloCard' ;
BlazeComponent . extendComponent ( {
template ( ) {
return 'importTextarea' ;
} ,
getLabel ( ) {
return 'import-card-trello-instruction' ;
} ,
} ) . register ( 'listImportCardPopup' ) ;
ImportPopup . extendComponent ( {
getAdditionalData ( ) {
const result = { } ;
return result ;
} ,
getMethodName ( ) {
return 'importTrelloBoard' ;
} ,
getLabel ( ) {
return 'import-board-trello-instruction' ;
events ( ) {
return [ {
submit ( evt ) {
return this . parentComponent ( ) . importData ( evt ) ;
} ,
} ] ;
} ,
} ) . register ( 'importTextarea' ) ;
onFinish ( response ) {
Utils . goBoardId ( response ) ;
BlazeComponent . extendComponent ( {
onCreated ( ) {
this . autorun ( ( ) => {
this . parentComponent ( ) . membersToMap . get ( ) . forEach ( ( { wekan } ) => {
if ( wekan !== undefined ) {
const userId = wekan . _id ;
this . subscribe ( 'user-miniprofile' , userId ) ;
}
} ) ;
} ) ;
} ,
} ) . register ( 'boardImportBoardPopup' ) ;
const ImportMapMembers = BlazeComponent . extendComponent ( {
members ( ) {
return Session . get ( 'import.membersToMap' ) ;
return this . parentComponent ( ) . membersToMap . get ( ) ;
} ,
_refreshMembers ( listOfMembers ) {
Session . set ( 'import.membersToMap' , listOfMembers ) ;
return this . parentComponent ( ) . membersToMap . set ( listOfMembers ) ;
} ,
/ * *
* Will look into the list of members to import for the specified memberId ,
* then set its property to the supplied value .
@ -202,15 +156,17 @@ const ImportMapMembers = BlazeComponent.extendComponent({
// Session.get gives us a copy, we have to set it back so it sticks
this . _refreshMembers ( listOfMembers ) ;
} ,
setSelectedMember ( memberId ) {
return this . _setPropertyForMember ( 'selected' , true , memberId , true ) ;
} ,
/ * *
* returns the member with specified id ,
* or the selected member if memberId is not specified
* /
getMember ( memberId = null ) {
const allMembers = Session . get ( 'import.membersToMap' ) ;
const allMembers = this . members ( ) ;
let finder = null ;
if ( memberId ) {
finder = ( user ) => user . id === memberId ;
@ -219,15 +175,20 @@ const ImportMapMembers = BlazeComponent.extendComponent({
}
return allMembers . find ( finder ) ;
} ,
mapSelectedMember ( wekan ) {
return this . _setPropertyForMember ( 'wekan' , wekan , null ) ;
} ,
unmapMember ( memberId ) {
return this . _setPropertyForMember ( 'wekan' , null , memberId ) ;
} ,
} ) ;
ImportMapMembers . extendComponent ( {
onSubmit ( evt ) {
evt . preventDefault ( ) ;
this . parentComponent ( ) . nextStep ( ) ;
} ,
onMapMember ( evt ) {
const memberToMap = this . currentData ( ) ;
if ( memberToMap . wekan ) {
@ -235,33 +196,31 @@ ImportMapMembers.extendComponent({
this . unmapMember ( memberToMap . id ) ;
} else {
this . setSelectedMember ( memberToMap . id ) ;
Popup . open ( 'mapMembersAdd' ) ( evt ) ;
Popup . open ( 'i mportM apMembersAdd' ) ( evt ) ;
}
} ,
onSubmit ( evt ) {
evt . preventDefault ( ) ;
Popup . back ( ) ;
} ,
events ( ) {
return [ {
'submit' : this . onSubmit ,
'click .mapping ' : this . onMapMember ,
'click .js-select-member ' : this . onMapMember ,
} ] ;
} ,
} ) . register ( 'mapMembersPopup' ) ;
} ) . register ( 'importMapMembers' ) ;
BlazeComponent . extendComponent ( {
onRendered ( ) {
this . find ( '.js-map-member input' ) . focus ( ) ;
} ,
ImportMapMembers . extendComponent ( {
onSelectUser ( ) {
this . mapSelectedMember ( this . currentData ( ) ) ;
Popup . getOpenerComponent ( ) . mapSelectedMember ( this . currentData ( ) ) ;
Popup . back ( ) ;
} ,
events ( ) {
return [ {
'click .js-select-import' : this . onSelectUser ,
} ] ;
} ,
onRendered ( ) {
// todo XXX why do I not get the focus??
this . find ( '.js-map-member input' ) . focus ( ) ;
} ,
} ) . register ( 'mapMembersAddPopup' ) ;
} ) . register ( 'importMapMembersAddPopup' ) ;