Merge lp:~openerp-dev/openobject-client-web/trunk-cal-events-readonly into lp:openobject-client-web/trunk

Proposed by Olivier Laurent (Open ERP)
Status: Merged
Merged at revision: 3734
Proposed branch: lp:~openerp-dev/openobject-client-web/trunk-cal-events-readonly
Merge into: lp:openobject-client-web/trunk
Diff against target: 774 lines (+206/-131)
10 files modified
addons/view_calendar/static/javascript/calendar_gantt.js (+78/-68)
addons/view_calendar/static/javascript/calendar_month.js (+26/-16)
addons/view_calendar/static/javascript/calendar_utils.js (+10/-1)
addons/view_calendar/static/javascript/calendar_week.js (+48/-22)
addons/view_calendar/widgets/_base.py (+27/-5)
addons/view_calendar/widgets/templates/day.mako (+2/-2)
addons/view_calendar/widgets/templates/gantt.mako (+1/-1)
addons/view_calendar/widgets/templates/month.mako (+2/-2)
addons/view_calendar/widgets/templates/week.mako (+12/-12)
addons/view_calendar/widgets/widgets.py (+0/-2)
To merge this branch: bzr merge lp:~openerp-dev/openobject-client-web/trunk-cal-events-readonly
Reviewer Review Type Date Requested Status
Xavier (Open ERP) (community) Approve
OpenERP R&D Web Team functional tests (different browsers), non regression tests Pending
Review via email: mp+39348@code.launchpad.net

Description of the change

Some events have their 'starting date' and 'end date' read only depending on their state. For example, you cannot edit crm.meeting dates if it's state is 'done'.

But the calendar was not taking care of this and you could drag and drop events regardless of their state.

This branch is supposed to fix that.

To post a comment you must log in.
Revision history for this message
Xavier (Open ERP) (xmo-deactivatedaccount) wrote :

* New code should avoid using Mochikit functions when possible, and use jQuery instead (hasElementClass, getNodeAttribute)
* The call to self._is_event_droppable could probably go directly into the final TinyEvent call
* In the templates, TinyEvent.droppable is accessed 6 times (I think) and every single time it's in a conditional to decide on the right class to set. Maybe TinyEvent should have an additional droppable_class property which would directly return the right class for the event, no?

review: Needs Fixing
3716. By Olivier Laurent (Open ERP)

[IMP] calendar: simplified the way we access the 'state' attribute from fields

PS: thanks to Xavier

3717. By Olivier Laurent (Open ERP)

[FIX] gantt: calendar objects with server side readonly date fields should not be movable

3718. By Olivier Laurent (Open ERP)

[FIX] calendar, gantt: MochiKit -> jQuery

3719. By Olivier Laurent (Open ERP)

[FIX] calendar, gantt: MochiKit -> jQuery

3720. By Olivier Laurent (Open ERP)

[IMP] calendar: removed '_is_event_droppable' method

Revision history for this message
Olivier Laurent (Open ERP) (olt) wrote :

> * New code should avoid using Mochikit functions when possible, and use jQuery
> instead (hasElementClass, getNodeAttribute)

Ok. I did it but only for functions I added myself. I left the other one as is.

> * The call to self._is_event_droppable could probably go directly into the
> final TinyEvent call

Did it.

> * In the templates, TinyEvent.droppable is accessed 6 times (I think) and
> every single time it's in a conditional to decide on the right class to set.
> Maybe TinyEvent should have an additional droppable_class property which would
> directly return the right class for the event, no?

I started by adding an attribute to the div but I ran into some problems, which were probably related to something else. Since it's not always easy to debug javascript, I moved that atrribute to a new class. Perhaps that in the mean time, my problems were solved but I left that class. Now that code is running with the class, I could probably move it to an attribute again.

Revision history for this message
Xavier (Open ERP) (xmo-deactivatedaccount) wrote :

> Ok. I did it but only for functions I added myself. I left the other one as is.

Yep, sensible, didn't expect you to fix everything.

Note that (for next time around maybe) jQuery's `hasClass` works on element sets, so

    jQuery(element).hasClass('is-not-droppable') || jQuery(element.parentNode).hasClass('is-not-droppable')

