Пример #1
0
def handle_acknowledgement():
    if html.request.var("_werk_ack") and html.check_transaction():
        werk_id = html.request.get_integer_input_mandatory("_werk_ack")
        if werk_id not in g_werks:
            raise MKUserError("werk", _("This werk does not exist."))
        werk = g_werks[werk_id]

        if werk["compatible"] == "incomp_unack":
            acknowledge_werk(werk)
            html.show_message(
                _("Werk %s - %s has been acknowledged.") % (render_werk_id(
                    werk, with_link=True), render_werk_title(werk)))
            html.reload_sidebar()
            load_werks()  # reload ack states after modification

    elif html.request.var("_ack_all"):
        if html.confirm(_(
                "Do you really want to acknowledge <b>all</b> incompatible werks?"
        ),
                        method="GET"):
            num = len(unacknowledged_incompatible_werks())
            acknowledge_all_werks()
            html.show_message(
                _("%d incompatible Werks have been acknowledged.") % num)
            html.reload_sidebar()
            load_werks()  # reload ack states after modification

    render_unacknowleged_werks()
Пример #2
0
    def _show_form(self, profile_changed: bool) -> None:
        assert config.user.id is not None

        users = userdb.load_users()

        if profile_changed:
            html.reload_sidebar()
            html.show_message(_("Successfully updated user profile."))
            # Ensure theme changes are applied without additional user interaction
            html.immediate_browser_redirect(0.5, makeuri(global_request, []))

        if html.has_user_errors():
            html.show_user_errors()

        user = users.get(config.user.id)
        if user is None:
            html.show_warning(_("Sorry, your user account does not exist."))
            html.footer()
            return

        html.begin_form("profile", method="POST")
        html.prevent_password_auto_completion()
        html.open_div(class_="wato")
        forms.header(self._page_title())

        forms.section(_("Name"), simple=True)
        html.write_text(user.get("alias", config.user.id))

        select_language(user)

        # Let the user configure how he wants to be notified
        rulebased_notifications = rulebased_notifications_enabled()
        if (not rulebased_notifications
                and config.user.may('general.edit_notifications')
                and user.get("notifications_enabled")):
            forms.section(_("Notifications"))
            html.help(
                _("Here you can configure how you want to be notified about host and service problems and "
                  "other monitoring events."))
            watolib.get_vs_flexible_notifications().render_input(
                "notification_method", user.get("notification_method"))

        if config.user.may('general.edit_user_attributes'):
            for name, attr in userdb.get_user_attributes():
                if attr.user_editable():
                    vs = attr.valuespec()
                    forms.section(_u(vs.title()))
                    value = user.get(name, vs.default_value())
                    if not attr.permission() or config.user.may(
                            attr.permission()):
                        vs.render_input("ua_" + name, value)
                        html.help(_u(vs.help()))
                    else:
                        html.write(vs.value_to_text(value))

        forms.end()
        html.close_div()
        html.hidden_fields()
        html.end_form()
        html.footer()
Пример #3
0
def page_clear():
    acktime = html.request.get_float_input_mandatory('acktime', time.time())
    prev_url = html.get_url_input('prev_url', '')
    if html.request.var('_confirm'):
        acknowledge_failed_notifications(acktime)

        if config.user.authorized_login_sites():
            # This local import is needed for the moment
            import cmk.gui.wato.user_profile  # pylint: disable=redefined-outer-name
            cmk.gui.wato.user_profile.user_profile_async_replication_page()
            return

    failed_notifications = load_failed_notifications(before=acktime, after=acknowledged_time())
    render_page_confirm(acktime, prev_url, failed_notifications)
    if html.request.var('_confirm'):
        html.reload_sidebar()
Пример #4
0
    def show(self):
        # type: () -> None
        if not config.user.may("general.configure_sidebar"):
            raise MKGeneralException(_("You are not allowed to change the sidebar."))

        html.header(_("Available snapins"))

        html.begin_context_buttons()
        CustomSnapins.context_button_list()
        html.end_context_buttons()

        addname = html.request.var("name")
        if addname in snapin_registry and addname not in self._used_snapins(
        ) and html.check_transaction():
            self._user_config.add_snapin(UserSidebarSnapin.from_snapin_type_id(addname))
            self._user_config.save()
            html.reload_sidebar()

        self._show_builtin_snapins()
