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 @@
+
+
+
+
+
+
+
+
+
+
+
+