Merge branch 'pu/2013.10-icsimport'
[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             
107             var wp = this.app.mainScreen.getWestPanel(),
108                 tp = wp.getContainerTreePanel(),
109                 state = wp.getState();
110                 
111             tp.getLoader().load(tp.getRootNode());
112             wp.applyState(state);
113             
114         } else {
115             Tine.Tinebase.ExceptionHandler.handleRequestException(response, callback, that);
116         }
117     },
118     
119     /**
120      * Returns a panel with a upload field and descriptions
121      * 
122      * @returns {Object}
123      */
124     getUploadPanel: function () {
125         return {
126             xtype: 'panel',
127             baseCls: 'ux-subformpanel',
128             id: 'uploadPanel',
129             hidden: true,
130             title: _('Choose Import File'),
131             height: 100,
132             items: [{
133                 xtype: 'label',
134                 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 />'
135             }, {
136                 xtype: 'tw.uploadbutton',
137                 ref: '../../uploadButton',
138                 text: String.format(_('Select file containing your {0}'), this.recordClass.getRecordsName()),
139                 handler: this.onFileReady,
140                 allowedTypes: this.allowedFileExtensions,
141                 scope: this
142             }]
143         };
144     },
145     
146     /**
147      * Returns a panel with a text field for a remote location and a description
148      * 
149      * @returns {Object}
150      */    
151     getRemotePanel: function () {
152         var ttl = [
153             ['once', this.app.i18n._('once')],
154             ['hourly', this.app.i18n._('hourly')],
155             ['daily', this.app.i18n._('daily')],
156             ['weekly', this.app.i18n._('weekly')]
157         ];
158
159         var ttlStore = new Ext.data.ArrayStore({
160             fields: ['ttl_id', 'ttl'],
161             data: ttl
162         });
163         
164         return {
165             xtype: 'panel',
166             baseCls: 'ux-subformpanel',
167             id: 'remotePanel',
168             hidden: false,
169             title: this.app.i18n._('Choose Remote Location'),
170             height: 150,
171             items: [{
172                 xtype: 'label',
173                 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 />'
174             }, {
175                 ref: '../../remoteLocation',
176                 xtype: 'textfield',
177                 scope: this,
178                 enableKeyEvents: true,
179                 width: 400,
180                 listeners: {
181                     scope: this,
182                     keyup: function() {
183                         this.manageButtons();
184                     }
185                 }
186             }, {
187                 xtype: 'label',
188                 html: '<p><br />' + this.app.i18n._('Refresh time').replace(/Tine 2\.0/g, Tine.title) + '</p><br />'
189             }, {
190                 xtype: 'combo',
191                 mode: 'local',
192                 ref: '../../ttlCombo',
193                 value: 'once',
194                 scope: this,
195                 width: 400,
196                 listeners: {
197                     scope: this,
198                     'select': function() {
199                         this.manageButtons();
200                     }
201                 },
202                 editable: false,
203                 allowblank: false,
204                 valueField: 'ttl_id',
205                 displayField: 'ttl',
206                 store: ttlStore
207             }]
208         };
209     },
210     
211     getImportOptionsPanel: function () {
212         if (this.importOptionsPanel) {
213             return this.importOptionsPanel;
214         }
215         
216         return {
217             xtype: 'panel',
218             ref: '../../importOptionsPanel',
219             baseCls: 'ux-subformpanel',
220             title: this.app.i18n._('General Settings'),
221             height: 100,
222             width: 400,
223             items: [{
224                 xtype: 'label',
225                 html: '<p>' + this.app.i18n._('Container name / New or existing if it already exists you need permissions to add to.') + '<br /><br /></p>'
226             }, {
227                 xtype: 'panel',
228                 heigth: 150,
229                 layout: 'hbox',
230                 items: [{
231                     id: this.app.appName + 'ContainerName',
232                     xtype: 'textfield',
233                     ref: '../../../containerField',
234                     disabled: false,
235                     enableKeyEvents: true,
236                     listeners: {
237                         scope: this,
238                         keyup: function() {
239                             this.manageButtons();
240                         }
241                     },
242                     flex: 1
243                 }, {
244                     xtype: 'label',
245                     html: ' - ' + this.app.i18n._('or') + ' - ',
246                     style: {
247                         'text-align': 'center'
248                     },
249                     width: 40
250                 }, {
251                     xtype: 'panel',
252                     flex: 1,
253                     height: 20,
254                     width: 200,
255                     items: [new Tine.widgets.container.selectionComboBox({
256                         id: this.app.appName + 'EditDialogContainerSelector',
257                         ref: '../../../../containerCombo',
258                         stateful: false,
259                         containerName: this.recordClass.getContainerName(),
260                         containersName: this.recordClass.getContainersName(),
261                         appName: this.appName,
262                         value: this.defaultImportContainer,
263                         requiredGrant: false
264                     })]
265                 }]
266             }]
267         };
268     },
269     
270     getDefinitionPanel: function () {
271         if (this.definitionPanel) {
272             return this.definitionPanel;
273         }
274         
275         var def = this.selectedDefinition,
276             description = def ? def.get('description') : '',
277             options = def ? def.get('plugin_options') : null,
278             example = options && options.example ? options.example : '';
279     
280         return {
281             xtype: 'panel',
282             ref: '../../definitionPanel',
283             id: 'definitionPanel',
284             hidden: true,
285             baseCls: 'ux-subformpanel',
286             title: this.app.i18n._('What should the file you upload look like?'),
287             flex: 1,
288             items: [
289             {
290                 xtype: 'label',
291                 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 />'
292             }, {
293 //                xtype: 'label',
294 //                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 />'
295 //            }, {
296                 xtype: 'label',
297                 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>'
298             }, {
299                 xtype: 'combo',
300                 ref: '../../definitionCombo',
301                 store: this.definitionsStore,
302                 displayField:'label',
303                 valueField:'id',
304                 mode: 'local',
305                 triggerAction: 'all',
306                 editable: false,
307                 allowBlank: false,
308                 forceSelection: true,
309                 width: 400,
310                 value: this.selectedDefinition ? this.selectedDefinition.id : null,
311                 listeners: {
312                     scope: this,
313                     'select': this.onDefinitionSelect
314                 }
315             }, {
316                 xtype: 'label',
317                 ref: '../../exampleLink',
318                 html: example ? ('<p><a href="' + example + '">' + this.app.i18n._('Download example file') + '</a></p>') : '<p>&nbsp;</p>'
319             }, {
320                 xtype: 'displayfield',
321                 ref: '../../definitionDescription',
322                 height: 70,
323                 value: description,
324                 cls: 'x-ux-display-background-border',
325                 style: 'padding-left: 5px;'
326             }]
327         };
328     },
329     
330     /**
331      * returns the file panel of this wizard (step 1)
332      */
333     getPanel: function() {
334         var def = this.selectedDefinition,
335             description = def ? def.get('description') : '',
336             options = def ? def.get('plugin_options') : null,
337             example = options && options.example ? options.example : '';
338         
339         var types = [
340             ['remote_ics', this.app.i18n._('Remote / ICS')],
341 //            ['remote_caldav', _('Remote / CALDav')],
342             ['upload', this.app.i18n._('Upload')]
343         ]
344         
345         var typeStore = new Ext.data.ArrayStore({
346             fields: [
347                 'type_id',
348                 'type_value'
349             ],
350             data: types,
351             disabled: false
352         });
353
354         return {
355             title: this.app.i18n._('Choose File and Format'),
356             layout: 'vbox',
357             border: false,
358             xtype: 'ux.displaypanel',
359             frame: true,
360             ref: '../filePanel',
361             items: [{
362                 xtype: 'panel',
363                 baseCls: 'ux-subformpanel',
364                 title: this.app.i18n._('Select type of source'),
365                 height: 100,
366                 items: [{
367                         xtype: 'label',
368                         html: '<p>' + this.app.i18n._('Please select the type of source you want to add to Tine 2.0') + '</p><br />'
369                 }, {
370                     xtype: 'combo',
371                     mode: 'local',
372                     ref: '../../typeCombo',
373                     width: 400,
374                     listeners:{
375                         scope: this,
376                         'select': function (combo) {
377                             if (combo.getValue() == 'upload') {
378                                 Ext.getCmp('uploadPanel').show();
379                                 Ext.getCmp('definitionPanel').show();
380                                 Ext.getCmp('remotePanel').hide();
381                             } else if (combo.getValue() == 'remote_ics' || combo.getValue() == 'remote_caldav') {
382                                 Ext.getCmp('uploadPanel').hide();
383                                 Ext.getCmp('definitionPanel').hide();
384                                 Ext.getCmp('remotePanel').show();
385                             }
386                             
387                             this.doLayout();
388                             this.manageButtons();
389                         },
390                         'render': function (combo) {
391                             /**
392                             * @todo enable and allow remotes
393                             *  ~mspahn
394                             */
395                             combo.setValue('upload');
396                             Ext.getCmp('uploadPanel').show();
397                             Ext.getCmp('definitionPanel').show();
398                             Ext.getCmp('remotePanel').hide();
399                         }
400                     },
401                     scope: this,
402                     valueField: 'type_id',
403                     displayField: 'type_value',
404                     store: typeStore
405                 }]
406             },
407             this.getUploadPanel(),
408             this.getRemotePanel(),
409             this.getImportOptionsPanel(),
410             this.getDefinitionPanel()],
411
412             /**
413             * finish button handler for this panel
414             */
415             onFinishButton: (function() {
416                 if (! this.importMask) {
417                     this.importMask = new Ext.LoadMask(this.getEl(), {msg: String.format(_('Importing {0}'), this.recordClass.getRecordsName())});
418                 }
419                 this.importMask.show();
420
421                 // collect client data
422                 var clientRecordData = [];
423                 var importOptions = {};
424
425                 this.doImport(function(request, success, response) {
426                     this.importMask.hide();
427
428                     this.fireEvent('finish', this, this.layout.activeItem);
429
430                     if (Ext.isArray(response.exceptions) && response.exceptions.length > 0) {
431                         this.backButton.setDisabled(true);
432                         this.finishButton.setHandler(function() {this.window.close()}, this);
433                     } else {
434                         this.window.close();
435                     }
436                 }, importOptions, clientRecordData);
437             }).createDelegate(this),
438
439             /**
440              * @todo fix later
441              */
442             finishIsAllowed: (function() {
443                 return (
444                     ((this.typeCombo && (this.typeCombo.getValue() == 'remote_ics' || this.typeCombo.getValue() == 'remote_caldav'))
445                     && (this.remoteLocation && this.remoteLocation.getValue())
446                     && (this.ttlCombo && (this.ttlCombo.getValue() || this.ttlCombo.getValue() === 0))))
447                     || ((this.typeCombo && (this.typeCombo.getValue() == 'upload'))
448                     && (this.definitionCombo && this.definitionCombo.getValue())
449                     && (this.uploadButton && this.uploadButton.upload))
450                     && ((this.containerField && this.containerField.getValue()) || (this.containerCombo && this.containerCombo.getValue())
451                 );
452
453             }).createDelegate(this)
454         };
455     }
456 });
457
458 /**
459  * Create new import window
460  */
461 Tine.Calendar.ImportDialog.openWindow = function (config) {
462     var window = Tine.WindowFactory.getWindow({
463         width: 800,
464         height: 600,
465         name: Tine.Calendar.ImportDialog.windowNamePrefix + Ext.id(),
466         contentPanelConstructor: 'Tine.Calendar.ImportDialog',
467         contentPanelConstructorConfig: config
468     });
469     return window;
470 };