Пример #5
0
    def _bulk_delete_after_confirm(cls, what):
        to_delete = []
        for varname, _value in html.request.itervars(prefix="_c_%s+" % what):
            if html.get_checkbox(varname):
                checkbox_ident = varname.split("_c_%s+" % what)[-1]
                to_delete.append(checkbox_ident.split("+", 1))

        if not to_delete:
            return

        c = html.confirm(
            _("Do you really want to delete %d %s?") % (len(to_delete), cls.phrase("title_plural")))

        if c:
            for owner, instance_id in to_delete:
                cls.remove_instance((owner, instance_id))

            for owner in {e[0] for e in to_delete}:
                cls.save_user_instances(owner)

            html.reload_sidebar()
        elif c is False:
            return False
Пример #6
0
def _show_page_user_profile(change_pw):
    start_async_replication = False

    if not config.user.id:
        raise MKUserError(None, _('Not logged in.'))

    if not config.user.may('general.edit_profile') and not config.user.may(
            'general.change_password'):
        raise MKAuthException(
            _("You are not allowed to edit your user profile."))

    if not config.wato_enabled:
        raise MKAuthException(
            _('User profiles can not be edited (WATO is disabled).'))

    success = None
    if html.request.has_var('_save') and html.check_transaction():
        users = userdb.load_users(lock=True)

        try:
            # Profile edit (user options like language etc.)
            if config.user.may('general.edit_profile'):
                if not change_pw:
                    set_lang = html.get_checkbox('_set_lang')
                    language = html.request.var('language')
                    # Set the users language if requested
                    if set_lang:
                        if language == '':
                            language = None
                        # Set custom language
                        users[config.user.id]['language'] = language
                        config.user.language = language
                        html.set_language_cookie(language)

                    else:
                        # Remove the customized language
                        if 'language' in users[config.user.id]:
                            del users[config.user.id]['language']
                        config.user.reset_language()

                    # load the new language
                    cmk.gui.i18n.localize(config.user.language)

                    user = users.get(config.user.id)
                    if config.user.may('general.edit_notifications'
                                       ) and user.get("notifications_enabled"):
                        value = forms.get_input(
                            watolib.get_vs_flexible_notifications(),
                            "notification_method")
                        users[config.user.id]["notification_method"] = value

                    # Custom attributes
                    if config.user.may('general.edit_user_attributes'):
                        for name, attr in userdb.get_user_attributes():
                            if attr.user_editable():
                                if not attr.permission() or config.user.may(
                                        attr.permission()):
                                    vs = attr.valuespec()
                                    value = vs.from_html_vars('ua_' + name)
                                    vs.validate_value(value, "ua_" + name)
                                    users[config.user.id][name] = value

            # Change the password if requested
            password_changed = False
            if config.user.may('general.change_password'):
                cur_password = html.request.var('cur_password')
                password = html.request.var('password')
                password2 = html.request.var('password2', '')

                if change_pw:
                    # Force change pw mode
                    if not cur_password:
                        raise MKUserError(
                            "cur_password",
                            _("You need to provide your current password."))
                    if not password:
                        raise MKUserError(
                            "password", _("You need to change your password."))
                    if cur_password == password:
                        raise MKUserError(
                            "password",
                            _("The new password must differ from your current one."
                              ))

                if cur_password and password:
                    if userdb.hook_login(config.user.id,
                                         cur_password) is False:
                        raise MKUserError("cur_password",
                                          _("Your old password is wrong."))
                    if password2 and password != password2:
                        raise MKUserError(
                            "password2",
                            _("The both new passwords do not match."))

                    watolib.verify_password_policy(password)
                    users[config.user.id]['password'] = hash_password(password)
                    users[config.user.id]['last_pw_change'] = int(time.time())

                    if change_pw:
                        # Has been changed, remove enforcement flag
                        del users[config.user.id]['enforce_pw_change']

                    # Increase serial to invalidate old cookies
                    if 'serial' not in users[config.user.id]:
                        users[config.user.id]['serial'] = 1
                    else:
                        users[config.user.id]['serial'] += 1

                    password_changed = True

            # Now, if in distributed environment where users can login to remote sites,
            # set the trigger for pushing the new auth information to the slave sites
            # asynchronous
            if config.user.authorized_login_sites():
                start_async_replication = True

            userdb.save_users(users)

            if password_changed:
                # Set the new cookie to prevent logout for the current user
                login.set_auth_cookie(config.user.id)

            success = True
        except MKUserError as e:
            html.add_user_error(e.varname, e)
    else:
        users = userdb.load_users()

    watolib.init_wato_datastructures(with_wato_lock=True)

    # When in distributed setup, display the replication dialog instead of the normal
    # profile edit dialog after changing the password.
    if start_async_replication:
        user_profile_async_replication_page()
        return

    if change_pw:
        title = _("Change Password")
    else:
        title = _("Edit User Profile")

    html.header(title)

    # Rule based notifications: The user currently cannot simply call the according
    # WATO module due to WATO permission issues. So we cannot show this button
    # right now.
    if not change_pw:
        rulebased_notifications = watolib.load_configuration_settings().get(
            "enable_rulebased_notifications")
        if rulebased_notifications and config.user.may(
                'general.edit_notifications'):
            html.begin_context_buttons()
            url = "wato.py?mode=user_notifications_p"
            html.context_button(_("Notifications"), url, "notifications")
            html.end_context_buttons()
    else:
        reason = html.request.var('reason')
        if reason == 'expired':
            html.p(
                _('Your password is too old, you need to choose a new password.'
                  ))
        else:
            html.p(
                _('You are required to change your password before proceeding.'
                  ))

    if success:
        html.reload_sidebar()
        if change_pw:
            html.show_message(_("Your password has been changed."))
            raise HTTPRedirect(html.request.var('_origtarget', 'index.py'))
        else:
            html.show_message(_("Successfully updated user profile."))
            # Ensure theme changes are applied without additional user interaction
            html.immediate_browser_redirect(0.5, html.makeuri([]))

    if html.has_user_errors():
        html.show_user_errors()

    user = users.get(config.user.id)
    if user is None:
        html.show_warning(_("Sorry, your user account does not exist."))
        html.footer()
        return

    # Returns true if an attribute is locked and should be read only. Is only
    # checked when modifying an existing user
    locked_attributes = userdb.locked_attributes(user.get('connector'))

    def is_locked(attr):
        return attr in locked_attributes

    html.begin_form("profile", method="POST")
    html.prevent_password_auto_completion()
    html.open_div(class_="wato")
    forms.header(_("Personal Settings"))

    if not change_pw:
        forms.section(_("Name"), simple=True)
        html.write_text(user.get("alias", config.user.id))

    if config.user.may(
            'general.change_password') and not is_locked('password'):
        forms.section(_("Current Password"))
        html.password_input('cur_password', autocomplete="new-password")

        forms.section(_("New Password"))
        html.password_input('password', autocomplete="new-password")

        forms.section(_("New Password Confirmation"))
        html.password_input('password2', autocomplete="new-password")

    if not change_pw and config.user.may('general.edit_profile'):
        select_language(user)

        # Let the user configure how he wants to be notified
        if not rulebased_notifications \
            and config.user.may('general.edit_notifications') \
            and user.get("notifications_enabled"):
            forms.section(_("Notifications"))
            html.help(
                _("Here you can configure how you want to be notified about host and service problems and "
                  "other monitoring events."))
            watolib.get_vs_flexible_notifications().render_input(
                "notification_method", user.get("notification_method"))

        if config.user.may('general.edit_user_attributes'):
            for name, attr in userdb.get_user_attributes():
                if attr.user_editable():
                    vs = attr.valuespec()
                    forms.section(_u(vs.title()))
                    value = user.get(name, vs.default_value())
                    if not attr.permission() or config.user.may(
                            attr.permission()):
                        vs.render_input("ua_" + name, value)
                        html.help(_u(vs.help()))
                    else:
                        html.write(vs.value_to_text(value))

    # Save button
    forms.end()
    html.button("_save", _("Save"))
    html.close_div()
    html.hidden_fields()
    html.end_form()
    html.footer()