could also have been written

    jQuery([element, element.parentNode]).hasClass('is-not-droppable')

> Did it.

OK

> I started by adding an attribute to the div but I ran into some problems, which were probably related to something else. Since it's not always easy to debug javascript, I moved that atrribute to a new class. Perhaps that in the mean time, my problems were solved but I left that class. Now that code is running with the class, I could probably move it to an attribute again.

Class is OK, I didn't mean a DOM attribute, just that the code

    ${evt.droppable and 'is-droppable' or 'is-not-droppable'}

is repeated several time, and thus that maybe instead of having `evt.droppable` we could have `evt.droppable_class` with the right value and thus we could just write `${evt.droppable_class}`. Doesn't really matter.

review: Approve
3721. By Olivier Laurent (Open ERP)

[IMP] calendar, gantt: movability and resizeability (based on object 'state') are now separated

3722. By Olivier Laurent (Open ERP)

[IMP] calendar, gantt: minor modifs

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'addons/view_calendar/static/javascript/calendar_gantt.js'
2--- addons/view_calendar/static/javascript/calendar_gantt.js 2010-10-08 06:38:34 +0000
3+++ addons/view_calendar/static/javascript/calendar_gantt.js 2010-10-27 09:48:00 +0000
4@@ -18,28 +18,28 @@
5 };
6
7 GanttCalendar.prototype = {
8-
9+
10 __init__: function(options) {
11-
12+
13 this.options = MochiKit.Base.update({
14-
15+
16 }, options || {});
17-
18+
19 this.starts = MochiKit.DateTime.isoDate(getNodeAttribute('calGantt', 'dtStart'));
20-
21+
22 this.mode = openobject.dom.get('_terp_selected_mode').value;
23 this.range = parseInt(getNodeAttribute('calGantt', 'dtRange')) || 7;
24
25 this.scale = 0;
26 this.header = new GanttCalendar.Header(this);
27-
28+
29 this.events = {};
30- this.groups = {};
31-
32+ this.groups = {};
33+
34 this._makeEvents();
35-
36+
37 MochiKit.DOM.removeElement('calBodySect');
38-
39+
40 var tbl = TABLE(null,
41 TBODY(null,
42 TR(null,
43@@ -48,13 +48,13 @@
44 TR(null,
45 TD({'width': 200, 'nowrap': 'nowrap'}, DIV({'id': 'calListC'})),
46 TD({}, DIV({'id': 'calGridC'})))));
47-
48+
49 tbl.cellPadding = 0;
50 tbl.cellSpacing = 0;
51-
52+
53 tbl.style.width = '100%';
54 tbl.style.height = '100%';
55-
56+
57 MochiKit.DOM.appendChildNodes('calGantt', tbl);
58
59 this.grid = new GanttCalendar.Grid(this);
60@@ -65,7 +65,7 @@
61 this.lc = openobject.dom.get('calListC');
62
63 this.attachSignals();
64-
65+
66 },
67
68 __delete__: function() {
69@@ -96,21 +96,21 @@
70 },
71
72 onResize: function(evt) {
73-
74+
75 var h1 = getElementDimensions('calGroupC').h;
76 var h2 = openobject.dom.get('calGridC').clientHeight;
77
78 setElementDimensions('calList', {h: h1 > h2 ? h1 : h2});
79 setElementDimensions('calGrid', {h: h1 > h2 ? h1 : h2});
80 },
81-
82+
83 onScrollGrid: function(evt) {
84 this.lc.scrollTop = this.gc.scrollTop;
85 this.hc.scrollLeft = this.gc.scrollLeft;
86 },
87-
88+
89 _makeEvents: function() {
90-
91+
92 this.events = {};
93 this.groups = {};
94
95@@ -118,7 +118,7 @@
96 var groups = openobject.dom.select('div.calGroup', 'calGantt') || [];
97
98 for (var i = 0; i < events.length; i++) {
99-
100+
101 var elem = events[i];
102 var id = MochiKit.DOM.getNodeAttribute(elem, 'nRecordID');
103
104@@ -142,7 +142,7 @@
105 }
106
107 for (var i = 0; i < groups.length; i++) {
108-
109+
110 var elem = groups[i];
111 var id = MochiKit.DOM.getNodeAttribute(elem, 'nRecordID');
112 var items = MochiKit.DOM.getNodeAttribute(elem, 'items');
113@@ -208,7 +208,7 @@
114 return;
115 }
116
117- var dt = this.computeDates(element);
118+ var dt = this.computeDates(element);
119
120 var pos = getElementPosition(element, 'calGrid');
121 var dim = getElementDimensions(element);
122@@ -243,25 +243,31 @@
123 var dt = this.computeDates(element);
124
125 var self = this;
126- var req = saveCalendarRecord(id, toISOTimestamp(dt.starts), toISOTimestamp(dt.ends));
127-
128- req.addCallback(function(obj) {
129-
130- if (obj.error) {
131+
132+ var recordmoveinfo = getRecordMovability(element);
133+ if (recordmoveinfo.starts != toISOTimestamp(dt.starts) && recordmoveinfo.ends != toISOTimestamp(dt.ends)) {
134+ if (recordmoveinfo.is_not_movable) {
135 self.grid.adjust();
136- return error_display(obj.error);
137+ return error_display(_("This calendar object can no longer be moved !"));
138+ } else {
139+ var req = saveCalendarRecord(id, toISOTimestamp(dt.starts), toISOTimestamp(dt.ends));
140+
141+ req.addCallback(function(obj) {
142+
143+ if (obj.error) {
144+ self.grid.adjust();
145+ return error_display(obj.error);
146+ }
147+
148+ self.events[id].starts = toISOTimestamp(dt.starts);
149+ self.events[id].ends = toISOTimestamp(dt.ends);
150+
151+ setNodeAttribute(element, 'dtstart', toISOTimestamp(dt.starts));
152+ setNodeAttribute(element, 'dtend', toISOTimestamp(dt.ends));
153+ self.grid.adjust();
154+ });
155 }
156-
157- self.events[id].starts = toISOTimestamp(dt.starts);
158- self.events[id].ends = toISOTimestamp(dt.ends);
159-
160- setNodeAttribute(element, 'dtstart', toISOTimestamp(dt.starts));
161- setNodeAttribute(element, 'dtend', toISOTimestamp(dt.ends));
162-
163- //self.grid.adjust();
164- getCalendar();
165- });
166-
167+ }
168 },
169
170 onEventResized: function(resizable, evt) {
171@@ -300,21 +306,26 @@
172 se.setMinutes(m);
173
174 var self = this;
175- var req = saveCalendarRecord(id, toISOTimestamp(ds), toISOTimestamp(se));
176-
177- req.addCallback(function(obj) {
178-
179- if (obj.error) {
180+
181+ var recordmoveinfo = getRecordMovability(element);
182+ if (recordmoveinfo.is_not_resizeable) {
183+ self.grid.adjust();
184+ return error_display(_("This calendar object can no longer be resized !"));
185+ } else {
186+ var req = saveCalendarRecord(id, toISOTimestamp(ds), toISOTimestamp(se));
187+
188+ req.addCallback(function(obj) {
189+
190+ if (obj.error) {
191+ self.grid.adjust();
192+ return error_display(obj.error);
193+ }
194+
195+ self.events[id].ends = toISOTimestamp(se);
196+ setNodeAttribute(element, 'dtend', toISOTimestamp(se));
197 self.grid.adjust();
198- return error_display(obj.error);
199- }
200-
201- self.events[id].ends = toISOTimestamp(se);
202- setNodeAttribute(element, 'dtend', toISOTimestamp(se));
203-
204- //self.grid.adjust();
205- getCalendar();
206- });
207+ });
208+ }
209 }
210 };
211
212@@ -388,11 +399,11 @@
213 this.elements = [DIV({'class': 'calHeader'}, divs), DIV({'class': 'calHeader'}, subs)];
214 },
215
216-
217+
218 __delete__: function() {
219 },
220
221- adjust: function() {
222+ adjust: function() {
223 }
224 };
225
226@@ -443,7 +454,7 @@
227 });
228
229 appendChildNodes('calListC', DIV({'id': 'calList'}, elements));
230-
231+
232 //XXX: MochiKit bug #140 (http://trac.mochikit.com/ticket/140)
233 MochiKit.Position.includeScrollOffsets = true;
234
235@@ -453,9 +464,9 @@
236 'only': ['calEventLabel'],
237 'containment': elements
238 });
239- });
240+ });
241 },
242-
243+
244 __delete__: function() {
245 forEach(this._signals, function(s) {
246 MochiKit.Signal.disconnect(s);
247@@ -481,7 +492,6 @@
248 },
249
250 onToggle: function(element, group, evt) {
251-
252 var key = openobject.dom.get('_terp_model').value + '-' + group.model + '-' + group.id;
253
254 var visible = this.stat[key];
255@@ -549,7 +559,7 @@
256 GanttCalendar.Grid.prototype = {
257
258 __init__: function(calendar) {
259-
260+
261 this.calendar = calendar;
262 this.starts = calendar.starts;
263
264@@ -562,9 +572,9 @@
265 __delete__: function() {
266
267 },
268-
269+
270 _makeGrid: function() {
271-
272+
273 this.columns = [];
274
275 var divs = [];
276@@ -630,7 +640,7 @@
277 GanttCalendar.GridColumn.prototype = {
278
279 __init__: function(calendar, spec) {
280-
281+
282 this.calendar = calendar;
283 this.spec = spec;
284
285@@ -669,7 +679,7 @@
286
287 var group = calendar.groups[id];
288 var events = calendar.events;
289-
290+
291 this.id = id;
292 this.title = group.title;
293 this.model = group.model;
294@@ -682,7 +692,7 @@
295 var self = this;
296 forEach(this.items, function(id) {
297 var evt = events[id];
298-
299+
300 var div = DIV({
301 'nRecordID': id,
302 'dtStart': evt.starts,
303@@ -723,7 +733,7 @@
304 calculate_usages: function() {
305
306 this.bars = [];
307-
308+
309 if (!this.events.length) {
310 return;
311 }
312@@ -789,7 +799,7 @@
313 n += 1;
314 }
315 });
316-
317+
318 div.style.backgroundColor = n == 1 ? "blue" : n > 1 ? "red" : "";
319
320 return div;
321@@ -835,7 +845,7 @@
322 forEach(this.events, function(e) {
323 e.adjust();
324 });
325-
326+
327 var w = 0;
328
329 forEach(this.calendar.header.specs, function(spec) {
330@@ -941,7 +951,7 @@
331 }
332
333 var x = Math.round(x / snap) * snap;
334-
335+
336 return [x + 1, y];
337 }
338 };
339
340=== modified file 'addons/view_calendar/static/javascript/calendar_month.js'
341--- addons/view_calendar/static/javascript/calendar_month.js 2010-09-28 12:02:18 +0000
342+++ addons/view_calendar/static/javascript/calendar_month.js 2010-10-27 09:48:00 +0000
343@@ -404,23 +404,33 @@
344 e = toISOTimestamp(new Date(e))
345
346 var self = this;
347- var req = saveCalendarRecord(id, s, e);
348-
349- req.addCallback(function(obj) {
350-
351- if (obj.error) {
352- return error_display(obj.error);
353+
354+ // check that the object was really modified to avoid unnecessary warning popups:
355+ var recordmoveinfo = getRecordMovability(draggable);
356+ if (recordmoveinfo.starts != s && recordmoveinfo.ends != e) {
357+ if (recordmoveinfo.is_not_movable){
358+ self.calendar.onResize();
359+ return error_display(_("This calendar object can no longer be moved !"));
360+ } else {
361+ var req = saveCalendarRecord(id, s, e);
362+
363+ req.addCallback(function(obj) {
364+
365+ if (obj.error) {
366+ return error_display(obj.error);
367+ }
368+
369+ record.starts = s;
370+ record.ends = e;
371+
372+ self.calendar.makeEvents();
373+ });
374+ req.addBoth(function(obj) {
375+ self.calendar.onResize();
376+ });
377 }
378-
379- record.starts = s;
380- record.ends = e;
381-
382- self.calendar.makeEvents();
383- });
384-
385- req.addBoth(function(obj) {
386- self.calendar.onResize();
387- });
388+ }
389+ self.calendar.onResize();
390 },
391
392 makeEventContainers : function() {
393
394=== modified file 'addons/view_calendar/static/javascript/calendar_utils.js'
395--- addons/view_calendar/static/javascript/calendar_utils.js 2010-08-19 14:11:12 +0000
396+++ addons/view_calendar/static/javascript/calendar_utils.js 2010-10-27 09:48:00 +0000
397@@ -154,7 +154,6 @@
398 }
399
400 function saveCalendarRecord(record_id, starts, ends) {
401-
402 var params = getFormParams('_terp_concurrency_info');
403 MochiKit.Base.update(params, {
404 '_terp_id': record_id,
405@@ -203,3 +202,13 @@
406 '_terp_context': openobject.dom.get('_terp_context').value
407 });
408 }
409+
410+function getRecordMovability(element) {
411+ return {
412+ starts: jQuery(element).attr('dtstart'),
413+ ends : jQuery(element).attr('dtend'),
414+ is_not_movable: jQuery(element, element.parentNode).hasClass('event-is-not-movable')
415+ is_not_resizeable: jQuery(element, element.parentNode).hasClass('event-is-not-resizeable')
416+ }
417+}
418+
419
420=== modified file 'addons/view_calendar/static/javascript/calendar_week.js'
421--- addons/view_calendar/static/javascript/calendar_week.js 2010-10-25 12:26:19 +0000
422+++ addons/view_calendar/static/javascript/calendar_week.js 2010-10-27 09:48:00 +0000
423@@ -84,7 +84,6 @@
424 },
425
426 onResizeEnd : function(resizable, evt) {
427-
428 var element = resizable.element;
429
430 if (!hasElementClass(element, 'calEvent')) return;
431@@ -98,10 +97,16 @@
432
433 var self = this;
434
435+ // check that the object was really modified to avoid unnecessary warning popups:
436+ var recordmoveinfo = getRecordMovability(element);
437+ if (recordmoveinfo.is_not_resizeable) {
438+ self.dayGrid.adjust();
439+ return error_display(_("This calendar object can no longer be resized !"));
440+ }
441+
442 var req = saveCalendarRecord(id, toISOTimestamp(dt), toISOTimestamp(e));
443
444 req.addCallback(function(obj) {
445-
446 if (obj.error) {
447 return error_display(obj.error);
448 }
449@@ -245,7 +250,6 @@
450 },
451
452 onDrop : function(draggable, droppable, evt) {
453-
454 var dt = MochiKit.DateTime.isoDate(getNodeAttribute(droppable, 'dtDay'));
455 var id = getNodeAttribute(draggable, 'nRecordID');
456
457@@ -263,23 +267,34 @@
458 e = toISOTimestamp(new Date(e))
459
460 var self = this;
461- var req = saveCalendarRecord(id, s, e);
462-
463- req.addCallback(function(obj) {
464-
465- if (obj.error) {
466- return error_display(obj.error);
467+
468+ var recordmoveinfo = getRecordMovability(draggable);
469+
470+ // check that the object was really modified to avoid unnecessary warning popups:
471+ if (recordmoveinfo.starts != s && recordmoveinfo.ends != e) {
472+ if (recordmoveinfo.is_not_movable) {
473+ self.adjust();
474+ return error_display(_("This calendar object can no longer be moved !"));
475+ } else {
476+ var req = saveCalendarRecord(id, s, e);
477+
478+ req.addCallback(function(obj) {
479+
480+ if (obj.error) {
481+ return error_display(obj.error);
482+ }
483+
484+ record.starts = s;
485+ record.ends = e;
486+
487+ self.makeEvents();
488+ });
489+ req.addBoth(function(obj) {
490+ self.adjust();
491+ });
492 }
493-
494- record.starts = s;
495- record.ends = e;
496-
497- self.makeEvents();
498- });
499-
500- req.addBoth(function(obj) {
501- self.adjust();
502- });
503+ }
504+ self.adjust();
505 },
506
507 onMouseDown : function(evt) {
508@@ -405,7 +420,7 @@
509 grid: this, // reference to the grid
510 calendar: self.calendar, // reference to the calendar
511 events: [], // events in the day container
512- rows: [] // mark used rows
513+ rows: [] // mark used rows
514 }
515 }
516
517@@ -638,10 +653,10 @@
518 },
519
520 onDrop : function(draggable, droppable, evt) {
521-
522 var dt = MochiKit.DateTime.isoDate(getNodeAttribute(droppable, 'dtDay'));
523 var id = getNodeAttribute(draggable, 'nRecordID');
524
525+
526 var y = parseInt(draggable.style.top);
527 var h = parseInt(draggable.style.height) + 2;
528
529@@ -655,6 +670,17 @@
530 e = new Date(e);
531
532 var self = this;
533+
534+
535+ // check that the object was really modified to avoid unnecessary warning popups:
536+ var recordmoveinfo = getRecordMovability(draggable);
537+ if (recordmoveinfo.starts != toISOTimestamp(s) && recordmoveinfo.ends != toISOTimestamp(e)) {
538+ if (recordmoveinfo.is_not_movable) {
539+ self.adjust();
540+ return error_display(_("This calendar object can no longer be moved !"));
541+ }
542+ }
543+
544 var req = saveCalendarRecord(id, toISOTimestamp(s), toISOTimestamp(e));
545
546 req.addCallback(function(obj) {
547@@ -676,7 +702,7 @@
548 t.shift();
549 t = t.join(' - ');
550
551- title.innerHTML = s.strftime('%I:%M %P') + ' - ' + t;
552+ title.innerHTML = s.strftime('%H:%M') + ' - ' + t;
553 });
554
555 req.addBoth(function(obj) {
556
557=== modified file 'addons/view_calendar/widgets/_base.py'
558--- addons/view_calendar/widgets/_base.py 2010-10-25 09:07:29 +0000
559+++ addons/view_calendar/widgets/_base.py 2010-10-27 09:48:00 +0000
560@@ -75,7 +75,7 @@
561 record = {}
562 record_id = False
563
564- def __init__(self, record, starts, ends, title='', description='', dayspan=0, color=None):
565+ def __init__(self, record, starts, ends, title='', description='', dayspan=0, color=None, classes=None):
566
567 super(TinyEvent, self).__init__()
568
569@@ -95,6 +95,8 @@
570 self.create_uid = ustr(record.get('create_uid'))
571 self.write_uid = ustr(record.get('write_uid'))
572 self.write_date = ustr(record.get('write_date'))
573+ self.classes = classes and ' '.join(classes) or ''
574+
575
576 class ICalendar(TinyWidget):
577 """ Base Calendar calss
578@@ -174,7 +176,7 @@
579 self.info_fields = self.parse(root, view['fields'])
580
581 fields = view['fields']
582- fields = fields.keys() + [self.date_start, self.date_stop, self.date_delay, self.color_field]
583+ fields = fields.keys() + [self.date_start, self.date_stop, self.date_delay, self.color_field, 'state']
584
585 fields = list(set([x for x in fields if x]))
586
587@@ -228,9 +230,11 @@
588 proxy = rpc.RPCProxy(self.model)
589
590 if self.date_stop:
591+ # use the correct algorithm:
592 domain = self.domain + [(self.date_start, '<=', days[-1].isoformat() + ' 23:59:59'),
593 (self.date_stop, '>=', days[0].isoformat() + ' 00:00:00')]
594 else:
595+ # cannot use the correct algorithm, use the old one:
596 first = days[0].month2.prev()[0] #HACK: add prev month
597 domain = self.domain + [(self.date_start, '>', first.isoformat()),
598 (self.date_start, '<', days[-1].next().isoformat())]
599@@ -271,6 +275,7 @@
600 ids = proxy.search(domain, 0, 0, order_by, ctx)
601
602 result = proxy.read(ids, self.fields.keys()+['__last_update'], ctx)
603+
604 self._update_concurrency_info(self.model, result)
605 self.concurrency_info = ConcurrencyInfo(self.model, ids)
606 if self.color_field:
607@@ -310,7 +315,6 @@
608 return result
609
610 def get_event_widget(self, event):
611-
612 title = '' # the title
613 description = [] # the description
614 if self.info_fields:
615@@ -377,19 +381,37 @@
616
617 color_key = event.get(self.color_field)
618 color = self.colors.get(color_key)
619+ classes = self._get_classes(event)
620
621 title = title.strip()
622 description = ', '.join(description).strip()
623 if isinstance(event['id'], int):
624 event_log = rpc.session.execute('object', 'execute', self.model, 'perm_read', [event['id']])[0]
625-
626+
627 event['create_date'] = event_log['create_date']
628 event['create_uid'] = event_log['create_uid'][1]
629 if isinstance(event_log['write_uid'], tuple):
630 event_log['write_uid'] = event_log['write_uid'][1]
631 event['write_uid'] = event_log['write_uid']
632 event['write_date'] = event_log['write_date']
633- return TinyEvent(event, starts, ends, title, description, dayspan=span, color=(color or None) and color[-1])
634+ return TinyEvent(event, starts, ends, title, description, dayspan=span, color=(color or None) and color[-1], classes=classes)
635+
636+ def _get_classes(self, event):
637+ """Get css classes which handle movable and/or resizable events"""
638+ classes = []
639+
640+ event_state = event.get('state')
641+ if event_state:
642+ # check if that event cannot be moved:
643+ if self.date_start and dict(self.fields[self.date_start].get('states', {}).get(event_state, [])).get('readonly'):
644+ classes.append('event-is-not-movable')
645+ # check if that event cannot be resized:
646+ date_to_check = self.date_stop or self.date_delay
647+ if date_to_check:
648+ if dict(self.fields[date_to_check].get('states', {}).get(event_state, [])).get('readonly'):
649+ classes.append('event-is-not-resizeable')
650+
651+ return classes
652
653
654 class TinyCalendar(ICalendar):
655
656=== modified file 'addons/view_calendar/widgets/templates/day.mako'
657--- addons/view_calendar/widgets/templates/day.mako 2010-10-21 15:25:44 +0000
658+++ addons/view_calendar/widgets/templates/day.mako 2010-10-27 09:48:00 +0000
659@@ -63,7 +63,7 @@
660 nWriteDate="${evt.write_date}"
661 nWriteId="${evt.write_uid}"
662 style="background-color: ${evt.color}"
663- class="calEvent allDay">${evt.title}</div>
664+ class="calEvent allDay ${evt.classes}">${evt.title}</div>
665 % endif
666 % endfor
667 </div>
668@@ -79,7 +79,7 @@
669 nWriteDate="${evt.write_date}"
670 nWriteId="${evt.write_uid}"
671 style="background-color: ${evt.color}"
672- class="calEvent noAllDay">
673+ class="calEvent noAllDay ${evt.classes}">
674 <div style="height: 10px;" class="calEventTitle">${evt.starts.strftime('%I:%M %P')} - ${evt.title}</div>
675 <div class="calEventDesc">${evt.description}</div>
676 <div class="calEventGrip"></div>
677
678=== modified file 'addons/view_calendar/widgets/templates/gantt.mako'
679--- addons/view_calendar/widgets/templates/gantt.mako 2010-10-21 15:25:44 +0000
680+++ addons/view_calendar/widgets/templates/gantt.mako 2010-10-27 09:48:00 +0000
681@@ -68,7 +68,7 @@
682 />
683 % endfor
684 % for evt in events:
685- <div class="calEvent"
686+ <div class="calEvent ${evt.classes}"
687 nRecordID="${evt.record_id}"
688 nDaySpan="${evt.dayspan}"
689 dtStart="${str(evt.starts)}"
690
691=== modified file 'addons/view_calendar/widgets/templates/month.mako'
692--- addons/view_calendar/widgets/templates/month.mako 2010-10-21 15:25:44 +0000
693+++ addons/view_calendar/widgets/templates/month.mako 2010-10-27 09:48:00 +0000
694@@ -51,7 +51,7 @@
695 <div id="calBodySect">
696 % for evt in events:
697 % if evt.dayspan > 0:
698- <div class="calEvent"
699+ <div class="calEvent ${evt.classes}"
700 nRecordID="${evt.record_id}"
701 nDaySpan="${evt.dayspan}"
702 dtStart="${str(evt.starts)}"
703@@ -64,7 +64,7 @@
704 style="background-color: ${evt.color}">${evt.title}</div>
705 % endif
706 % if evt.dayspan == 0:
707- <div class="calEvent calEventInfo"
708+ <div class="calEvent calEventInfo ${evt.classes}"
709 nRecordID="${evt.record_id}"
710 nDaySpan="${evt.dayspan}"
711 dtStart="${str(evt.starts)}"
712
713=== modified file 'addons/view_calendar/widgets/templates/week.mako'
714--- addons/view_calendar/widgets/templates/week.mako 2010-10-21 15:25:44 +0000
715+++ addons/view_calendar/widgets/templates/week.mako 2010-10-27 09:48:00 +0000
716@@ -51,33 +51,33 @@
717 <div id="calAllDaySect">
718 % for evt in events:
719 % if evt.dayspan > 0:
720- <div nRecordID="${evt.record_id}"
721- nDaySpan="${evt.dayspan}"
722- dtStart="${str(evt.starts)}"
723- dtEnd="${str(evt.ends)}"
724+ <div nRecordID="${evt.record_id}"
725+ nDaySpan="${evt.dayspan}"
726+ dtStart="${str(evt.starts)}"
727+ dtEnd="${str(evt.ends)}"
728 title="${evt.description}"
729 nCreationDate="${evt.create_date}"
730 nCreationId="${evt.create_uid}"
731 nWriteDate="${evt.write_date}"
732- nWriteId="${evt.write_uid}"
733- style="background-color: ${evt.color};"
734- class="calEvent allDay">${evt.title}</div>
735+ nWriteId="${evt.write_uid}"
736+ style="background-color: ${evt.color};"
737+ class="calEvent allDay ${evt.classes}">${evt.title}</div>
738 % endif
739 % endfor
740 </div>
741 <div id="calBodySect">
742 % for evt in events:
743 % if evt.dayspan == 0:
744- <div nRecordID="${evt.record_id}"
745- dtStart="${str(evt.starts)}"
746+ <div nRecordID="${evt.record_id}"
747+ dtStart="${str(evt.starts)}"
748 dtEnd="${str(evt.ends)}"
749 nCreationDate="${evt.create_date}"
750 nCreationId="${evt.create_uid}"
751 nWriteDate="${evt.write_date}"
752 nWriteId="${evt.write_uid}"
753- style="background-color: ${evt.color};"
754- class="calEvent noAllDay">
755- <div style="height: 10px;" class="calEventTitle">${evt.starts.strftime('%I:%M %P')} - ${evt.title}</div>
756+ style="background-color: ${evt.color};"
757+ class="calEvent noAllDay ${evt.classes}">
758+ <div style="height: 10px;" class="calEventTitle">${evt.starts.strftime('%H:%M')} - ${evt.title}</div>
759 <div class="calEventDesc">${evt.description}</div>
760 <div class="calEventGrip"></div>
761 </div>
762
763=== modified file 'addons/view_calendar/widgets/widgets.py'
764--- addons/view_calendar/widgets/widgets.py 2010-10-25 12:26:19 +0000
765+++ addons/view_calendar/widgets/widgets.py 2010-10-27 09:48:00 +0000
766@@ -142,8 +142,6 @@
767 self.month = Month(y, m)
768 self.events = self.get_events(self.month.days)
769
770-
771-
772 self.selected_day = _get_selection_day(Day(y, m, 1), self.selected_day, 'month')
773
774 minical = MiniCalendar(self.selected_day)