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