Пример #7
0
    def page_list(cls):
        cls.load()

        # custom_columns = []
        # render_custom_buttons = None
        # render_custom_columns = None
        # render_custom_context_buttons = None
        # check_deletable_handler = None

        cls.need_overriding_permission("edit")

        html.header(cls.phrase("title_plural"))
        html.begin_context_buttons()
        html.context_button(cls.phrase("new"), cls.create_url(), "new_" + cls.type_name())

        # TODO: Remove this legacy code as soon as views, dashboards and reports have been
        # moved to pagetypes.py
        html.context_button(_("Views"), "edit_views.py", "view")
        html.context_button(_("Dashboards"), "edit_dashboards.py", "dashboard")

        def has_reporting():
            try:
                # The suppression below is OK, we just want to check if the module is there.
                import cmk.gui.cee.reporting  # noqa: F401 # pylint: disable=unused-variable,redefined-outer-name
                return True
            except ImportError:
                return False

        if has_reporting():
            html.context_button(_("Reports"), "edit_reports.py", "report")

        # ## if render_custom_context_buttons:
        # ##     render_custom_context_buttons()

        for other_type_name, other_pagetype in page_types.items():
            if cls.type_name() != other_type_name:
                html.context_button(
                    other_pagetype.phrase("title_plural").title(), '%ss.py' % other_type_name,
                    other_type_name)
        html.end_context_buttons()

        # Deletion
        delname = html.request.var("_delete")
        if delname and html.transaction_valid():
            owner = UserId(html.request.get_unicode_input_mandatory('_owner', config.user.id))

            try:
                instance = cls.instance((owner, delname))
            except KeyError:
                raise MKUserError(
                    "_delete",
                    _("The %s you are trying to delete "
                      "does not exist.") % cls.phrase("title"))

            if not instance.may_delete():
                raise MKUserError("_delete", _("You are not permitted to perform this action."))

            try:
                if owner != config.user.id:
                    owned_by = _(" (owned by %s)") % owner
                else:
                    owned_by = ""
                c = html.confirm(
                    _("Please confirm the deletion of \"%s\"%s.") % (instance.title(), owned_by))
                if c:
                    cls.remove_instance((owner, delname))
                    cls.save_user_instances(owner)
                    html.reload_sidebar()
                elif c is False:
                    html.footer()
                    return
            except MKUserError as e:
                html.user_error(e)

        # Bulk delete
        if html.request.var("_bulk_delete_my") and html.transaction_valid():
            if cls._bulk_delete_after_confirm("my") is False:
                html.footer()
                return

        elif html.request.var("_bulk_delete_foreign") and html.transaction_valid():
            if cls._bulk_delete_after_confirm("foreign") is False:
                html.footer()
                return

        my_instances, foreign_instances, builtin_instances = cls.get_instances()
        for what, title, instances in [
            ("my", _('Customized'), my_instances),
            ("foreign", _('Owned by other users'), foreign_instances),
            ("builtin", _('Builtin'), builtin_instances),
        ]:
            if not instances:
                continue

            html.open_h3()
            html.write(title)
            html.close_h3()

            if what != "builtin":
                html.begin_form("bulk_delete_%s" % what, method="POST")

            with table_element(limit=None) as table:
                for instance in instances:
                    table.row()

                    if what != "builtin" and instance.may_delete():
                        table.cell(html.render_input(
                            "_toggle_group",
                            type_="button",
                            class_="checkgroup",
                            onclick="cmk.selection.toggle_all_rows(this.form);",
                            value='X'),
                                   sortable=False,
                                   css="checkbox")
                        html.checkbox("_c_%s+%s+%s" % (what, instance.owner(), instance.name()))

                    # Actions
                    table.cell(_('Actions'), css='buttons visuals')

                    # View
                    if isinstance(instance, PageRenderer):
                        html.icon_button(instance.page_url(), _("View"), "new_" + cls.type_name())

                    # Clone / Customize
                    html.icon_button(instance.clone_url(), _("Create a customized copy of this"),
                                     "clone")

                    # Delete
                    if instance.may_delete():
                        html.icon_button(instance.delete_url(), _("Delete!"), "delete")

                    # Edit
                    if instance.may_edit():
                        html.icon_button(instance.edit_url(), _("Edit"), "edit")

                    cls.custom_list_buttons(instance)

                    # Internal ID of instance (we call that 'name')
                    table.cell(_('ID'), instance.name(), css="narrow")

                    # Title
                    table.cell(_('Title'))
                    html.write_text(instance.render_title())
                    html.help(_u(instance.description()))

                    # Custom columns specific to that page type
                    instance.render_extra_columns(table)

                    # ## for title, renderer in custom_columns:
                    # ##     table.cell(title, renderer(visual))

                    # Owner
                    if instance.is_builtin():
                        ownertxt = html.render_i(_("builtin"))
                    else:
                        ownertxt = instance.owner()
                    table.cell(_('Owner'), ownertxt)
                    table.cell(_('Public'), _("yes") if instance.is_public() else _("no"))
                    table.cell(_('Hidden'), _("yes") if instance.is_hidden() else _("no"))

                    # FIXME: WTF?!?
                    # TODO: Haeeh? Another custom columns
                    # ## if render_custom_columns:
                    # ##     render_custom_columns(visual_name, visual)

            if what != "builtin":
                html.button("_bulk_delete_%s" % what,
                            _("Bulk delete"),
                            "submit",
                            style="margin-top:10px")
                html.hidden_fields()
                html.end_form()

        html.footer()
        return
