79ef0b321d14427a8cf9fe47734c99451df30e4e
[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.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 tree = this.tree = new Ext.tree.TreePanel({
200                                                 loader: new Ext.tree.TreeLoader(),
201                                                 enableDD: false,
202                                                 animate: false,
203                                                 trackMouseOver: false,
204                                                 autoScroll: true,
205                                                 root: {
206                                                         'disabled': true,
207                                                         'text': 'To be deleted',
208                                                         'iconCls': 'icon-minus',
209                                                         'leaf': false,
210                                                         'children': convert_consequences_array(consequences),
211                                                 },
212                                                 useArrows: true,
213                                                 rootVisible: false,
214                                                 region: 'center',
215                                         });
216                                         
217                                         var consequences_win = application.create_window({
218                                                 layout: 'border',
219                                                 width: 300,
220                                                 height: 300,
221                                                 modal: true,
222                                                 title: 'Delete ' + model.verbose_name_plural,
223                                                 iconCls: 'icon-minus',
224                                                 items: [
225                                                         {
226                                                                 region: 'north',
227                                                                 xtype: 'panel',
228                                                                 html: 'Are you sure you want to delete these ' + model.verbose_name_plural + '?',
229                                                                 bodyStyle: 'padding: 15px;',
230                                                         },
231                                                         tree,
232                                                 ],
233                                                 bbar: [
234                                                         {
235                                                                 xtype: 'button',
236                                                                 text: 'Cancel',
237                                                                 handler: function () {
238                                                                         consequences_win.close();
239                                                                 },
240                                                         },
241                                                         '->',
242                                                         {
243                                                                 xtype: 'button',
244                                                                 text: 'Delete',
245                                                                 handler: function () {
246                                                                         consequences_win.close();
247                                                                         store.remove(records);
248                                                                         store.save();
249                                                                         store.reload();
250                                                                 },
251                                                         },
252                                                 ],
253                                         });
254                                         
255                                         consequences_win.show();
256                                 });
257                         }
258                 });
259                 
260                 grid.on('cellcontextmenu', function (grid, rowIndex, cellIndex, e) {
261                         e.stopEvent();
262                         selmodel = grid.getSelectionModel();
263                         if (!selmodel.isSelected(rowIndex)) {
264                                 selmodel.selectRow(rowIndex, false);
265                         }
266                         var contextmenu = new Ext.menu.Menu({
267                                 items: [
268                                         edit_action,
269                                         delete_action,
270                                 ],
271                         });
272                         contextmenu.showAt(e.xy);
273                 });
274                 
275                 grid.on('rowdblclick', function(grid, rowIndex, e) {
276                         var record = grid.getStore().getAt(rowIndex)
277                         plugin.create_instance_window(model, record.id, function (win) {
278                                 win.on('saved', function () {
279                                         store.reload();
280                                 });
281                                 win.show();
282                         });
283                 });
284                 
285                 grid.getSelectionModel().on('selectionchange', function (selmodel) {
286                         if (selmodel.hasSelection()) {
287                                 edit_action.setDisabled(false);
288                                 delete_action.setDisabled(false);
289                         } else {
290                                 edit_action.setDisabled(true);
291                                 delete_action.setDisabled(true);
292                         }
293                 });
294                 
295                 Gilbert.lib.plugins.models.ui.ModelPanel.superclass.constructor.call(this, Ext.applyIf(config||{}, {
296                         layout: 'fit',
297                         tbar: new Ext.Toolbar({
298                                 items: [
299                                         new_action,
300                                         { xtype: 'tbseparator' },
301                                         edit_action,
302                                         delete_action,
303                                         '->',
304                                         {
305                                                 text: 'Advanced',
306                                                 iconCls: 'icon-gear',
307                                                 disabled: true,
308                                                 menu: [],
309                                         },
310                                 ],
311                         }),
312                         items: [grid],
313                 }));
314         },
315 });
316
317
318 Gilbert.lib.plugins.models.Plugin = Ext.extend(Gilbert.lib.plugins.Plugin, {
319         
320         init: function (application) {
321                 Gilbert.lib.plugins.models.Plugin.superclass.init.call(this, application);
322                 
323                 var new_menu = this.new_menu = new Ext.menu.Menu();
324                 var manage_menu = this.manage_menu = new Ext.menu.Menu();
325                 
326                 application.mainmenu.insert(2, {
327                         xtype: 'button',
328                         iconCls: 'icon-plus',
329                         text: 'New',
330                         menu: new_menu,
331                 });
332                 
333                 application.mainmenu.insert(3, {
334                         xtype: 'button',
335                         iconCls: 'icon-databases',
336                         text: 'Manage',
337                         menu: manage_menu,
338                 });
339                 
340                 application.do_layout();
341                 
342                 Ext.iterate(application.models, function (app_label, models) {
343                         Ext.iterate(models, function (name, model) {
344                                 this.handle_new_model(model);
345                         }, this);
346                 }, this);
347                 
348                 application.on('model_registered', function (model) {
349                         this.handle_new_model(model);
350                 }, this);
351                 
352                 this.instance_windows = {}
353         },
354         
355         handle_new_model: function (model) {
356                 var outer = this;
357                 model.api.has_add_permission(function (has_add_permission) {
358                         if (has_add_permission) {
359                                 outer.add_to_new_menu(model);
360                         }
361                 });
362                 model.api.has_read_permission(function (has_read_permission) {
363                         if (has_read_permission) {
364                                 outer.add_to_manage_menu(model);
365                         }
366                 });
367         },
368         
369         add_to_new_menu: function (model) {
370                 var outer = this;
371                 this.new_menu.add({
372                         text: model.verbose_name.capfirst(),
373                         iconCls: model.iconCls,
374                         model: model,
375                         handler: function (button, event) {
376                                 outer.create_instance_window(this.model, undefined, function (win) {
377                                         win.show();
378                                 });
379                         },
380                 });
381         },
382         
383         add_to_manage_menu: function (model) {
384                 var outer = this;
385                 this.manage_menu.add({
386                         text: model.verbose_name_plural.capfirst(),
387                         iconCls: model.iconCls,
388                         model: model,
389                         handler: function (button, event) {
390                                 var win = outer.create_model_management_window(this.model);
391                                 win.show(button.el);
392                         },
393                 });
394         },
395         
396         create_model_management_window: function (model, config, cls) {
397                 var model = model;
398                 var panel = new Gilbert.lib.plugins.models.ui.ModelPanel(model, this);
399                 var win = this.application.create_window(Ext.applyIf(config||{},{
400                         layout: 'fit',
401                         title: model.verbose_name_plural.capfirst(),
402                         iconCls: model.iconCls,
403                         width: 640,
404                         height: 320,
405                         maximizable: true,
406                         items: [panel],
407                 }), cls);
408                 return win;
409         },
410         
411         create_instance_window: function (model, pk, callback, config, cls) {
412                 var win = this.instance_windows[[model.app_label, model.name, pk]];
413                 if (win != undefined){
414                         win.show()
415                         return
416                 }
417                 var pk = pk;
418                 var callback = callback;
419                 var application = this.application;
420                 var outer = this;
421                 
422                 var form_callback = function (form) {
423                         var oldform = form;
424                         var win = outer.instance_windows[[model.app_label, model.name, pk]] = application.create_window({
425                                 layout: 'fit',
426                                 title: form.title,
427                                 iconCls: form.iconCls,
428                                 bodyStyle: 'padding: 5px; background: solid;',
429                                 width: 640,
430                                 height: 320,
431                                 maximizable: true,
432                                 items: [form],
433                                 bbar: [
434                                         '->',
435                                         {
436                                                 xtype: 'button',
437                                                 text: 'Save and Close',
438                                                 iconCls: 'icon-database-import',
439                                                 handler: function (button) {
440                                                         var loading_mask = new Ext.LoadMask(win.body, {
441                                                                 msg: 'Saving...',
442                                                                 removeMask: true,
443                                                         });
444                                                         loading_mask.show();
445                                                         win.items.items[0].getForm().submit({
446                                                                 success: function (form, action) {
447                                                                         loading_mask.hide();
448                                                                         win.fireEvent('saved');
449                                                                         win.close();
450                                                                 },
451                                                                 failure: function (form, action) {
452                                                                         loading_mask.hide();
453                                                                 },
454                                                         });
455                                                 }
456                                         },
457                                         {
458                                                 xtype: 'button',
459                                                 text: 'Save',
460                                                 iconCls: 'icon-database-import',
461                                                 handler: function (button) {
462                                                         var loading_mask = new Ext.LoadMask(win.body, {
463                                                                 msg: 'Saving...',
464                                                                 removeMask: true,
465                                                         });
466                                                         loading_mask.show();
467                                                         win.items.items[0].getForm().submit({
468                                                                 success: function (form, action) {
469                                                                         win.fireEvent('saved');
470                                                                         var pk = action.result.pk;
471                                                                         model.create_edit_form(function (newform) {
472                                                                                 win.remove(oldform);
473                                                                                 win.add(newform);
474                                                                                 loading_mask.hide();
475                                                                                 win.setTitle(newform.title);
476                                                                                 win.setIconClass(newform.iconCls);
477                                                                                 win.doLayout();
478                                                                         }, pk);
479                                                                 },
480                                                                 failure: function (form, action) {
481                                                                         loading_mask.hide();
482                                                                 },
483                                                         });
484                                                 },
485                                         },
486                                 ],
487                         });
488                         win.on('close', function(){
489                                 delete outer.instance_windows[[model.app_label, model.name, pk]];
490                         });
491                         win.addEvents({
492                                 'saved': true,
493                         });
494                         callback(win);
495                 };
496
497                 if (pk) {
498                         model.create_edit_form(form_callback, pk, {
499                                 bodyStyle: 'padding: 10px;',
500                         });
501                 } else {
502                         model.create_new_form(form_callback, {
503                                 bodyStyle: 'padding: 10px;',
504                         });
505                 }
506         },
507         
508 });
509
510
511 Gilbert.on('ready', function (application) {
512         application.register_plugin('models', new Gilbert.lib.plugins.models.Plugin());
513 });