diff --git a/web_view_calendar_list/README.rst b/web_view_calendar_list/README.rst new file mode 100644 index 000000000..cd53324e2 --- /dev/null +++ b/web_view_calendar_list/README.rst @@ -0,0 +1,84 @@ +====================== +Web View Calendar List +====================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github + :target: https://github.com/OCA/web/tree/11.0/web_view_calendar_list + :alt: OCA/web +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/web-11-0/web-11-0-web_view_calendar_list + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/162/11.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds a new view type that can be used to show calendars as lists. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +Create a new view using the calendar_list tag. +It has the same options than calendar:: + + + + + + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Creu Blanca + +Contributors +~~~~~~~~~~~~ + +* Enric Tobella + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/web `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/web_view_calendar_list/__init__.py b/web_view_calendar_list/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/web_view_calendar_list/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/web_view_calendar_list/__manifest__.py b/web_view_calendar_list/__manifest__.py new file mode 100644 index 000000000..3740f21c6 --- /dev/null +++ b/web_view_calendar_list/__manifest__.py @@ -0,0 +1,18 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Web View Calendar List', + 'summary': """ + Show calendars as a List""", + 'version': '11.0.1.0.0', + 'license': 'AGPL-3', + 'author': 'Creu Blanca,Odoo Community Association (OCA)', + 'website': 'https://github.com/OCA/web', + 'depends': [ + 'web', + ], + 'data': [ + 'templates/assets.xml', + ], +} diff --git a/web_view_calendar_list/models/__init__.py b/web_view_calendar_list/models/__init__.py new file mode 100644 index 000000000..8f9417a18 --- /dev/null +++ b/web_view_calendar_list/models/__init__.py @@ -0,0 +1,2 @@ +from . import ir_actions_act_window_view +from . import ir_ui_view diff --git a/web_view_calendar_list/models/ir_actions_act_window_view.py b/web_view_calendar_list/models/ir_actions_act_window_view.py new file mode 100644 index 000000000..43f8729a1 --- /dev/null +++ b/web_view_calendar_list/models/ir_actions_act_window_view.py @@ -0,0 +1,12 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class IrActionsActWindowView(models.Model): + _inherit = 'ir.actions.act_window.view' + + view_mode = fields.Selection( + selection_add=[('calendar_list', 'Calendar List')] + ) diff --git a/web_view_calendar_list/models/ir_ui_view.py b/web_view_calendar_list/models/ir_ui_view.py new file mode 100644 index 000000000..f1daf1af4 --- /dev/null +++ b/web_view_calendar_list/models/ir_ui_view.py @@ -0,0 +1,12 @@ +# Copyright 2020 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class IrUiView(models.Model): + _inherit = 'ir.ui.view' + + type = fields.Selection( + selection_add=[('calendar_list', 'Calendar List')] + ) diff --git a/web_view_calendar_list/readme/CONTRIBUTORS.rst b/web_view_calendar_list/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..93ec993e0 --- /dev/null +++ b/web_view_calendar_list/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Enric Tobella diff --git a/web_view_calendar_list/readme/DESCRIPTION.rst b/web_view_calendar_list/readme/DESCRIPTION.rst new file mode 100644 index 000000000..893ea1fcc --- /dev/null +++ b/web_view_calendar_list/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module adds a new view type that can be used to show calendars as lists. diff --git a/web_view_calendar_list/readme/USAGE.rst b/web_view_calendar_list/readme/USAGE.rst new file mode 100644 index 000000000..164227198 --- /dev/null +++ b/web_view_calendar_list/readme/USAGE.rst @@ -0,0 +1,7 @@ +Create a new view using the calendar_list tag. +It has the same options than calendar:: + + + + + diff --git a/web_view_calendar_list/static/description/icon.png b/web_view_calendar_list/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/web_view_calendar_list/static/description/icon.png differ diff --git a/web_view_calendar_list/static/src/js/calendar_list_controller.js b/web_view_calendar_list/static/src/js/calendar_list_controller.js new file mode 100644 index 000000000..18eb05206 --- /dev/null +++ b/web_view_calendar_list/static/src/js/calendar_list_controller.js @@ -0,0 +1,11 @@ +odoo.define('web_view_calendar_list.CalendarListController', function (require) { + "use strict"; + + var CalendarController = require('web.CalendarController'); + + var CalendarListController = CalendarController.extend({ + }); + + return CalendarListController; + +}); diff --git a/web_view_calendar_list/static/src/js/calendar_list_model.js b/web_view_calendar_list/static/src/js/calendar_list_model.js new file mode 100644 index 000000000..d2fcb9d78 --- /dev/null +++ b/web_view_calendar_list/static/src/js/calendar_list_model.js @@ -0,0 +1,59 @@ +odoo.define('web_view_calendar_list.CalendarListModel', function (require) { + "use strict"; + + var CalendarModel = require('web.CalendarModel'); + + var AppointmentModel= CalendarModel.extend({ + _recordToCalendarEvent: function (evt) { + var date_start = false; + var date_stop = false; + var date_delay = evt[this.mapping.date_delay] || 1.0, + all_day = this.fields[ + this.mapping.date_start + ].type === 'date' || this.mapping.all_day && + evt[this.mapping.all_day] || false, + the_title = '', + attendees = []; + + if (!all_day) { + date_start = evt[this.mapping.date_start].clone(); + date_stop = this.mapping.date_stop ? evt[ + this.mapping.date_stop].clone() : null; + } else { + date_start = evt[this.mapping.date_start].clone().startOf( + 'day'); + date_stop = this.mapping.date_stop ? evt[ + this.mapping.date_stop].clone().startOf('day') : null; + } + + if (!date_stop && date_delay) { + date_stop = date_start.clone().add(date_delay, 'hours'); + } + + if (!all_day) { + date_start.add( + this.getSession().getTZOffset(date_start), 'minutes'); + date_stop.add( + this.getSession().getTZOffset(date_stop), 'minutes'); + } + + if (this.mapping.all_day && evt[this.mapping.all_day]) { + date_stop.add(1, 'days'); + } + return { + 'record': evt, + 'start': date_start, + 'end': date_stop, + 'r_start': date_start, + 'r_end': date_stop, + 'title': the_title, + 'allDay': all_day, + 'id': evt.id, + 'attendees':attendees, + }; + }, + }); + + return AppointmentModel; + +}); diff --git a/web_view_calendar_list/static/src/js/calendar_list_renderer.js b/web_view_calendar_list/static/src/js/calendar_list_renderer.js new file mode 100644 index 000000000..a135c24b9 --- /dev/null +++ b/web_view_calendar_list/static/src/js/calendar_list_renderer.js @@ -0,0 +1,159 @@ +odoo.define('web_view_calendar_list.CalendarListRenderer', function (require) { + "use strict"; + + var CalendarRenderer = require('web.CalendarRenderer'); + var session = require('web.session'); + var core = require('web.core'); + var qweb = core.qweb; + var _t = core._t; + + var scales = { + day: 'listDay', + week: 'listWeek', + month: 'listMonth' + }; + + var AppointmentRenderer= CalendarRenderer.extend({ + _initCalendar: function () { + var self = this; + + this.$calendar = this.$(".o_calendar_widget"); + + // This seems like a workaround but apparently passing the locale + // in the options is not enough. We should initialize it beforehand + var locale = moment.locale(); + $.fullCalendar.locale(locale); + + //Documentation here : http://arshaw.com/fullcalendar/docs/ + var fc_options = $.extend({}, this.state.fc_options, { + eventDrop: function (event) { + self.trigger_up('dropRecord', event); + }, + eventResize: function (event) { + self.trigger_up('updateRecord', event); + }, + eventClick: function (event) { + self.trigger_up('openEvent', event); + self.$calendar.fullCalendar('unselect'); + }, + select: function (target_date, end_date, event, _js_event, _view) { + var data = {'start': target_date, 'end': end_date}; + if (self.state.context.default_name) { + data.title = self.state.context.default_name; + } + self.trigger_up('openCreate', data); + self.$calendar.fullCalendar('unselect'); + }, + eventRender: function (event, element) { + var $render = $(self._eventRender(event)); + event.title = $render.find('.o_field_type_char:first').text(); + element.find('.fc-list-item-title').html($render.html()); + element.addClass($render.attr('class')); + var display_hour = ''; + if (!event.allDay) { + var start = event.r_start || event.start; + var end = event.r_end || event.end; + var timeFormat = _t.database.parameters.time_format.search("%H") != -1 ? 'HH:mm': 'h:mma'; + display_hour = start.format(timeFormat) + ' - ' + end.format(timeFormat); + if (display_hour === '00:00 - 00:00') { + display_hour = _t('All day'); + } + } + element.find('.fc-list-item-time').text(display_hour); + }, + // Dirty hack to ensure a correct first render + eventAfterAllRender: function () { + $(window).trigger('resize'); + }, + viewRender: function (view) { + // compute mode from view.name which is either 'month', 'agendaWeek' or 'agendaDay' + var mode = view.name === 'listMonth' ? 'month' : (view.name === 'listWeek' ? 'week' : 'day'); + // compute title: in week mode, display the week number + var title = mode === 'week' ? view.intervalStart.week() : view.title; + self.trigger_up('viewUpdated', { + mode: mode, + title: title, + }); + }, + height: 'parent', + unselectAuto: false, + locale: locale, + // reset locale when fullcalendar has already been instanciated before now + }); + + this.$calendar.fullCalendar(fc_options); + }, + /* + We need to overwrite it in order make a change on the state variable + that is not dependant on the class, so we cannot modify it without + overwriting all the class + */ + _render: function () { + var $calendar = this.$calendar; + var $fc_view = $calendar.find('.fc-view'); + var scrollPosition = $fc_view.scrollLeft(); + var scrollTop = this.$calendar.find('.fc-scroller').scrollTop(); + + $fc_view.scrollLeft(0); + $calendar.fullCalendar('unselect'); + + if (scales[this.state.scale] !== $calendar.data('fullCalendar').getView().type) { + $calendar.fullCalendar('changeView', scales[this.state.scale]); + } + + if (this.target_date !== this.state.target_date.toString()) { + $calendar.fullCalendar( + 'gotoDate', moment(this.state.target_date)); + this.target_date = this.state.target_date.toString(); + } + + this.$small_calendar.datepicker( + "setDate", this.state.highlight_date.toDate() + ).find('.o_selected_range').removeClass('o_color o_selected_range'); + var $a = false; + switch (this.state.scale) { + case 'month': + $a = this.$small_calendar.find('td a'); + break; + case 'week': + $a = this.$small_calendar.find( + 'tr:has(.ui-state-active) a'); + break; + case 'day': + $a = this.$small_calendar.find('a.ui-state-active'); + break; + } + $a.addClass('o_selected_range'); + setTimeout(function () { + $a.not('.ui-state-active').addClass('o_color'); + }); + + $fc_view.scrollLeft(scrollPosition); + + var fullWidth = this.state.fullWidth; + this.$('.o_calendar_sidebar_toggler') + .toggleClass('fa-close', !fullWidth) + .toggleClass('fa-chevron-left', fullWidth) + .attr( + 'title', + !fullWidth ? _('Close Sidebar') : _('Open Sidebar')); + this.$sidebar_container.toggleClass('o_sidebar_hidden', fullWidth); + this.$sidebar.toggleClass('o_hidden', fullWidth); + + this._renderFilters(); + this.$calendar.appendTo('body'); + if (scrollTop) { + this.$calendar.fullCalendar('reinitView'); + } else { + this.$calendar.fullCalendar('render'); + } + this._renderEvents(); + this.$calendar.prependTo(this.$('.o_calendar_view')); + + return $.when(); + }, + }); + + return AppointmentRenderer; + +}); diff --git a/web_view_calendar_list/static/src/js/calendar_list_view.js b/web_view_calendar_list/static/src/js/calendar_list_view.js new file mode 100644 index 000000000..280b20ec1 --- /dev/null +++ b/web_view_calendar_list/static/src/js/calendar_list_view.js @@ -0,0 +1,30 @@ +odoo.define('web_view_calendar_list.CalendarListView', function (require) { + "use strict"; + + var CalendarView = require('web.CalendarView'); + var core = require('web.core'); + var CalendarListController = require( + 'web_view_calendar_list.CalendarListController'); + var CalendarListModel = require('web_view_calendar_list.CalendarListModel'); + var CalendarListRenderer = require( + 'web_view_calendar_list.CalendarListRenderer'); + var view_registry = require('web.view_registry'); + + var _lt = core._lt; + + var CalendarListView = CalendarView.extend({ + display_name: _lt('Calendar List'), + icon: 'fa-calendar-check-o', + config: { + Model: CalendarListModel, + Controller: CalendarListController, + Renderer: CalendarListRenderer, + }, + }); + + view_registry + .add('calendar_list', CalendarListView); + + return CalendarListView; + +}); diff --git a/web_view_calendar_list/templates/assets.xml b/web_view_calendar_list/templates/assets.xml new file mode 100644 index 000000000..4431686d6 --- /dev/null +++ b/web_view_calendar_list/templates/assets.xml @@ -0,0 +1,14 @@ + + + +