Пример #8
0
    def page_edit(cls):
        back_url = html.get_url_input("back", cls.list_url())

        cls.load()
        cls.need_overriding_permission("edit")

        # Three possible modes:
        # "create" -> create completely new page
        # "clone"  -> like new, but prefill form with values from existing page
        # "edit"   -> edit existing page
        mode = html.request.var('mode', 'edit')
        if mode == "create":
            title = cls.phrase("create")
            page_dict = {
                "name": cls.default_name(),
                "topic": cls.default_topic(),
            }
        else:
            # Load existing page. visual from disk - and create a copy if 'load_user' is set
            page_name = html.request.var("load_name")
            if mode == "edit":
                title = cls.phrase("edit")

                owner_user_id = UserId(
                    html.request.get_unicode_input_mandatory("owner", config.user.id))
                if owner_user_id == config.user.id:
                    page = cls.find_my_page(page_name)
                else:
                    page = cls.find_foreign_page(owner_user_id, page_name)

                if page is None:
                    raise MKUserError(None,
                                      _("The requested %s does not exist") % cls.phrase("title"))

                # TODO FIXME: Looks like a hack
                cls.remove_instance((owner_user_id, page_name))  # will be added later again
            else:  # clone
                title = cls.phrase("clone")
                load_user = html.request.get_unicode_input(
                    "load_user")  # FIXME: Change varname to "owner"

                try:
                    page = cls.instance((load_user, page_name))
                except KeyError:
                    raise MKUserError(None,
                                      _("The requested %s does not exist") % cls.phrase("title"))
            page_dict = page.internal_representation()

        html.header(title)
        html.begin_context_buttons()
        html.context_button(_("Back"), back_url, "back")
        html.end_context_buttons()

        parameters, keys_by_topic = cls._collect_parameters(mode)
        vs = Dictionary(
            title=_("General Properties"),
            render='form',
            optional_keys=False,
            elements=parameters,
            headers=keys_by_topic,
        )

        def validate(page_dict):
            owner_user_id = UserId(html.request.get_unicode_input_mandatory(
                "owner", config.user.id))
            page_name = page_dict["name"]
            if owner_user_id == config.user.id:
                page = cls.find_my_page(page_name)
            else:
                page = cls.find_foreign_page(owner_user_id, page_name)
            if page:
                raise MKUserError(
                    "_p_name",
                    _("You already have an element with the ID <b>%s</b>") % page_dict["name"])

        new_page_dict = forms.edit_valuespec(vs,
                                             page_dict,
                                             validate=validate,
                                             focus="_p_title",
                                             method="POST")
        if new_page_dict is not None:
            # Take over keys from previous value that are specific to the page type
            # and not edited here.
            if mode in ("edit", "clone"):
                for key, value in page_dict.items():
                    new_page_dict.setdefault(key, value)

            owner = UserId(html.request.get_unicode_input_mandatory("owner", config.user.id))
            new_page_dict["owner"] = owner
            new_page = cls(new_page_dict)

            cls.add_page(new_page)
            cls.save_user_instances(owner)
            if mode == "create":
                redirect_url = new_page.after_create_url() or back_url
            else:
                redirect_url = back_url

            html.immediate_browser_redirect(0.5, redirect_url)
            html.show_message(_('Your changes haven been saved.'))
            # Reload sidebar.TODO: This code logically belongs to PageRenderer. How
            # can we simply move it there?
            # TODO: This is not true for all cases. e.g. the BookmarkList is not
            # of type PageRenderer but has a dedicated sidebar snapin. Maybe
            # the best option would be to make a dedicated method to decide whether
            # or not to reload the sidebar.
            if new_page_dict.get("hidden") in [ None, False ] \
               or new_page_dict.get("hidden") != page_dict.get("hidden"):
                html.reload_sidebar()

        else:
            html.show_localization_hint()

        html.footer()
        return
