diff --git a/addons/website_event_meet/__init__.py b/addons/website_event_meet/__init__.py index 67dee8c60db..7d34c7c054a 100644 --- a/addons/website_event_meet/__init__.py +++ b/addons/website_event_meet/__init__.py @@ -1,2 +1,5 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import controllers +from . import models diff --git a/addons/website_event_meet/__manifest__.py b/addons/website_event_meet/__manifest__.py index 2856e3e88f9..ee97ff02819 100644 --- a/addons/website_event_meet/__manifest__.py +++ b/addons/website_event_meet/__manifest__.py @@ -11,11 +11,23 @@ 'website': 'https://www.odoo.com/page/events', 'description': "", 'depends': [ + # STD-TODO: remove 'website_event_track_online' we just need it to be + # able to use 'website.event.menu' which is defined in 'website_event_track' + # as well as even form view defined in track_online 'website_event_online', + 'website_event_track_online', + 'website_jitsi', ], + 'demo': ['data/website_event_meet_demo.xml'], 'data': [ - ], - 'demo': [ + 'security/ir.model.access.csv', + 'security/security.xml', + 'views/assets.xml', + 'views/event_meet_templates_list.xml', + 'views/event_meet_templates_page.xml', + 'views/event_meeting_room_views.xml', + 'views/event_event_views.xml', + 'views/event_type_views.xml', ], 'application': False, 'installable': True, diff --git a/addons/website_event_meet/controllers/__init__.py b/addons/website_event_meet/controllers/__init__.py new file mode 100644 index 00000000000..e6526380f26 --- /dev/null +++ b/addons/website_event_meet/controllers/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import community +from . import website_event_main diff --git a/addons/website_event_meet/controllers/main.py b/addons/website_event_meet/controllers/main.py new file mode 100644 index 00000000000..c653b9fa9f9 --- /dev/null +++ b/addons/website_event_meet/controllers/main.py @@ -0,0 +1,162 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import logging +from werkzeug.exceptions import Forbidden, NotFound +from werkzeug.utils import redirect + +from odoo import exceptions, http +from odoo.http import request +from odoo.addons.http_routing.models.ir_http import slug +from odoo.addons.website_event_track_online.controllers.community import WebsiteEventCommunityController +from odoo.osv import expression + +_logger = logging.getLogger(__name__) + + +class WebsiteEventMeetController(WebsiteEventCommunityController): + + def _get_event_rooms_base_domain(self, event): + search_domain_base = [('event_id', '=', event.id), ('is_published', '=', True)] + return search_domain_base + + def _sort_event_rooms(self, room): + return (room.is_pinned, room.room_last_activity, room.id) + + # ------------------------------------------------------------ + # MAIN PAGE + # ------------------------------------------------------------ + + @http.route(["/event//community"], type="http", + auth="public", website=True, sitemap=True) + def community(self, event, page=1, lang=None, **kwargs): + """Display the meeting rooms of the event on the frontend side. + + :param event: event for which we display the meeting rooms + :param lang: lang id used to perform a search + """ + if not event.can_access_from_current_website(): + raise Forbidden() + + return request.render( + "website_event_meet.event_meet", + self._event_meeting_rooms_get_values(event, lang=lang) + ) + + def _event_meeting_rooms_get_values(self, event, lang=None): + search_domain = self._get_event_rooms_base_domain(event) + meeting_rooms_all = request.env['event.meeting.room'].sudo().search(search_domain) + if lang: + search_domain = expression.AND([ + search_domain, + [('room_lang_id', '=', int(lang))] + ]) + meeting_rooms = request.env['event.meeting.room'].sudo().search(search_domain) + meeting_rooms = meeting_rooms.sorted(self._sort_event_rooms, reverse=True) + + is_event_manager = request.env.user.has_group("event.group_event_manager") + if not is_event_manager: + meeting_rooms = meeting_rooms.filtered(lambda m: not m.room_is_full) + + visitor = request.env['website.visitor']._get_visitor_from_request() + + return { + # event information + "event": event.sudo(), + 'main_object': event, + # rooms + "meeting_rooms": meeting_rooms, + "current_lang": request.env["res.lang"].browse(int(lang)) if lang else False, + "available_languages": meeting_rooms_all.mapped("room_lang_id"), + "default_lang_code": request.context.get('lang', request.env.user.lang), + "default_username": visitor.display_name if visitor else None, + # environment + "is_event_manager": is_event_manager, + } + + @http.route("/event//meeting_room_create", + type="http", auth="public", methods=["POST"], website=True) + def create_meeting_room(self, event, **post): + if not event or not event.can_access_from_current_website() or not event.is_published or not event.meeting_room_allow_creation: + raise Forbidden() + + name = post.get("name") + summary = post.get("summary") + target_audience = post.get("audience") + lang_code = post.get("lang_code") + max_capacity = post.get("capacity") + + # get the record to be sure they really exist + lang = request.env["res.lang"].search([("code", "=", lang_code)], limit=1) + + if not lang or max_capacity == "no_limit": + raise Forbidden() + + meeting_room = request.env["event.meeting.room"].sudo().create({ + "name": name, + "summary": summary, + "target_audience": target_audience, + "is_pinned": False, + "event_id": event.id, + "room_lang_id": lang.id, + "room_max_capacity": max_capacity, + "is_published": True, + }) + _logger.info("New meeting room (%s) created by %s (uid %s)" % (name, request.httprequest.remote_addr, request.env.uid)) + + return redirect(f"/event/{slug(event)}/meeting_room/{slug(meeting_room)}") + + @http.route(["/event/active_langs"], type="json", auth="public") + def active_langs(self): + return request.env["res.lang"].sudo().get_installed() + + # ------------------------------------------------------------ + # ROOM PAGE VIEW + # ------------------------------------------------------------ + + @http.route("/event//meeting_room/", + type="http", auth="public", website=True, sitemap=True) + def event_meeting_room_page(self, event, meeting_room, **post): + """Display the meeting room frontend view. + + :param event: Event for which we display the meeting rooms + :param meeting_room: Meeting Room to display + """ + if not event.can_access_from_current_website() or meeting_room not in event.sudo().meeting_room_ids: + raise NotFound() + + try: + meeting_room.check_access_rule('read') + except exceptions.AccessError: + raise Forbidden() + + meeting_room = meeting_room.sudo() + + return request.render( + "website_event_meet.event_meet_main", + self._event_meeting_room_page_get_values(event, meeting_room), + ) + + def _event_meeting_room_page_get_values(self, event, meeting_room): + # search for meeting room list + meeting_rooms_other = request.env['event.meeting.room'].sudo().search([ + ('event_id', '=', event.id), ('id', '!=', meeting_room.id), ('is_published', '=', True), + ]) + + if not request.env.user.has_group("event.group_event_manager"): + # only the event manager can see meeting rooms which are full + meeting_rooms_other = meeting_rooms_other.filtered(lambda m: not m.room_is_full) + + meeting_rooms_other = meeting_rooms_other.sorted(self._sort_event_rooms, reverse=True) + + return { + # event information + 'event': event, + 'main_object': meeting_room, + 'meeting_room': meeting_room, + # sidebar + 'meeting_rooms_other': meeting_rooms_other, + # options + 'option_widescreen': True, + 'is_event_manager': request.env.user.has_group('event.group_event_manager'), + } diff --git a/addons/website_event_meet/controllers/website_event_main.py b/addons/website_event_meet/controllers/website_event_main.py new file mode 100644 index 00000000000..f395ca09e3f --- /dev/null +++ b/addons/website_event_meet/controllers/website_event_main.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from babel.dates import format_datetime + +from odoo import _ +from odoo.http import request +from odoo.addons.website_event.controllers.main import WebsiteEventController + + +class WebsiteEventController(WebsiteEventController): + def _prepare_event_register_values(self, event, **post): + values = super(WebsiteEventController, self)._prepare_event_register_values(event, **post) + + if "from_room_id" in post and not event.is_ongoing: + meeting_room = request.env["event.meeting.room"].browse(int(post["from_room_id"])).sudo().exists() + if meeting_room and meeting_room.is_published: + date_begin = format_datetime(event.with_context(tz=event.date_tz).date_begin, format="medium") + + values["toast_message"] = ( + _('The event %s starts at %s (%s). \nJoin us there to chat about "%s" !') + % (event.name, date_begin, event.date_tz, meeting_room.name) + ) + + return values diff --git a/addons/website_event_meet/data/website_event_meet_demo.xml b/addons/website_event_meet/data/website_event_meet_demo.xml new file mode 100644 index 00000000000..b492f898263 --- /dev/null +++ b/addons/website_event_meet/data/website_event_meet_demo.xml @@ -0,0 +1,42 @@ + + + + + Best wood for furniture + Let's talk about wood types for furniture + wood expert(s) + + + + + 12 + 9 + + + Reducing the ecological footprint with wood ? + Share your tips to reduce your ecological footprint using wood. + ecologist(s) + + + + + 8 + 8 + + + Vos meubles préférés ? + Venez partager vos meubles préférés et l'utilisation que vous en faites. + client(s) + + + + + 8 + 3 + + + + + + + diff --git a/addons/website_event_meet/models/__init__.py b/addons/website_event_meet/models/__init__.py new file mode 100644 index 00000000000..8f975bbef96 --- /dev/null +++ b/addons/website_event_meet/models/__init__.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import event_event +from . import event_type +from . import event_meeting_room +from . import ir_autovacuum +from . import website_event_menu diff --git a/addons/website_event_meet/models/event_event.py b/addons/website_event_meet/models/event_event.py new file mode 100644 index 00000000000..4b254e91dbb --- /dev/null +++ b/addons/website_event_meet/models/event_event.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models, _ +from odoo.addons.http_routing.models.ir_http import slug + + +class Event(models.Model): + _inherit = "event.event" + + meeting_room_ids = fields.One2many("event.meeting.room", "event_id", string="Meeting rooms") + meeting_room_count = fields.Integer("Room count", compute="_compute_meeting_room_count") + meeting_room_allow_creation = fields.Boolean( + "Allow Room Creation", compute="_compute_meeting_room_allow_creation", + readonly=False, store=True, + help="Let Visitors Create Rooms") + + @api.depends("meeting_room_ids") + def _compute_meeting_room_count(self): + meeting_room_count = self.env["event.meeting.room"].sudo().read_group( + domain=[("event_id", "in", self.ids)], + fields=["id:count"], + groupby=["event_id"], + ) + + meeting_room_count = { + result["event_id"][0]: result["event_id_count"] + for result in meeting_room_count + } + + for event in self: + event.meeting_room_count = meeting_room_count.get(event.id, 0) + + @api.depends("event_type_id", "community_menu", "meeting_room_allow_creation") + def _compute_meeting_room_allow_creation(self): + for event in self: + if event.event_type_id and event.event_type_id != event._origin.event_type_id: + event.meeting_room_allow_creation = event.event_type_id.meeting_room_allow_creation + elif event.community_menu and event.community_menu != event._origin.community_menu: + event.meeting_room_allow_creation = True + elif not event.community_menu or not event.meeting_room_allow_creation: + event.meeting_room_allow_creation = False diff --git a/addons/website_event_meet/models/event_meeting_room.py b/addons/website_event_meet/models/event_meeting_room.py new file mode 100644 index 00000000000..cad8bc78936 --- /dev/null +++ b/addons/website_event_meet/models/event_meeting_room.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from werkzeug.urls import url_join + +from odoo import api, fields, models, _ +from odoo.addons.http_routing.models.ir_http import slug + + +class EventMeetingRoom(models.Model): + _name = "event.meeting.room" + _description = "Event Meeting Room" + _order = "is_pinned DESC, id" + _inherit = [ + 'chat.room.mixin', + 'website.published.mixin', + ] + + name = fields.Char("Topic", required=True, translate=True) + active = fields.Boolean('Active', default=True) + is_published = fields.Boolean(copy=True) # make the inherited field copyable + event_id = fields.Many2one("event.event", string="Event", required=True, ondelete="cascade") + is_pinned = fields.Boolean("Is Pinned") + summary = fields.Char("Summary") + target_audience = fields.Char("Audience", translate=True) + + # TDE FIXME: merge with mixin code + ROOM_CONFIG_FIELDS = { + 'room_name': 'name', + 'room_lang_id': 'lang_id', + 'room_max_capacity': 'max_capacity', + 'room_participant_count': 'participant_count', + } + + @api.depends('name', 'event_id.name') + def _compute_website_url(self): + super(EventMeetingRoom, self)._compute_website_url() + for meeting_room in self: + if meeting_room.id: + base_url = meeting_room.event_id.get_base_url() + meeting_room.website_url = '%s/event/%s/meeting_room/%s' % (base_url, slug(meeting_room.event_id), slug(meeting_room)) + + @api.model_create_multi + def create(self, values_list): + # TDE FIXME: merge with mixin code + for values in values_list: + if not values.get("chat_room_id"): + # be sure to always create a `chat.room` for each `event.meeting.room` + chat_room = self.env["chat.room"].create({ + self.ROOM_CONFIG_FIELDS[field]: value + for field, value in dict(values).items() + if field in self.ROOM_CONFIG_FIELDS and values.pop(field) + }) + + values["chat_room_id"] = chat_room.id + + values["name"] = values["name"].capitalize() + values["target_audience"] = (values.get("target_audience") or _("Attendee(s)")).capitalize() + + return super(EventMeetingRoom, self).create(values_list) diff --git a/addons/website_event_meet/models/event_type.py b/addons/website_event_meet/models/event_type.py new file mode 100644 index 00000000000..4272de467b4 --- /dev/null +++ b/addons/website_event_meet/models/event_type.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models + + +class EventType(models.Model): + _inherit = "event.type" + + meeting_room_allow_creation = fields.Boolean( + "Allow Room Creation", compute='_compute_meeting_room_allow_creation', + readonly=False, store=True, + help="Let Visitors Create Rooms") + + @api.depends('community_menu') + def _compute_meeting_room_allow_creation(self): + for event_type in self: + event_type.meeting_room_allow_creation = event_type.community_menu diff --git a/addons/website_event_meet/models/ir_autovacuum.py b/addons/website_event_meet/models/ir_autovacuum.py new file mode 100644 index 00000000000..11907a0d5ac --- /dev/null +++ b/addons/website_event_meet/models/ir_autovacuum.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import datetime + +from odoo import api, models, fields + + +class AutoVacuum(models.AbstractModel): + _inherit = 'ir.autovacuum' + + _DELAY_CLEAN = datetime.timedelta(hours=4) + + @api.model + def power_on(self, *args, **kwargs): + """Archive all non-pinned room with 0 participant if nobody has joined it for a moment.""" + self.env["event.meeting.room"].sudo().search([ + ("is_pinned", "=", False), + ("active", "=", True), + ("room_participant_count", "=", 0), + ("room_last_activity", "<", fields.Datetime.now() - self._DELAY_CLEAN), + ]).active = False + + return super(AutoVacuum, self).power_on(*args, **kwargs) diff --git a/addons/website_event_meet/models/website_event_menu.py b/addons/website_event_meet/models/website_event_menu.py new file mode 100644 index 00000000000..827d3bc1fa4 --- /dev/null +++ b/addons/website_event_meet/models/website_event_menu.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import fields, models + + +class EventMenu(models.Model): + _inherit = "website.event.menu" + + menu_type = fields.Selection(selection_add=[("meeting_room", "Event Meeting Room Menus")]) diff --git a/addons/website_event_meet/security/ir.model.access.csv b/addons/website_event_meet/security/ir.model.access.csv new file mode 100644 index 00000000000..eeae56fb6a0 --- /dev/null +++ b/addons/website_event_meet/security/ir.model.access.csv @@ -0,0 +1,6 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +event_meeting_room_access_all,event.meeting.room.access.all,model_event_meeting_room,,1,0,0,0 +event_meeting_room_access_user,event.meeting.room.access.user,model_event_meeting_room,event.group_event_user,1,0,0,0 +event_meeting_room_access_manager,event.meeting.room.access.manager,model_event_meeting_room,event.group_event_manager,1,1,1,1 +chat_room_access_event_user,chat.room.access.event.user,website_jitsi.model_chat_room,event.group_event_user,1,0,0,0 +chat_room_access_event_manager,chat.room.access.event.manager,website_jitsi.model_chat_room,event.group_event_manager,1,1,1,1 diff --git a/addons/website_event_meet/security/security.xml b/addons/website_event_meet/security/security.xml new file mode 100644 index 00000000000..e4680042533 --- /dev/null +++ b/addons/website_event_meet/security/security.xml @@ -0,0 +1,15 @@ + + + + + Event Meeting Room: public/portal published only + + [('website_published', '=', True)] + + + + + + + + diff --git a/addons/website_event_meet/static/src/js/customize_options.js b/addons/website_event_meet/static/src/js/customize_options.js new file mode 100644 index 00000000000..4d8ccbd3ab7 --- /dev/null +++ b/addons/website_event_meet/static/src/js/customize_options.js @@ -0,0 +1,69 @@ +odoo.define('website_event_meet.set_customize_options', function (require) { +"use strict"; + +let EventSpecificOptions = require('website_event.set_customize_options').EventSpecificOptions; + +EventSpecificOptions.include({ + xmlDependencies: (EventSpecificOptions.prototype.xmlDependencies || []) + .concat([ + '/website_event_meet/static/src/xml/customize_options.xml', + ]), + + events: _.extend({}, EventSpecificOptions.prototype.events, { + 'change #allow-room-creation': '_onAllowRoomCreationChange', + }), + + start: function () { + this.$allowRoomCreationInput = this.$('#allow-room-creation'); + this._super.apply(this, arguments); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + _onAllowRoomCreationChange: function () { + let checkboxValue = this.$allowRoomCreationInput.is(':checked'); + this._toggleAllowRoomCreation(checkboxValue); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + _getCheckboxFields: function () { + let fields = this._super(); + fields = _.union(fields, ['meeting_room_allow_creation']); + return fields; + }, + + _getCheckboxFieldMatch: function (checkboxField) { + if (checkboxField === 'meeting_room_allow_creation') { + return this.$allowRoomCreationInput; + } + return this._super(checkboxField); + }, + + _initCheckboxCallback: function (rpcData) { + this._super(rpcData); + if (rpcData[0]['meeting_room_allow_creation']) { + let submenuInput = this._getCheckboxFieldMatch('meeting_room_allow_creation'); + submenuInput.attr('checked', 'checked'); + } + }, + + _toggleAllowRoomCreation: async function (val) { + await this._rpc({ + model: this.modelName, + method: 'write', + args: [[this.eventId], { + meeting_room_allow_creation: val + }], + }); + + this._reloadEventPage(); + }, + +}); + +}); diff --git a/addons/website_event_meet/static/src/js/website_event_create_meeting_room_button.js b/addons/website_event_meet/static/src/js/website_event_create_meeting_room_button.js new file mode 100644 index 00000000000..c64537a57a7 --- /dev/null +++ b/addons/website_event_meet/static/src/js/website_event_create_meeting_room_button.js @@ -0,0 +1,59 @@ +odoo.define('website_event_meet.website_event_create_room_button', function (require) { +'use strict'; + +const publicWidget = require('web.public.widget'); +const core = require('web.core'); +const QWeb = core.qweb; + +publicWidget.registry.websiteEventCreateMeetingRoom = publicWidget.Widget.extend({ + selector: '.o_wevent_create_room_button', + xmlDependencies: ['/website_event_meet/static/src/xml/website_event_meeting_room.xml'], + events: { + 'click': '_onClickCreate', + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + _onClickCreate: async function () { + if (!this.$createModal) { + const langs = await this._rpc({ + route: "/event/active_langs", + }); + + this.$createModal = $(QWeb.render( + 'event_meet_create_room_modal', + { + csrf_token: odoo.csrf_token, + eventId: this.$el.data("eventId"), + defaultLangCode: this.$el.data("defaultLangCode"), + langs: langs, + } + )); + + this.$createModal.appendTo(this.$el.parentNode); + } + + this.$createModal.modal('show'); + }, + + //-------------------------------------------------------------------------- + // Override + //-------------------------------------------------------------------------- + + /** + * Remove the create modal from the DOM, to avoid issue when editing the template + * with the website editor. + * + * @override + */ + destroy: function () { + $('.o_wevent_create_meeting_room_modal').remove(); + this._super.apply(this, arguments); + }, +}); + +return publicWidget.registry.websiteEventMeetingRoom; + +}); diff --git a/addons/website_event_meet/static/src/js/website_event_meeting_room.js b/addons/website_event_meet/static/src/js/website_event_meeting_room.js new file mode 100644 index 00000000000..774a186e9f7 --- /dev/null +++ b/addons/website_event_meet/static/src/js/website_event_meeting_room.js @@ -0,0 +1,111 @@ +odoo.define('website_event_meet.website_event_meet_meeting_room', function (require) { +'use strict'; + +const publicWidget = require('web.public.widget'); +const core = require('web.core'); +const Dialog = require('web.Dialog'); +const _t = core._t; + +publicWidget.registry.websiteEventMeetingRoom = publicWidget.Widget.extend({ + selector: '.o_wevent_meeting_room_card', + xmlDependencies: ['/website_event_meet/static/src/xml/website_event_meeting_room.xml'], + events: { + 'click .o_wevent_meeting_room_delete': '_onDeleteClick', + 'click .o_wevent_meeting_room_duplicate': '_onDuplicateClick', + 'click .o_wevent_meeting_room_is_pinned': '_onPinClick', + }, + + start: function () { + this._super.apply(this, arguments); + this.meetingRoomId = parseInt(this.$el.data('meeting-room-id')); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * Delete the meeting room. + * + * @private + */ + _onDeleteClick: async function (event) { + event.preventDefault(); + event.stopPropagation(); + + Dialog.confirm( + this, + _t("Are you sure you want to close this room ?"), + { + confirm_callback: async () => { + await this._rpc({ + model: 'event.meeting.room', + method: 'write', + args: [this.meetingRoomId, {is_published: false}], + context: this.context, + }); + + // remove the element so we do not need to refresh the page + this.$el.remove(); + } + }, + ); + }, + + /** + * Duplicate the room. + * + * @private + */ + _onDuplicateClick: function (event) { + event.preventDefault(); + event.stopPropagation(); + Dialog.confirm( + this, + _t("Are you sure you want to duplicate this room ?"), + { + confirm_callback: async () => { + await this._rpc({ + model: 'event.meeting.room', + method: 'copy', + args: [this.meetingRoomId], + context: this.context, + }); + + window.location.reload(); + } + }, + ); + }, + + /** + * Pin/unpin the room. + * + * @private + */ + _onPinClick: async function (event) { + event.preventDefault(); + event.stopPropagation(); + + const pinnedButtonClass = "o_wevent_meeting_room_pinned"; + const isPinned = event.currentTarget.classList.contains(pinnedButtonClass); + + await this._rpc({ + model: 'event.meeting.room', + method: 'write', + args: [this.meetingRoomId, {is_pinned: !isPinned}], + context: this.context, + }); + + // TDE FIXME: addclass ? + if (isPinned) { + event.currentTarget.classList.remove(pinnedButtonClass); + } else { + event.currentTarget.classList.add(pinnedButtonClass); + } + } +}); + +return publicWidget.registry.websiteEventMeetingRoom; + +}); diff --git a/addons/website_event_meet/static/src/scss/event_meet_templates.scss b/addons/website_event_meet/static/src/scss/event_meet_templates.scss new file mode 100644 index 00000000000..ddd9e030f3c --- /dev/null +++ b/addons/website_event_meet/static/src/scss/event_meet_templates.scss @@ -0,0 +1,59 @@ +.o_wemeet_index { + + /* + * COMMON + */ + + /* + * Meet Room PAGE + */ + + .o_wevent_online_page_container { + + // Jitsi container + #o_wemeet_jitsi_iframe { + height: 85vh; + } + } +} + +/* + * MEETING MODAL (JS / XML WIDGET) + */ + +.o_wevent_create_meeting_room_modal .modal-dialog { + top: 25%; + max-width: 500px; + height: auto; +} + +/* TDE FIXME: reorganize css cleanly */ +.o_wevent_meeting_room_is_pinned { + transition: 0.2s; + + // the icon must be filled with white when the meeting room is not pinned + color: white; + -webkit-text-stroke: 1px $o-main-text-color; + -webkit-text-fill-color: transparent; + + &.o_wevent_meeting_room_pinned { + // the icon must be filled with black when the meeting room is pinned + -webkit-text-fill-color: $o-main-text-color; + } +} + +.o_wevent_meeting_room_corner_ribbon { + width: 200px; + background: #e43; + top: 25px; + left: -50px; + text-align: center; + line-height: 50px; + letter-spacing: 1px; + color: #f0f0f0; + transform: rotate(-45deg); + -webkit-transform: rotate(-45deg); + z-index: 1; + position: absolute; + opacity: 0.6; +} diff --git a/addons/website_event_meet/static/src/xml/customize_options.xml b/addons/website_event_meet/static/src/xml/customize_options.xml new file mode 100644 index 00000000000..7be9b617bc9 --- /dev/null +++ b/addons/website_event_meet/static/src/xml/customize_options.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/addons/website_event_meet/static/src/xml/website_event_meeting_room.xml b/addons/website_event_meet/static/src/xml/website_event_meeting_room.xml new file mode 100644 index 00000000000..bb08735c4ee --- /dev/null +++ b/addons/website_event_meet/static/src/xml/website_event_meeting_room.xml @@ -0,0 +1,54 @@ + + + + + + diff --git a/addons/website_event_meet/tests/__init__.py b/addons/website_event_meet/tests/__init__.py new file mode 100644 index 00000000000..b2835ad5a69 --- /dev/null +++ b/addons/website_event_meet/tests/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import test_website_event_meet diff --git a/addons/website_event_meet/tests/test_website_event_meet.py b/addons/website_event_meet/tests/test_website_event_meet.py new file mode 100644 index 00000000000..becb51e85bf --- /dev/null +++ b/addons/website_event_meet/tests/test_website_event_meet.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.event.tests.common import TestEventCommon +from odoo.tests import Form + + +class TestWebsiteEventMeet(TestEventCommon): + def test_meeting_room_create(self): + """Test that the field of the mixin are automatically filled.""" + new_meeting_room_form = Form(self.env["event.meeting.room"]) + new_meeting_room_form.name = "Test name" + new_meeting_room_form.event_id = self.event_0 + new_meeting_room_form.target_audience = "dev" + new_meeting_room_form.room_max_capacity = "20" + meeting_room = new_meeting_room_form.save() + + self.assertTrue(meeting_room.chat_room_id) + self.assertEqual(meeting_room.chat_room_id.max_capacity, "20") + + # Try to create a meeting room without filling the related field + # It must create the `chat.room` with the default value + meeting_room = self.env["event.meeting.room"].create({ + "name": "Test 2", + "event_id": self.event_0.id, + "target_audience": "dev" + }) + + self.assertTrue(meeting_room.chat_room_id) + self.assertTrue(meeting_room.chat_room_id.name) + self.assertEqual(meeting_room.chat_room_id.max_capacity, "8") + + def test_meeting_room_copy(self): + """Test the duplication of the meeting room.""" + meeting_room_1 = self.env["event.meeting.room"].create({ + "name": "Test meeting room", + "event_id": self.event_0.id, + "target_audience": "dev", + "room_max_capacity": "20", + }) + + meeting_room_2 = meeting_room_1.copy() + + chat_room_1 = meeting_room_1.chat_room_id + chat_room_2 = meeting_room_2.chat_room_id + + self.assertTrue(chat_room_1) + self.assertTrue(chat_room_2) + self.assertNotEqual(chat_room_1.id, chat_room_2.id, "Must create a new chat room") + self.assertNotEqual(chat_room_1.name, chat_room_2.name, "Must generate a new chat room name") + self.assertEqual(chat_room_1.max_capacity, "20", "Must set the max capacity on the chat room") + self.assertEqual(chat_room_2.max_capacity, "20", "Must copy the max capacity") + + def test_meeting_room_unlink(self): + """Test the duplication of the meeting room.""" + meeting_room = self.env["event.meeting.room"].create({ + "name": "Test meeting room", + "event_id": self.event_0.id, + "target_audience": "dev", + "room_max_capacity": "20", + }) + + self.assertTrue(meeting_room.chat_room_id) + chat_room_id = meeting_room.chat_room_id.id + + meeting_room.unlink() + self.assertFalse(self.env["chat.room"].browse(chat_room_id).exists()) diff --git a/addons/website_event_meet/views/assets.xml b/addons/website_event_meet/views/assets.xml new file mode 100644 index 00000000000..0d57557fb4d --- /dev/null +++ b/addons/website_event_meet/views/assets.xml @@ -0,0 +1,11 @@ + + +