Merge branch 'pu/2013.10-groupimport'
[tine20] / tine20 / Calendar / js / ImportDialog.js
1 /*
2  * Tine 2.0
3  * 
4  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
5  * @author      Michael Spahn <m.spahn@metaways.de>
6  * @copyright   Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
7  */
8 Ext.ns('Tine.Calendar');
9
10 /**
11  * Simple Import Dialog
12  *
13  * @namespace   Tine.widgets.dialog
14  * @class       Tine.widgets.dialog.ImportDialog
15  * @extends     Tine.widgets.dialog.EditDialog
16  * @constructor
17  * @param       {Object} config The configuration options.
18  * 
19  * TODO add app grid to show results when dry run is selected
20  */
21 Tine.Calendar.ImportDialog = Ext.extend(Tine.widgets.dialog.ImportDialog, {
22     
23     appName: 'Calendar',
24     modelName: 'Event',
25     
26     /**
27      * init import wizard
28      */
29     initComponent: function() {
30         Tine.log.debug('Tine.Calendar.ImportDialog::initComponent');
31         Tine.log.debug(this);
32
33         Tine.Calendar.ImportDialog.superclass.initComponent.call(this);
34     },
35
36     getItems: function() {
37         return [this.getPanel()];
38     },
39     
40     /**
41      * do import request
42      * 
43      * @param {Function} callback
44      * @param {Object}   importOptions
45      */
46     doImport: function(callback, importOptions, clientRecordData) {
47         var targetContainer = this.containerField.getValue() || this.containerCombo.getValue();
48         var type = this.typeCombo.getValue();
49         
50         var params = {
51             importOptions: Ext.apply({
52                 container_id: targetContainer
53             }, importOptions || {})
54         };
55         
56         if (type == 'upload') {
57             params = Ext.apply(params, {
58                 clientRecordData: clientRecordData,
59                 method: this.appName + '.import' + this.recordClass.getMeta('modelName')  + 's',
60                 tempFileId: this.uploadButton.getTempFileId(),
61                 definitionId: this.definitionCombo.getValue()
62             });
63         } else {
64             params = Ext.apply(params, {
65                 method: this.appName + '.importRemote' + this.recordClass.getMeta('modelName')  + 's',
66                 remoteUrl: this.remoteLocation.getValue(),
67                 interval: this.ttlCombo.getValue()
68             });
69         }
70        
71         Ext.Ajax.request({
72             scope: this,
73             timeout: 1800000, // 30 minutes
74             callback: this.onImportResponse.createDelegate(this, [callback], true),
75             params: params
76         });
77     },
78     
79     /**
80      * called when import request sends response
81      * 
82      * @param {Object}   request
83      * @param {Boolean}  success
84      * @param {Object}   response
85      * @param {Function} callback
86      */
87     onImportResponse: function(request, success, response, callback) {
88         var decoded = Ext.util.JSON.decode(response.responseText);
89         
90         Tine.log.debug('Tine.widgets.dialog.SimpleImportDialog::onImportResponse server response');
91         Tine.log.debug(decoded);
92         
93         this.lastImportResponse = decoded;
94
95         var that = this;
96         
97         if (success) {
98             Ext.MessageBox.show({
99                 buttons: Ext.Msg.OK,
100                 icon: Ext.MessageBox.INFO,
101                 fn: callback,
102                 scope: that,
103                 title: that.app.i18n._('Import Definition Success!'),
104                 msg: that.app.i18n._('The Ical Import definition has been created successfully! Please wait some minutes to get the events synced.')
105             });
106         } else {
107             Tine.Tinebase.ExceptionHandler.handleRequestException(response, callback, that);
108         }
109     },
110     
111     /**
112      * Returns a panel with a upload field and descriptions
113      * 
114      * @returns {Object}
115      */
116     getUploadPanel: function () {
117         return {
118             xtype: 'panel',
119             baseCls: 'ux-subformpanel',
120             id: 'uploadPanel',
121             hidden: true,
122             title: _('Choose Import File'),
123             height: 100,
124             items: [{
125                 xtype: 'label',
126                 html: '<p>' + _('Please choose the file that contains the records you want to add to Tine 2.0').replace(/Tine 2\.0/g, Tine.title) + '</p><br />'
127             }, {
128                 xtype: 'tw.uploadbutton',
129                 ref: '../../uploadButton',
130                 text: String.format(_('Select file containing your {0}'), this.recordClass.getRecordsName()),
131                 handler: this.onFileReady,
132                 allowedTypes: this.allowedFileExtensions,
133                 scope: this
134             }]
135         };
136     },
137     
138     /**
139      * Returns a panel with a text field for a remote location and a description
140      * 
141      * @returns {Object}
142      */    
143     getRemotePanel: function () {
144         var ttl = [
145             ['once', this.app.i18n._('once')],
146             ['hourly', this.app.i18n._('hourly')],
147             ['daily', this.app.i18n._('daily')],
148             ['weekly', this.app.i18n._('weekly')]
149         ];
150
151         var ttlStore = new Ext.data.ArrayStore({
152             fields: ['ttl_id', 'ttl'],
153             data: ttl
154         });
155         
156         return {
157             xtype: 'panel',
158             baseCls: 'ux-subformpanel',
159             id: 'remotePanel',
160             hidden: false,
161             title: this.app.i18n._('Choose Remote Location'),
162             height: 150,
163             items: [{
164                 xtype: 'label',
165                 html: '<p>' + this.app.i18n._('Please choose a remote location you want to add to Tine 2.0').replace(/Tine 2\.0/g, Tine.title) + '</p><br />'
166             }, {
167                 ref: '../../remoteLocation',
168                 xtype: 'textfield',
169                 scope: this,
170                 enableKeyEvents: true,
171                 width: 400,
172                 listeners: {
173                     scope: this,
174                     keyup: function() {
175                         this.manageButtons();
176                     }
177                 }
178             }, {
179                 xtype: 'label',
180                 html: '<p><br />' + this.app.i18n._('Refresh time').replace(/Tine 2\.0/g, Tine.title) + '</p><br />'
181             }, {
182                 xtype: 'combo',
183                 mode: 'local',
184                 ref: '../../ttlCombo',
185                 value: 'once',
186                 scope: this,
187                 width: 400,
188                 listeners: {
189                     scope: this,
190                     'select': function() {
191                         this.manageButtons();
192                     }
193                 },
194                 editable: false,
195                 allowblank: false,
196                 valueField: 'ttl_id',
197                 displayField: 'ttl',
198                 store: ttlStore
199             }]
200         };
201     },
202     
203     getImportOptionsPanel: function () {
204         if (this.importOptionsPanel) {
205             return this.importOptionsPanel;
206         }
207         
208         return {
209             xtype: 'panel',
210             ref: '../../importOptionsPanel',
211             baseCls: 'ux-subformpanel',
212             title: this.app.i18n._('General Settings'),
213             height: 100,
214             width: 400,
215             items: [{
216                 xtype: 'label',
217                 html: '<p>' + this.app.i18n._('Container name / New or existing if it already exists you need permissions to add to.') + '<br /><br /></p>'
218             }, {
219                 xtype: 'panel',
220                 heigth: 150,
221                 layout: 'hbox',
222                 items: [{
223                     id: this.app.appName + 'ContainerName',
224                     xtype: 'textfield',
225                     ref: '../../../containerField',
226                     disabled: true,
227                     enableKeyEvents: true,
228                     listeners: {
229                         scope: this,
230                         keyup: function() {
231                             this.manageButtons();
232                         }
233                     },
234                     flex: 1
235                 }, {
236                     xtype: 'label',
237                     html: ' - ' + this.app.i18n._('or') + ' - ',
238                     style: {
239                         'text-align': 'center'
240                     },
241                     width: 40
242                 }, {
243                     xtype: 'panel',
244                     flex: 1,
245                     height: 20,
246                     width: 200,
247                     items: [new Tine.widgets.container.selectionComboBox({
248                         id: this.app.appName + 'EditDialogContainerSelector',
249                         ref: '../../../../containerCombo',
250                         stateful: false,
251                         containerName: this.recordClass.getContainerName(),
252                         containersName: this.recordClass.getContainersName(),
253                         appName: this.appName,
254                         value: this.defaultImportContainer,
255                         requiredGrant: false
256                     })]
257                 }]
258             }]
259         };
260     },
261     
262     getDefinitionPanel: function () {
263         if (this.definitionPanel) {
264             return this.definitionPanel;
265         }
266         
267         var def = this.selectedDefinition,
268             description = def ? def.get('description') : '',
269             options = def ? def.get('plugin_options') : null,
270             example = options && options.example ? options.example : '';
271     
272         return {
273             xtype: 'panel',
274             ref: '../../definitionPanel',
275             id: 'definitionPanel',
276             hidden: true,
277             baseCls: 'ux-subformpanel',
278             title: this.app.i18n._('What should the file you upload look like?'),
279             flex: 1,
280             items: [
281             {
282                 xtype: 'label',
283                 html: '<p>' + this.app.i18n._('Tine 2.0 does not understand all kind of files you might want to upload. You will have to manually adjust your file so Tine 2.0 can handle it.').replace(/Tine 2\.0/g, Tine.title) + '</p><br />'
284             }, {
285 //                xtype: 'label',
286 //                html: '<p>' + this.app.i18n._('Following you find a list of all supported import formats and a sample file, how Tine 2.0 expects your file to look like.').replace(/Tine 2\.0/g, Tine.title) + '</p><br />'
287 //            }, {
288                 xtype: 'label',
289                 html: '<p>' + this.app.i18n._('Please select the import format of the file you want to upload').replace(/Tine 2\.0/g, Tine.title) + '<br /><br /></p>'
290             }, {
291                 xtype: 'combo',
292                 ref: '../../definitionCombo',
293                 store: this.definitionsStore,
294                 displayField:'label',
295                 valueField:'id',
296                 mode: 'local',
297                 triggerAction: 'all',
298                 editable: false,
299                 allowBlank: false,
300                 forceSelection: true,
301                 width: 400,
302                 value: this.selectedDefinition ? this.selectedDefinition.id : null,
303                 listeners: {
304                     scope: this,
305                     'select': this.onDefinitionSelect
306                 }
307             }, {
308                 xtype: 'label',
309                 ref: '../../exampleLink',
310                 html: example ? ('<p><a href="' + example + '">' + this.app.i18n._('Download example file') + '</a></p>') : '<p>&nbsp;</p>'
311             }, {
312                 xtype: 'displayfield',
313                 ref: '../../definitionDescription',
314                 height: 70,
315                 value: description,
316                 cls: 'x-ux-display-background-border',
317                 style: 'padding-left: 5px;'
318             }]
319         };
320     },
321     
322     /**
323      * returns the file panel of this wizard (step 1)
324      */
325     getPanel: function() {
326         var def = this.selectedDefinition,
327             description = def ? def.get('description') : '',
328             options = def ? def.get('plugin_options') : null,
329             example = options && options.example ? options.example : '';
330         
331         var types = [
332             ['remote_ics', this.app.i18n._('Remote / ICS')],
333 //            ['remote_caldav', _('Remote / CALDav')],
334             ['upload', this.app.i18n._('Upload')]
335         ]
336         
337         var typeStore = new Ext.data.ArrayStore({
338             fields: [
339                 'type_id',
340                 'type_value'
341             ],
342             data: types,
343             disabled: false
344         });
345
346         return {
347             title: this.app.i18n._('Choose File and Format'),
348             layout: 'vbox',
349             border: false,
350             xtype: 'ux.displaypanel',
351             frame: true,
352             ref: '../filePanel',
353             items: [{
354                 xtype: 'panel',
355                 baseCls: 'ux-subformpanel',
356                 title: this.app.i18n._('Select type of source'),
357                 height: 100,
358                 items: [{
359                         xtype: 'label',
360                         html: '<p>' + this.app.i18n._('Please select the type of source you want to add to Tine 2.0') + '</p><br />'
361                 }, {
362                     xtype: 'combo',
363                     mode: 'local',
364                     ref: '../../typeCombo',
365                     width: 400,
366                     listeners:{
367                         scope: this,
368                         'select': function (combo) {
369                             if (combo.getValue() == 'upload') {
370                                 Ext.getCmp('uploadPanel').show();
371                                 Ext.getCmp('definitionPanel').show();
372                                 Ext.getCmp('remotePanel').hide();
373                             } else if (combo.getValue() == 'remote_ics' || combo.getValue() == 'remote_caldav') {
374                                 Ext.getCmp('uploadPanel').hide();
375                                 Ext.getCmp('definitionPanel').hide();
376                                 Ext.getCmp('remotePanel').show();
377                             }
378                             
379                             this.doLayout();
380                             this.manageButtons();
381                         },
382                         'render': function (combo) {
383                             /**
384                             * @todo enable and allow remotes
385                             *  ~mspahn
386                             */
387                             combo.setValue('upload');
388                             Ext.getCmp('uploadPanel').show();
389                             Ext.getCmp('definitionPanel').show();
390                             Ext.getCmp('remotePanel').hide();
391                         }
392                     },
393                     scope: this,
394                     valueField: 'type_id',
395                     displayField: 'type_value',
396                     store: typeStore
397                 }]
398             },
399             this.getUploadPanel(),
400             this.getRemotePanel(),
401             this.getImportOptionsPanel(),
402             this.getDefinitionPanel()],
403
404             /**
405             * finish button handler for this panel
406             */
407             onFinishButton: (function() {
408                 if (! this.importMask) {
409                     this.importMask = new Ext.LoadMask(this.getEl(), {msg: String.format(_('Importing {0}'), this.recordClass.getRecordsName())});
410                 }
411                 this.importMask.show();
412
413                 // collect client data
414                 var clientRecordData = [];
415                 var importOptions = {};
416
417                 this.doImport(function(request, success, response) {
418                     this.importMask.hide();
419
420                     this.fireEvent('finish', this, this.layout.activeItem);
421
422                     if (Ext.isArray(response.exceptions) && response.exceptions.length > 0) {
423                         this.backButton.setDisabled(true);
424                         this.finishButton.setHandler(function() {this.window.close()}, this);
425                     } else {
426                         this.window.close();
427                     }
428                 }, importOptions, clientRecordData);
429             }).createDelegate(this),
430
431             /**
432              * @todo fix later
433              */
434             finishIsAllowed: (function() {
435                 return (
436                     ((this.typeCombo && (this.typeCombo.getValue() == 'remote_ics' || this.typeCombo.getValue() == 'remote_caldav'))
437                     && (this.remoteLocation && this.remoteLocation.getValue())
438                     && (this.ttlCombo && (this.ttlCombo.getValue() || this.ttlCombo.getValue() === 0))))
439                     || ((this.typeCombo && (this.typeCombo.getValue() == 'upload'))
440                     && (this.definitionCombo && this.definitionCombo.getValue())
441                     && (this.uploadButton && this.uploadButton.upload))
442                     && ((this.containerField && this.containerField.getValue()) || (this.containerCombo && this.containerCombo.getValue())
443                 );
444
445             }).createDelegate(this)
446         };
447     }
448 });
449
450 /**
451  * Create new import window
452  */
453 Tine.Calendar.ImportDialog.openWindow = function (config) {
454     var window = Tine.WindowFactory.getWindow({
455         width: 800,
456         height: 600,
457         name: Tine.Calendar.ImportDialog.windowNamePrefix + Ext.id(),
458         contentPanelConstructor: 'Tine.Calendar.ImportDialog',
459         contentPanelConstructorConfig: config
460     });
461     return window;
462 };