Пример #9
0
def _wato_page_handler(current_mode: str,
                       mode_permissions: List[PermissionName],
                       mode_class: Type[WatoMode]) -> None:
    try:
        init_wato_datastructures(with_wato_lock=not html.is_transaction())
    except Exception:
        # Snapshot must work in any case
        if current_mode == 'snapshot':
            pass
        else:
            raise

    # Check general permission for this mode
    if mode_permissions is not None and not config.user.may("wato.seeall"):
        _ensure_mode_permissions(mode_permissions)

    mode = mode_class()

    # Do actions (might switch mode)
    action_message: Optional[str] = None
    if html.is_transaction():
        try:
            config.user.need_permission("wato.edit")

            # Even if the user has seen this mode because auf "seeall",
            # he needs an explicit access permission for doing changes:
            if config.user.may("wato.seeall"):
                if mode_permissions:
                    _ensure_mode_permissions(mode_permissions)

            if cmk.gui.watolib.read_only.is_enabled(
            ) and not cmk.gui.watolib.read_only.may_override():
                raise MKUserError(None, cmk.gui.watolib.read_only.message())

            result = mode.action()
            if isinstance(result, tuple):
                newmode, action_message = result
            else:
                newmode = result

            # We assume something has been modified and increase the config generation ID by one.
            update_config_generation()

            # If newmode is False, then we shall immediately abort.
            # This is e.g. the case, if the page outputted non-HTML
            # data, such as a tarball (in the export function). We must
            # be sure not to output *any* further data in that case.
            if newmode is False:
                return

            # if newmode is not None, then the mode has been changed
            if newmode is not None:
                assert not isinstance(newmode, bool)
                if newmode == "":  # no further information: configuration dialog, etc.
                    if action_message:
                        html.show_message(action_message)
                        wato_html_footer()
                    return
                mode_permissions, mode_class = _get_mode_permission_and_class(
                    newmode)
                current_mode = newmode
                mode = mode_class()
                html.request.set_var("mode",
                                     newmode)  # will be used by makeuri

                # Check general permissions for the new mode
                if mode_permissions is not None and not config.user.may(
                        "wato.seeall"):
                    for pname in mode_permissions:
                        if '.' not in pname:
                            pname = "wato." + pname
                        config.user.need_permission(pname)

        except MKUserError as e:
            action_message = "%s" % e
            html.add_user_error(e.varname, action_message)

        except MKAuthException as e:
            reason = e.args[0]
            action_message = reason
            html.add_user_error(None, reason)

    breadcrumb = make_main_menu_breadcrumb(
        mode.main_menu()) + mode.breadcrumb()
    page_menu = mode.page_menu(breadcrumb)
    wato_html_head(title=mode.title(),
                   breadcrumb=breadcrumb,
                   page_menu=page_menu,
                   show_body_start=display_options.enabled(display_options.H),
                   show_top_heading=display_options.enabled(display_options.T))

    if not html.is_transaction() or (cmk.gui.watolib.read_only.is_enabled() and
                                     cmk.gui.watolib.read_only.may_override()):
        _show_read_only_warning()

    # Show outcome of action
    if html.has_user_errors():
        html.show_user_errors()
    elif action_message:
        html.show_message(action_message)

    # Show content
    mode.handle_page()

    if is_sidebar_reload_needed():
        html.reload_sidebar()

    if config.wato_use_git and html.is_transaction():
        do_git_commit()

    wato_html_footer(show_footer=display_options.enabled(display_options.Z),
                     show_body_end=display_options.enabled(display_options.H))
