diff --git a/addons/web_editor/controllers/main.py b/addons/web_editor/controllers/main.py index 972d2b5e27dc878e63a8eec6232ff93c3e3e9480..923b78492b3eb492bc814baa45a2d62c0047d768 100644 --- a/addons/web_editor/controllers/main.py +++ b/addons/web_editor/controllers/main.py @@ -222,3 +222,11 @@ class Web_Editor(http.Controller): if attachments_to_remove: attachments_to_remove.unlink() return removal_blocked_by + + @http.route('/web_editor/customize_template_get', type='json', auth='user', website=True) + def customize_template_get(self, key, full=False, bundles=False): + """ Get inherit view's informations of the template ``key``. + returns templates (which can be active or not) + ``bundles=True`` returns also the asset bundles + """ + return request.env["ir.ui.view"].customize_template_get(key, full=full, bundles=bundles) diff --git a/addons/web_editor/models/ir_ui_view.py b/addons/web_editor/models/ir_ui_view.py index bb6ff4b741163c49ea047465ef82d2db9a07f58d..b4ec78cfe8bc6fde6b6daed1828418f4384e1ace 100644 --- a/addons/web_editor/models/ir_ui_view.py +++ b/addons/web_editor/models/ir_ui_view.py @@ -113,3 +113,99 @@ class IrUiView(models.Model): view.write({'arch': view._pretty_arch(arch)}) self.sudo().mapped('model_data_id').write({'noupdate': True}) + + @api.model + def _view_obj(self, view_id): + if isinstance(view_id, basestring): + return self.env.ref(view_id) + elif isinstance(view_id, (int, long)): + return self.browse(view_id) + # assume it's already a view object (WTF?) + return view_id + + # Returns all views (called and inherited) related to a view + # Used by translation mechanism, SEO and optional templates + + @api.model + def _views_get(self, view_id, options=True, bundles=False, root=True): + """ For a given view ``view_id``, should return: + * the view itself + * all views inheriting from it, enabled or not + - but not the optional children of a non-enabled child + * all views called from it (via t-call) + :returns recordset of ir.ui.view + """ + try: + view = self._view_obj(view_id) + except ValueError: + _logger.warning("Could not find view object with view_id '%s'", view_id) + return [] + + while root and view.inherit_id: + view = view.inherit_id + + views_to_return = view + + node = etree.fromstring(view.arch) + xpath = "//t[@t-call]" + if bundles: + xpath += "| //t[@t-call-assets]" + for child in node.xpath(xpath): + try: + called_view = self._view_obj(child.get('t-call', child.get('t-call-assets'))) + except ValueError: + continue + if called_view not in views_to_return: + views_to_return += self._views_get(called_view, options=options, bundles=bundles) + + extensions = view.inherit_children_ids + if not options: + # only active children + extensions = view.inherit_children_ids.filtered(lambda view: view.active) + + # Keep options in a deterministic order regardless of their applicability + for extension in extensions.sorted(key=lambda v: v.id): + # only return optional grandchildren if this child is enabled + for ext_view in self._views_get(extension, options=extension.active, root=False): + if ext_view not in views_to_return: + views_to_return += ext_view + return views_to_return + + @api.model + def customize_template_get(self, key, full=False, bundles=False): + """ Get inherit view's informations of the template ``key``. + returns templates (which can be active or not) + ``bundles=True`` returns also the asset bundles + """ + user = self.env.user + user_groups = set(user.groups_id) + views = self.with_context(active_test=False)._views_get(key, bundles=bundles) + done = set() + result = [] + for view in views: + if full: + if not user_groups.issuperset(view.groups_id): + continue + if view.inherit_id not in done: + result.append({ + 'name': view.inherit_id.name, + 'id': view.id, + 'key': view.key, + 'xml_id': view.xml_id, + 'arch': view.arch, + 'inherit_id': view.inherit_id.id, + 'header': True, + 'active': False + }) + done.add(view.inherit_id) + result.append({ + 'name': view.name, + 'id': view.id, + 'key': view.key, + 'xml_id': view.xml_id, + 'arch': view.arch, + 'inherit_id': view.inherit_id.id, + 'header': False, + 'active': view.active, + }) + return result diff --git a/addons/web_editor/static/src/less/web_editor.ui.components.less b/addons/web_editor/static/src/less/web_editor.ui.components.less index 31d2ce07dac1b3f8e2e714415167e68627e21244..7dd882ffbd07b0c525b499da0329bc76da12d146 100644 --- a/addons/web_editor/static/src/less/web_editor.ui.components.less +++ b/addons/web_editor/static/src/less/web_editor.ui.components.less @@ -119,6 +119,63 @@ .o-animation(fadeInDownSmall, 700ms, @fill-mode: forwards); } +// ACE EDITOR +.o_ace_view_editor { + @o-ace-color: #2F3129; + background: @o-ace-color; + color: white; + .o-flex-display(); + .o-flex-flow(column, nowrap); + + .o_ace_view_editor_title { + .o-flex(0, 0, auto); + .o-flex-display(); + .o-align-items(center); + .o-justify-content(space-between); + padding: @grid-gutter-width/4; + + > * { + .o-flex(0, 0, auto); + margin: 0 @grid-gutter-width/4; + + &.oe_include_bundles { + .o-flex-display(); + .o-align-items(center); + font-size: 11px; + + > .o_checkbox { + margin-right: @grid-gutter-width/4; + } + } + } + + > select { + .o-flex(0, 1, auto); + min-width: 60px; + } + } + #ace-view-id { + .o-flex(0, 0, auto); + padding: @grid-gutter-width/4 @grid-gutter-width/2; + background-color: lighten(@o-ace-color, 10%); + } + #ace-view-editor { + height: 70%; // in case flex is not supported + .o-flex(1, 1, auto); + .ace_gutter { + cursor: ew-resize; + } + } + + &.oe_ace_open { + opacity: 0.97; + } + &.oe_ace_closed { + z-index: -1000; + opacity: 0; + } +} + // MODALS body .modal { // SELECT MEDIA diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index 0826ac839d76a7707f0bc5e7fc32fc0f80bd05bd..336250f1a59a4aea7e2731b93a877508859c04d2 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -261,7 +261,7 @@ class Website(Home): return request.redirect(redirect) @http.route('/website/customize_template_get', type='json', auth='user', website=True) - def customize_template_get(self, key, full=False, bundles=False): + def customize_template_get(self, key, full=True, bundles=False): """ Get inherit view's informations of the template ``key``. By default, only :returns ``customize_show`` templates (which can be active or not), if ``full=True`` returns inherit view's informations of the template ``key``. diff --git a/addons/website/models/ir_ui_view.py b/addons/website/models/ir_ui_view.py index e08116c1465f97471abd1471ca995e17fa1c1c8b..acd9256ea52b9c3be4e8709e8a95b41c18a24528 100644 --- a/addons/website/models/ir_ui_view.py +++ b/addons/website/models/ir_ui_view.py @@ -69,54 +69,6 @@ class View(models.Model): # assume it's already a view object (WTF?) return view_id - # Returns all views (called and inherited) related to a view - # Used by translation mechanism, SEO and optional templates - - @api.model - def _views_get(self, view_id, options=True, bundles=False, root=True): - """ For a given view ``view_id``, should return: - * the view itself - * all views inheriting from it, enabled or not - - but not the optional children of a non-enabled child - * all views called from it (via t-call) - :returns recordset of ir.ui.view - """ - try: - view = self._view_obj(view_id) - except ValueError: - _logger.warning("Could not find view object with view_id '%s'", view_id) - return [] - - while root and view.inherit_id: - view = view.inherit_id - - views_to_return = view - - node = etree.fromstring(view.arch) - xpath = "//t[@t-call]" - if bundles: - xpath += "| //t[@t-call-assets]" - for child in node.xpath(xpath): - try: - called_view = self._view_obj(child.get('t-call', child.get('t-call-assets'))) - except ValueError: - continue - if called_view not in views_to_return: - views_to_return += self._views_get(called_view, options=options, bundles=bundles) - - extensions = view.inherit_children_ids - if not options: - # only active children - extensions = view.inherit_children_ids.filtered(lambda view: view.active) - - # Keep options in a deterministic order regardless of their applicability - for extension in extensions.sorted(key=lambda v: v.id): - # only return optional grandchildren if this child is enabled - for ext_view in self._views_get(extension, options=extension.active, root=False): - if ext_view not in views_to_return: - views_to_return += ext_view - return views_to_return - @api.model @tools.ormcache_context('self._uid', 'xml_id', keys=('website_id',)) def get_view_id(self, xml_id): @@ -186,37 +138,23 @@ class View(models.Model): @api.model def customize_template_get(self, key, full=False, bundles=False): """ Get inherit view's informations of the template ``key``. By default, only - returns ``customize_show`` templates (which can be active or not), if - ``full=True`` returns inherit view's informations of the template ``key``. - ``bundles=True`` returns also the asset bundles + :returns ``customize_show`` templates (which can be active or not), if + ``full=True`` returns inherit view's informations of the template ``key``. + ``bundles=True`` returns also the asset bundles """ + result = super(View, self).customize_template_get(key, full=full, bundles=bundles) + imd = self.env['ir.model.data'] view_theme_id = imd.xmlid_to_res_id('website.theme') - user = self.env.user - user_groups = set(user.groups_id) - views = self.with_context(active_test=False)._views_get(key, bundles=bundles) - done = set() - result = [] - for view in views: - if not user_groups.issuperset(view.groups_id): + + result_filtered = [] + for x in result: + if not full: + view = self.browse(x['id']) + if not view.customize_show: + continue + if view_theme_id and x['inherit_id'] == view_theme_id: continue - if full or (view.customize_show and view.inherit_id.id != view_theme_id): - if view.inherit_id not in done: - result.append({ - 'name': view.inherit_id.name, - 'id': view.id, - 'key': view.key, - 'inherit_id': view.inherit_id.id, - 'header': True, - 'active': False - }) - done.add(view.inherit_id) - result.append({ - 'name': view.name, - 'id': view.id, - 'key': view.key, - 'inherit_id': view.inherit_id.id, - 'header': False, - 'active': view.active, - }) - return result + result_filtered.append(x) + + return result_filtered diff --git a/addons/website/static/src/less/website.ui.components.less b/addons/website/static/src/less/website.ui.components.less index 92a116ca309a2f87e0571c6ee021910f86902376..e1230d2b3588aa158543120b566c3a98b3e394b9 100644 --- a/addons/website/static/src/less/website.ui.components.less +++ b/addons/website/static/src/less/website.ui.components.less @@ -324,63 +324,9 @@ body .modal { // ACE EDITOR .o_ace_view_editor { - @o-ace-color: #2F3129; - .o-position-absolute(@odoo-navbar-height, 0, 0); position: fixed; - .o-flex-display(); - .o-flex-flow(column, nowrap); z-index: 1001; - background: @o-ace-color; - color: white; - - .o_ace_view_editor_title { - .o-flex(0, 0, auto); - .o-flex-display(); - .o-align-items(center); - .o-justify-content(space-between); - padding: @grid-gutter-width/4; - - > * { - .o-flex(0, 0, auto); - margin: 0 @grid-gutter-width/4; - - &.oe_include_bundles { - .o-flex-display(); - .o-align-items(center); - font-size: 11px; - - > .o_checkbox { - margin-right: @grid-gutter-width/4; - } - } - } - - > select { - .o-flex(0, 1, auto); - min-width: 60px; - } - } - #ace-view-id { - .o-flex(0, 0, auto); - padding: @grid-gutter-width/4 @grid-gutter-width/2; - background-color: lighten(@o-ace-color, 10%); - } - #ace-view-editor { - height: 70%; // in case flex is not supported - .o-flex(1, 1, auto); - .ace_gutter { - cursor: ew-resize; - } - } - - &.oe_ace_open { - opacity: 0.97; - } - &.oe_ace_closed { - z-index: -1000; - opacity: 0; - } } // PLANNER DIALOG