Merge branch 'gilbert-ext4-murano' of git://github.com/lapilofu/philo into gilbert...
[philo.git] / philo / contrib / gilbert / static / gilbert / plugins / models.js
1 Ext.ns('Gilbert.lib.plugins.models.ui');
2
3
4 Ext.override(Gilbert.lib.models.Model, {
5         create_new_form: function (callback, config) {
6                 var model = this;
7                 var config = config;
8                 model.api.get_form({}, function (formspec) {
9                         var formspec = formspec;
10                         for (var item_index in formspec.items) {
11                                 var item = formspec.items[item_index];
12                                 Ext.apply(item, {
13                                         anchor: '100%',
14                                 });
15                         }
16                         var form_panel = new Gilbert.lib.ui.DjangoForm(Ext.applyIf(Ext.applyIf(config||{},{
17                                 title: 'New '+model.verbose_name,
18                                 header: false,
19                                 iconCls: 'icon-plus',
20                                 baseCls: 'x-plain',
21                                 autoScroll: true,
22                                 api: {
23                                         submit: model.api.save_form,
24                                 },
25                         }), formspec));
26                         callback(form_panel);
27                 });
28         },
29         create_edit_form: function (callback, pk, config) {
30                 var model = this;
31                 var config = config;
32                 model.api.get_form({'pk': pk}, function (formspec) {
33                         var formspec = formspec;
34                         for (var item_index in formspec.items) {
35                                 var item = formspec.items[item_index];
36                                 Ext.apply(item, {
37                                         anchor: '100%',
38                                 });
39                         }
40                         callback(new Gilbert.lib.ui.DjangoForm(Ext.applyIf(Ext.applyIf(config||{},{
41                                 title: 'Editing '+model.verbose_name.capfirst()+' ('+pk+')',
42                                 header: false,
43                                 iconCls: 'icon-pencil',
44                                 baseCls: 'x-plain',
45                                 autoScroll: true,
46                                 api: {
47                                         submit: model.api.save_form,
48                                 },
49                                 baseParams: {
50                                         pk: pk,
51                                 },
52                         }), formspec)));
53                 });
54         },
55 });
56
57
58 Gilbert.lib.plugins.models.ui.ForeignKeyColumn = Ext.extend(Ext.grid.Column, {
59         renderer: function(v) {
60                 return v.__unicode__
61         }
62 })
63
64
65 Ext.grid.Column.types['foreignkeycolumn'] = Gilbert.lib.plugins.models.ui.ForeignKeyColumn
66
67
68 Gilbert.lib.plugins.models.ui.ModelPanel = Ext.extend(Ext.Panel, {
69         constructor: function (model, plugin, config) {
70                 var model = this.model = model;
71                 var plugin = this.plugin = plugin;
72                 var application = this.application = plugin.application;
73                 var outer = this;
74                 
75                 var store = this.store = model.create_store({
76                         autoLoad: true,
77                         autoDestroy: true,
78                         autoSave: false,
79                         baseParams: {
80                                 start: 0,
81                                 limit: 25,
82                         },
83                 });
84                 
85                 var grid = this.grid = new Ext.grid.GridPanel({
86                         ddGroup: model.drag_drop_group,
87                         enableDragDrop: true,
88                         loadMask: true,
89                         store: store,
90                         columns: model.columns,
91                         columnLines: true,
92                         stripeRows: true,
93                         viewConfig: {
94                                 forceFit: true,
95                         },
96                         selModel: new Ext.grid.RowSelectionModel(),
97                         bbar: new Ext.PagingToolbar({
98                                 pageSize: 25,
99                                 store: store,
100                                 displayInfo: true,
101                                 displayMsg: 'Displaying '+model.verbose_name_plural+' {0} - {1} of {2}',
102                                 emptyMsg: 'No '+model.verbose_name_plural+' to display',
103                                 items: (function () {
104                                         if (model.searchable) {
105                                                 return [
106                                                         {
107                                                                 xtype: 'tbseparator',
108                                                         },
109                                                         new Ext.ux.form.SearchField({
110                                                                 store: store,
111                                                         }),
112                                                 ];
113                                         } else {
114                                                 return [];
115                                         }
116                                 })(),
117                         }),
118                 });
119                 
120                 var new_action = this.new_action = new Ext.Action({
121                         text: 'New ' + model.verbose_name,
122                         iconCls: 'icon-plus',
123                         handler: function () {
124                                 plugin.create_instance_window(model, undefined, function (win) {
125                                         win.on('saved', function () {
126                                                 store.reload();
127                                         });
128                                         win.show();
129                                 });
130                         },
131                 });
132                 
133                 var edit_action = this.edit_action = new Ext.Action({
134                         disabled: true,
135                         text: 'Edit',
136                         iconCls: 'icon-pencil',
137                         handler: function () {
138                                 Ext.each(grid.getSelectionModel().getSelections(), function (record, index) {
139                                         plugin.create_instance_window(model, record.id, function (win) {
140                                                 win.on('saved', function () {
141                                                         store.reload();
142                                                 });
143                                                 win.show();
144                                         });
145                                 });
146                         }
147                 });
148                 
149                 var delete_action = this.delete_action = new Ext.Action({
150                         disabled: true,
151                         text: 'Delete',
152                         iconCls: 'icon-minus',
153                         handler: function () {
154                                 var records = grid.getSelectionModel().getSelections();
155                                 var pks = [];
156                                 Ext.each(records, function (record, index) {
157                                         pks.push(record.id);
158                                 });
159                                 model.api.data_destroy_consequences(pks, function (consequences) {
160                                         var convert_consequences_array = function (consequences) {
161                                                 var last_parent = consequences[0];
162                                                 Ext.each(consequences, function (consequence, index) {
163                                                         if (index != 0) {
164                                                                 if (!Ext.isArray(consequence)) {
165                                                                         last_parent = consequence;
166                                                                 } else {
167                                                                         last_parent['children'] = convert_consequences_array(consequence);
168                                                                         delete consequences[index];
169                                                                 }
170                                                         }
171                                                 });
172                                                 new_consequences = [];
173                                                 Ext.each(consequences, function (consequence) {
174                                                         if (consequence) {
175                                                                 var new_consequence = {};
176                                                                 if (!consequence['children']) {
177                                                                         new_consequence['leaf'] = true;
178                                                                 } else {
179                                                                         new_consequence['leaf'] = false;
180                                                                         new_consequence['children'] = consequence['children'];
181                                                                 }
182                                                                 var app_label = consequence['app_label'];
183                                                                 var name = consequence['name'];
184                                                                 var model = Gilbert.get_model(app_label, name);
185                                                                 if (model) {
186                                                                         new_consequence['text'] = consequence['__unicode__'];
187                                                                         new_consequence['iconCls'] = model.iconCls;
188                                                                 } else {
189                                                                         new_consequence['text'] = '(' + consequence['name'] + ') ' + consequence['__unicode__'];
190                                                                         new_consequence['iconCls'] = 'icon-block';
191                                                                 }
192                                                                 new_consequence['disabled'] = true;
193                                                                 new_consequences.push(new_consequence);
194                                                         }
195                                                 });
196                                                 return new_consequences;
197                                         };
198                                         
199                                         var new_consequences = convert_consequences_array(consequences);
200                                         var nested_consequences = false;
201                                         for(var i=0;i<new_consequences.length;i++){
202                                                 if (!new_consequences[i]['leaf']) {
203                                                         nested_consequences = true;
204                                                         break;
205                                                 }
206                                         }
207                                         
208                                         var tree = this.tree = new Ext.tree.TreePanel({
209                                                 loader: new Ext.tree.TreeLoader(),
210                                                 enableDD: false,
211                                                 animate: false,
212                                                 trackMouseOver: false,
213                                                 autoScroll: true,
214                                                 root: {
215                                                         'disabled': true,
216                                                         'text': 'To be deleted',
217                                                         'iconCls': 'icon-minus',
218                                                         'leaf': false,
219                                                         'children': new_consequences,
220                                                 },
221                                                 useArrows: true,
222                                                 rootVisible: false,
223                                                 region: 'center',
224                                         });
225                                         
226                                         var consequences_win = application.create_window({
227                                                 layout: 'border',
228                                                 width: 300,
229                                                 height: 300,
230                                                 modal: true,
231                                                 title: 'Delete ' + model.verbose_name_plural,
232                                                 iconCls: 'icon-minus',
233                                                 items: [
234                                                         {
235                                                                 region: 'north',
236                                                                 xtype: 'panel',
237                                                                 html: 'Are you sure you want to delete these ' + model.verbose_name_plural + '?' + (nested_consequences ? ' Nested objects will also be deleted.' : ''),
238                                                                 bodyStyle: 'padding: 15px;',
239                                                         },
240                                                         tree,
241                                                 ],
242                                                 bbar: [
243                                                         {
244                                                                 xtype: 'button',
245                                                                 text: 'Cancel',
246                                                                 handler: function () {
247                                                                         consequences_win.close();
248                                                                 },
249                                                         },
250                                                         '->',
251                                                         {
252                                                                 xtype: 'button',
253                                                                 text: 'Delete',
254                                                                 handler: function () {
255                                                                         consequences_win.close();
256                                                                         store.remove(records);
257                                                                         store.save();
258                                                                         store.reload();
259                                                                 },
260                                                         },
261                                                 ],
262                                         });
263                                         
264                                         consequences_win.show();
265                                 });
266                         }
267                 });
268                 
269                 grid.on('cellcontextmenu', function (grid, rowIndex, cellIndex, e) {
270                         e.stopEvent();
271                         selmodel = grid.getSelectionModel();
272                         if (!selmodel.isSelected(rowIndex)) {
273                                 selmodel.selectRow(rowIndex, false);
274                         }
275                         var contextmenu = new Ext.menu.Menu({
276                                 items: [
277                                         edit_action,
278                                         delete_action,
279                                 ],
280                         });
281                         contextmenu.showAt(e.xy);
282                 });
283                 
284                 grid.on('rowdblclick', function(grid, rowIndex, e) {
285                         var record = grid.getStore().getAt(rowIndex)
286                         plugin.create_instance_window(model, record.id, function (win) {
287                                 win.on('saved', function () {
288                                         store.reload();
289                                 });
290                                 win.show();
291                         });
292                 });
293                 
294                 grid.getSelectionModel().on('selectionchange', function (selmodel) {
295                         if (selmodel.hasSelection()) {
296                                 edit_action.setDisabled(false);
297                                 delete_action.setDisabled(false);
298                         } else {
299                                 edit_action.setDisabled(true);
300                                 delete_action.setDisabled(true);
301                         }
302                 });
303                 
304                 Gilbert.lib.plugins.models.ui.ModelPanel.superclass.constructor.call(this, Ext.applyIf(config||{}, {
305                         layout: 'fit',
306                         tbar: new Ext.Toolbar({
307                                 items: [
308                                         new_action,
309                                         { xtype: 'tbseparator' },
310                                         edit_action,
311                                         delete_action,
312                                         //'->',
313                                         //{
314                                         //      text: 'Advanced',
315                                         //      iconCls: 'icon-gear',
316                                         //      disabled: true,
317                                         //      menu: [],
318                                         //},
319                                 ],
320                         }),
321                         items: [grid],
322                 }));
323         },
324 });
325
326
327 Gilbert.lib.plugins.models.Plugin = Ext.extend(Gilbert.lib.plugins.Plugin, {
328         
329         init: function (application) {
330                 Gilbert.lib.plugins.models.Plugin.superclass.init.call(this, application);
331                 
332                 var new_menu = this.new_menu = new Ext.menu.Menu();
333                 var manage_menu = this.manage_menu = new Ext.menu.Menu();
334                 
335                 application.mainmenu.insert(2, {
336                         xtype: 'button',
337                         iconCls: 'icon-plus',
338                         text: 'New',
339                         menu: new_menu,
340                 });
341                 
342                 application.mainmenu.insert(3, {
343                         xtype: 'button',
344                         iconCls: 'icon-databases',
345                         text: 'Manage',
346                         menu: manage_menu,
347                 });
348                 
349                 application.do_layout();
350                 
351                 Ext.iterate(application.models, function (app_label, models) {
352                         Ext.iterate(models, function (name, model) {
353                                 this.handle_new_model(model);
354                         }, this);
355                 }, this);
356                 
357                 application.on('model_registered', function (model) {
358                         this.handle_new_model(model);
359                 }, this);
360                 
361                 this.instance_windows = {}
362         },
363         
364         handle_new_model: function (model) {
365                 var outer = this;
366                 model.api.has_add_permission(function (has_add_permission) {
367                         if (has_add_permission) {
368                                 outer.add_to_new_menu(model);
369                         }
370                 });
371                 model.api.has_read_permission(function (has_read_permission) {
372                         if (has_read_permission) {
373                                 outer.add_to_manage_menu(model);
374                         }
375                 });
376         },
377         
378         add_to_new_menu: function (model) {
379                 var outer = this;
380                 this.new_menu.add({
381                         text: model.verbose_name.capfirst(),
382                         iconCls: model.iconCls,
383                         model: model,
384                         handler: function (button, event) {
385                                 outer.create_instance_window(this.model, undefined, function (win) {
386                                         win.show();
387                                 });
388                         },
389                 });
390         },
391         
392         add_to_manage_menu: function (model) {
393                 var outer = this;
394                 this.manage_menu.add({
395                         text: model.verbose_name_plural.capfirst(),
396                         iconCls: model.iconCls,
397                         model: model,
398                         handler: function (button, event) {
399                                 var win = outer.create_model_management_window(this.model);
400                                 win.show(button.el);
401                         },
402                 });
403         },
404         
405         create_model_management_window: function (model, config, cls) {
406                 var model = model;
407                 var panel = new Gilbert.lib.plugins.models.ui.ModelPanel(model, this);
408                 var win = this.application.create_window(Ext.applyIf(config||{},{
409                         layout: 'fit',
410                         title: model.verbose_name_plural.capfirst(),
411                         iconCls: model.iconCls,
412                         width: 640,
413                         height: 320,
414                         maximizable: true,
415                         items: [panel],
416                 }), cls);
417                 return win;
418         },
419         
420         create_instance_window: function (model, pk, callback, config, cls) {
421                 if (pk != undefined) {
422                         var win = this.instance_windows[[model.app_label, model.name, pk]];
423                         if (win != undefined){
424                                 win.show();
425                                 return;
426                         };
427                 };
428                 var pk = pk;
429                 var callback = callback;
430                 var application = this.application;
431                 var outer = this;
432                 
433                 var form_callback = function (form) {
434                         var oldform = form;
435                         var win = application.create_window({
436                                 layout: 'fit',
437                                 title: form.title,
438                                 iconCls: form.iconCls,
439                                 bodyStyle: 'padding: 5px; background: solid;',
440                                 width: 640,
441                                 height: 320,
442                                 maximizable: true,
443                                 items: [form],
444                                 bbar: [
445                                         '->',
446                                         {
447                                                 xtype: 'button',
448                                                 text: 'Save and Close',
449                                                 iconCls: 'icon-database-import',
450                                                 handler: function (button) {
451                                                         var loading_mask = new Ext.LoadMask(win.body, {
452                                                                 msg: 'Saving...',
453                                                                 removeMask: true,
454                                                         });
455                                                         loading_mask.show();
456                                                         win.items.items[0].getForm().submit({
457                                                                 success: function (form, action) {
458                                                                         loading_mask.hide();
459                                                                         win.fireEvent('saved');
460                                                                         win.close();
461                                                                 },
462                                                                 failure: function (form, action) {
463                                                                         loading_mask.hide();
464                                                                 },
465                                                         });
466                                                 }
467                                         },
468                                         {
469                                                 xtype: 'button',
470                                                 text: 'Save',
471                                                 iconCls: 'icon-database-import',
472                                                 handler: function (button) {
473                                                         var loading_mask = new Ext.LoadMask(win.body, {
474                                                                 msg: 'Saving...',
475                                                                 removeMask: true,
476                                                         });
477                                                         loading_mask.show();
478                                                         win.items.items[0].getForm().submit({
479                                                                 success: function (form, action) {
480                                                                         win.fireEvent('saved');
481                                                                         var pk = action.result.pk;
482                                                                         model.create_edit_form(function (newform) {
483                                                                                 win.remove(oldform);
484                                                                                 win.add(newform);
485                                                                                 loading_mask.hide();
486                                                                                 win.setTitle(newform.title);
487                                                                                 win.setIconClass(newform.iconCls);
488                                                                                 win.doLayout();
489                                                                         }, pk);
490                                                                 },
491                                                                 failure: function (form, action) {
492                                                                         loading_mask.hide();
493                                                                 },
494                                                         });
495                                                 },
496                                         },
497                                 ],
498                         });
499                         if (pk != undefined) {
500                                 outer.instance_windows[[model.app_label, model.name, pk]] = win
501                                 win.on('close', function(){
502                                         delete outer.instance_windows[[model.app_label, model.name, pk]];
503                                 });
504                         }
505                         win.addEvents({
506                                 'saved': true,
507                         });
508                         callback(win);
509                 };
510
511                 if (pk) {
512                         model.create_edit_form(form_callback, pk, {
513                                 bodyStyle: 'padding: 10px;',
514                         });
515                 } else {
516                         model.create_new_form(form_callback, {
517                                 bodyStyle: 'padding: 10px;',
518                         });
519                 }
520         },
521         
522 });
523
524
525 Gilbert.on('ready', function (application) {
526         application.register_plugin('models', new Gilbert.lib.plugins.models.Plugin());
527 });