Пример #10
0
def _wato_page_handler(current_mode: str,
                       mode_permissions: List[PermissionName],
                       mode_class: Type[WatoMode]) -> None:
    try:
        init_wato_datastructures(with_wato_lock=not html.is_transaction())
    except Exception:
        # Snapshot must work in any case
        if current_mode == 'snapshot':
            pass
        else:
            raise

    # Check general permission for this mode
    if mode_permissions is not None and not config.user.may("wato.seeall"):
        _ensure_mode_permissions(mode_permissions)

    mode = mode_class()

    # Do actions (might switch mode)
    if html.is_transaction():
        try:
            config.user.need_permission("wato.edit")

            # Even if the user has seen this mode because auf "seeall",
            # he needs an explicit access permission for doing changes:
            if config.user.may("wato.seeall"):
                if mode_permissions:
                    _ensure_mode_permissions(mode_permissions)

            if cmk.gui.watolib.read_only.is_enabled(
            ) and not cmk.gui.watolib.read_only.may_override():
                raise MKUserError(None, cmk.gui.watolib.read_only.message())

            result = mode.action()
            if isinstance(result, (tuple, str, bool)):
                raise MKGeneralException(
                    f"WatoMode \"{current_mode}\" returns unsupported return value: {result!r}"
                )

            # We assume something has been modified and increase the config generation ID by one.
            update_config_generation()

            # Handle two cases:
            # a) Don't render the page content after action
            #    (a confirm dialog is displayed by the action, or a non-HTML content was sent)
            # b) Redirect to another page
            if isinstance(result, FinalizeRequest):
                raise result

        except MKUserError as e:
            html.add_user_error(e.varname, str(e))

        except MKAuthException as e:
            reason = e.args[0]
            html.add_user_error(None, reason)

    breadcrumb = make_main_menu_breadcrumb(
        mode.main_menu()) + mode.breadcrumb()
    page_menu = mode.page_menu(breadcrumb)
    wato_html_head(title=mode.title(),
                   breadcrumb=breadcrumb,
                   page_menu=page_menu,
                   show_body_start=display_options.enabled(display_options.H),
                   show_top_heading=display_options.enabled(display_options.T))

    if not html.is_transaction() or (cmk.gui.watolib.read_only.is_enabled() and
                                     cmk.gui.watolib.read_only.may_override()):
        _show_read_only_warning()

    # Show outcome of failed action on this page
    if html.has_user_errors():
        html.show_user_errors()

    # Show outcome of previous page (that redirected to this one)
    for message in get_flashed_messages():
        html.show_message(message)

    # Show content
    mode.handle_page()

    if is_sidebar_reload_needed():
        html.reload_sidebar()

    if config.wato_use_git and html.is_transaction():
        do_git_commit()

    wato_html_footer(show_body_end=display_options.enabled(display_options.H))