def test_breadcrumb_with_additions( self, monkeypatch, request_context, main_module_registry, ): def additional_breadcrumb_items() -> Iterable[BreadcrumbItem]: yield BreadcrumbItem( title="In between 1", url=None, ) yield BreadcrumbItem( title="In between 2", url="123", ) monkeypatch.setattr( SomeMainModule, "additional_breadcrumb_items", additional_breadcrumb_items, ) assert list(SomeWatoMode().breadcrumb()) == [ BreadcrumbItem(title="Hosts", url=None), BreadcrumbItem(title="In between 1", url=None), BreadcrumbItem(title="In between 2", url="123"), BreadcrumbItem(title="(Untitled module)", url="wato.py?mode=some_wato_mode"), ]
def _page_not_found() -> Response: # TODO: This is a page handler. It should not be located in generic application # object. Move it to another place if request.has_var("_plain_error"): html.write_text(_("Page not found")) else: title = _("Page not found") make_header( html, title, Breadcrumb([ BreadcrumbItem( title="Nowhere", url=None, ), BreadcrumbItem( title=title, url="javascript:document.location.reload(false)", ), ]), ) html.show_error(_("This page was not found. Sorry.")) html.footer() return response
def additional_breadcrumb_items() -> Iterable[BreadcrumbItem]: yield BreadcrumbItem( title="In between 1", url=None, ) yield BreadcrumbItem( title="In between 2", url="123", )
def test_breadcrumb_without_additions( self, request_context, main_module_registry, ): assert list(SomeWatoMode().breadcrumb()) == [ BreadcrumbItem(title="Hosts", url=None), BreadcrumbItem(title="(Untitled module)", url="wato.py?mode=some_wato_mode"), ]
def test_breadcrumb_add(): i1 = BreadcrumbItem("Title1", "index.py") b1 = Breadcrumb([i1]) i2 = BreadcrumbItem("Title2", "index.py") b2 = Breadcrumb([i2]) b3 = b1 + b2 assert len(b1) == 1 assert len(b2) == 1 assert len(b3) == 2
def _release_notes_breadcrumb() -> Breadcrumb: breadcrumb = make_main_menu_breadcrumb(mega_menu_registry.menu_setup()) breadcrumb.append(BreadcrumbItem( title=_("Maintenance"), url=None, )) breadcrumb.append( BreadcrumbItem( title=_("Release notes"), url="version.py", )) return breadcrumb
def breadcrumb(self): return Breadcrumb([ BreadcrumbItem( title="Hosts", url=watolib.Folder.root_folder().url(), ), ]) + self._folder.breadcrumb()
def _release_notes_breadcrumb() -> Breadcrumb: breadcrumb = make_main_menu_breadcrumb(MegaMenuSetup) breadcrumb.append(BreadcrumbItem( title=_("Release notes"), url="version.py", )) return breadcrumb
def _topic_breadcrumb_item(self) -> Iterable[BreadcrumbItem]: """Yield the BreadcrumbItem(s) for the topic of this mode For the top level modes we need to prepend the topic of the mode. The mode is sadly not available directly in WatoMode. Instead it is configured in the MainModule class that is related to the WatoMode. There is no 1:1 connection between WatoMode / MainModule classes. For the moment we lookup the main_module_registry to find the topics for as many modes as possible. TODO: Once all non top level modes have a parent_mode() method, we know which modes are top level modes. Then we could move all attributes from the MainModules to the WatoModes and create the WATO menu items directly out of the WatoModes. """ mode_name = self.name() main_module = main_module_registry.get(mode_name) if main_module is None: return # TODO: Can be activated once all non top level modes have a parent_mode set #raise RuntimeError("Could not determine topic breadcrumb item for mode %r" % mode_name) yield BreadcrumbItem( title=main_module().topic.title, url=None, ) yield from main_module.additional_breadcrumb_items()
def make_folder_breadcrumb(folder: watolib.CREFolder) -> Breadcrumb: return Breadcrumb([ BreadcrumbItem( title=_("Hosts"), url=None, ), ]) + folder.breadcrumb()
def _breadcrumb(self, title: str) -> Breadcrumb: breadcrumb = make_host_breadcrumb(self._request.host.name()) breadcrumb.append( BreadcrumbItem( title=title, url="javascript:document.location.reload(false)", )) return breadcrumb
def dashboard_breadcrumb(name: str, board: DashboardConfig, title: str) -> Breadcrumb: breadcrumb = make_topic_breadcrumb( mega_menu_registry.menu_monitoring(), PagetypeTopics.get_topic(board["topic"])) breadcrumb.append( BreadcrumbItem(title, makeuri_contextless(request, [("name", name)]))) return breadcrumb
def _show_file_breadcrumb(host_name: HostName, title: str) -> Breadcrumb: breadcrumb = make_host_breadcrumb(host_name) breadcrumb.append( BreadcrumbItem( title=_("Log files of host %s") % host_name, url=html.makeuri([('file', '')]), )) breadcrumb.append(make_current_page_breadcrumb_item(title)) return breadcrumb
def additional_breadcrumb_items(cls) -> Iterable[BreadcrumbItem]: yield BreadcrumbItem( title="Windows, Linux, Solaris, AIX", url=makeuri_contextless( request, [("mode", "agents")], filename="wato.py", ), )
def _show_file_breadcrumb(host_name: HostName, title: str) -> Breadcrumb: breadcrumb = make_host_breadcrumb(host_name) breadcrumb.append( BreadcrumbItem( title=_("Log files of host %s") % host_name, url=makeuri(request, [("file", "")]), )) breadcrumb.append(make_current_page_breadcrumb_item(title)) return breadcrumb
def _breadcrumb(self) -> Breadcrumb: breadcrumb = make_simple_page_breadcrumb(mega_menu_registry.menu_user(), self._page_title()) breadcrumb.insert( -1, BreadcrumbItem( title=_("Two-factor authentication"), url="user_two_factor_overview.py", ), ) return breadcrumb
def _add_breadcrumb_topic_items(breadcrumb, titles, path): for num_elements in range(1, len(path)): elements = path[:num_elements] breadcrumb.append( BreadcrumbItem( title=titles.get(elements[-1], elements[-1]), url=html.makeuri_contextless([("mode", "check_plugin_topic"), ("topic", "/".join(elements))]), )) return breadcrumb
def _release_notes_breadcrumb() -> Breadcrumb: breadcrumb = make_main_menu_breadcrumb(mega_menu_registry["help_links"]) breadcrumb.append( BreadcrumbItem( title=_("Release notes"), url="version.py", )) return breadcrumb
def test_breadcrumb_creation(): i1 = BreadcrumbItem("Title1", "index.py") b = Breadcrumb([i1]) assert len(b) == 1 assert b[0].title == "Title1" b.append(BreadcrumbItem("Title2", "index.py")) assert len(b) == 2 assert b[1].title == "Title2" b += [ # type: ignore[misc] BreadcrumbItem("Title3", "index.py"), BreadcrumbItem("Title4", "index.py"), ] assert isinstance(b, Breadcrumb) assert len(b) == 4 assert b[2].title == "Title3" assert b[3].title == "Title4"
def _breadcrumb_item(self) -> BreadcrumbItem: """Return the breadcrumb item for the current mode""" # For the currently active mode use the same link as the "page title click" if request.get_ascii_input("mode") == self.name(): breadcrumb_url = "javascript:window.location.reload(false)" else: breadcrumb_url = self._breadcrumb_url() return BreadcrumbItem( title=self.title(), url=breadcrumb_url, )
def page_werk(): load_werks() werk_id = request.get_integer_input_mandatory("werk") if werk_id not in g_werks: raise MKUserError("werk", _("This werk does not exist.")) werk = g_werks[werk_id] title = ("%s %s - %s") % (_("Werk"), render_werk_id( werk, with_link=False), werk["title"]) breadcrumb = make_main_menu_breadcrumb(mega_menu_registry["help_links"]) breadcrumb.append( BreadcrumbItem( title=_("Change log (Werks)"), url="change_log.py", )) breadcrumb.append(make_current_page_breadcrumb_item(title)) html.header(title, breadcrumb, _page_menu_werk(breadcrumb, werk)) html.open_table(class_=["data", "headerleft", "werks"]) def werk_table_row(caption, content, css=None): html.open_tr() html.th(caption) html.td(content, class_=css) html.close_tr() translator = cmk.utils.werks.WerkTranslator() werk_table_row(_("ID"), render_werk_id(werk, with_link=False)) werk_table_row(_("Title"), html.render_b(render_werk_title(werk))) werk_table_row(_("Component"), translator.component_of(werk)) werk_table_row(_("Date"), render_werk_date(werk)) werk_table_row(_("Checkmk Version"), werk["version"]) werk_table_row(_("Level"), translator.level_of(werk), css="werklevel werklevel%d" % werk["level"]) werk_table_row(_("Class"), translator.class_of(werk), css="werkclass werkclass%s" % werk["class"]) werk_table_row( _("Compatibility"), translator.compatibility_of(werk), css="werkcomp werkcomp%s" % werk["compatible"], ) werk_table_row(_("Description"), render_werk_description(werk), css="nowiki") html.close_table() html.footer()
def _breadcrumb(self, title: str) -> Breadcrumb: breadcrumb = make_topic_breadcrumb(mega_menu_registry.menu_monitoring(), PagetypeTopics.get_topic("analyze")) # Add the parent element: List of all crashes breadcrumb.append( BreadcrumbItem( title=_("Crash reports"), url=html.makeuri_contextless([("view_name", "crash_reports")], filename="view.py"), )) breadcrumb.append(make_current_page_breadcrumb_item(title)) return breadcrumb
def _edit_annotation_breadcrumb(breadcrumb: Breadcrumb, title: str) -> Breadcrumb: breadcrumb.append(BreadcrumbItem( title=title, url=html.makeuri([]), )) return breadcrumb
def render_availability_page(view: 'View', filterheaders: 'FilterHeaders') -> None: config.user.need_permission("general.see_availability") # We make reports about hosts, services or BI aggregates if "service" in view.datasource.infos: what = "service" elif "aggr_name" in view.datasource.infos: what = "bi" else: what = "host" avoptions = get_availability_options_from_url(what) time_range, range_title = avoptions["range"] # We have two display modes: # - Show availability table (stats) "table" # - Show timeline "timeline" # --> controlled by URL variable "av_mode" av_mode = html.request.get_ascii_input_mandatory("av_mode", "table") if av_mode == "timeline": title = _("Availability Timeline") else: title = _("Availability") # This is combined with the object selection # - Show all objects # - Show one specific object # --> controlled by URL variables "av_site", "av_host" and "av_service" # --> controlled by "av_aggr" in case of BI aggregate title += " - " av_object: AVObjectSpec = None if html.request.var("av_host"): av_object = (html.request.get_str_input_mandatory("av_site"), html.request.get_str_input_mandatory("av_host"), html.request.get_unicode_input_mandatory("av_service")) title += av_object[1] if av_object[2]: title += " - " + av_object[2] elif html.request.var("av_aggr"): av_object = (None, None, html.request.get_unicode_input_mandatory("av_aggr")) title += av_object[2] else: title += view_title(view.spec) title += " - " + range_title breadcrumb = view.breadcrumb() breadcrumb.append(BreadcrumbItem( title=title, url=breadcrumb[-1].url + "&mode=availability", )) if handle_edit_annotations(breadcrumb): return # Deletion must take place before computation, since it affects the outcome with html.plugged(): handle_delete_annotations() confirmation_html_code = html.drain() # Now compute all data, we need this also for CSV export if not html.has_user_errors(): include_long_output = av_mode == "timeline" \ and "timeline_long_output" in avoptions["labelling"] av_rawdata, has_reached_logrow_limit = availability.get_availability_rawdata( what, view.context, filterheaders, view.only_sites, av_object=av_object, include_output=av_mode == "timeline", include_long_output=include_long_output, avoptions=avoptions) av_data = availability.compute_availability(what, av_rawdata, avoptions) # Do CSV ouput if html.output_format == "csv_export" and config.user.may("general.csv_export"): _output_csv(what, av_mode, av_data, avoptions) return if display_options.enabled(display_options.H): html.body_start(title, force=True) if display_options.enabled(display_options.T): html.top_heading(title, breadcrumb) html.write(confirmation_html_code) # Remove variables for editing annotations, otherwise they will make it into the uris html.request.del_vars("anno_") if html.request.var("filled_in") == "editanno": html.request.del_var("filled_in") if display_options.enabled(display_options.B): html.begin_context_buttons() html.toggle_button( "avoptions", html.has_user_errors(), "painteroptions", _("Configure details of the report"), ) html.context_button( _("Status View"), html.makeuri([("mode", "status")]), "status", ) if config.reporting_available() and config.user.may("general.reporting"): html.context_button( _("Export as PDF"), html.makeuri([], filename="report_instant.py"), "report", ) if config.user.may("general.csv_export"): html.context_button( _("Export as CSV"), html.makeuri([("output_format", "csv_export")]), "download_csv", ) if av_mode == "timeline" or av_object: html.context_button( _("Availability"), html.makeuri([("av_mode", "availability"), ("av_host", ""), ("av_aggr", "")]), "availability", ) elif not av_object: html.context_button( _("Timeline"), html.makeuri([("av_mode", "timeline")]), "timeline", ) elif av_mode == "timeline" and what != "bi": history_url = availability.history_url_of(av_object, time_range) html.context_button( _("History"), history_url, "history", ) html.end_context_buttons() # Render the avoptions again to get the HTML code, because the HTML vars have changed # above (anno_ and editanno_ has been removed, which must not be part of the form avoptions = render_availability_options(what) if not html.has_user_errors(): # If we abolish the limit we have to fetch the data again # with changed logrow_limit = 0, which means no limit if has_reached_logrow_limit: text = _( "Your query matched more than %d log entries. " "<b>Note:</b> The number of shown rows does not necessarily reflect the " "matched entries and the result might be incomplete. ") % avoptions["logrow_limit"] text += html.render_a( _('Repeat query without limit.'), html.makeuri([("_unset_logrow_limit", "1"), ("avo_logrow_limit", 0)])) html.show_warning(text) do_render_availability(what, av_rawdata, av_data, av_mode, av_object, avoptions) if display_options.enabled(display_options.Z): html.bottom_footer() if display_options.enabled(display_options.H): html.body_end()
def test_breadcrumb_item_creation(): i1 = BreadcrumbItem("Title", "index.py") assert i1.title == "Title" assert i1.url == "index.py"
def render_bi_availability(view: "View", aggr_rows: 'Rows') -> None: config.user.need_permission("general.see_availability") av_mode = html.request.get_ascii_input_mandatory("av_mode", "availability") avoptions = get_availability_options_from_url("bi") title = view_title(view.spec) if av_mode == "timeline": title = _("Timeline of") + " " + title else: title = _("Availability of") + " " + title if html.output_format != "csv_export": html.body_start(title) breadcrumb = view.breadcrumb() breadcrumb.append( BreadcrumbItem( title=title, url=breadcrumb[-1].url + "&mode=availability", )) html.top_heading(title, breadcrumb) html.begin_context_buttons() html.toggle_button("avoptions", False, "painteroptions", _("Configure details of the report")) html.context_button(_("Status View"), html.makeuri([("mode", "status")]), "status") if config.reporting_available() and config.user.may("general.reporting"): html.context_button(_("Export as PDF"), html.makeuri([], filename="report_instant.py"), "report") if config.user.may("general.csv_export"): html.context_button(_("Export as CSV"), html.makeuri([("output_format", "csv_export")]), "download_csv") if av_mode == "timeline": html.context_button(_("Availability"), html.makeuri([("av_mode", "availability")]), "availability") elif len(aggr_rows) == 1: aggr_name = aggr_rows[0]["aggr_name"] aggr_group = aggr_rows[0]["aggr_group"] timeline_url = html.makeuri([("av_mode", "timeline"), ("av_aggr_name", aggr_name), ("av_aggr_group", aggr_group)]) html.context_button(_("Timeline"), timeline_url, "timeline") html.end_context_buttons() avoptions = render_availability_options("bi") if not html.has_user_errors(): logrow_limit = avoptions["logrow_limit"] if logrow_limit == 0: livestatus_limit = None else: livestatus_limit = (len(aggr_rows) * logrow_limit) spans: List[AVSpan] = [] # iterate all aggregation rows timewarpcode = HTML() timewarp = html.request.get_integer_input("timewarp") has_reached_logrow_limit = False timeline_containers, fetched_rows = availability.get_timeline_containers( aggr_rows, avoptions, timewarp, livestatus_limit + 1 if livestatus_limit is not None else None) if livestatus_limit and fetched_rows > livestatus_limit: has_reached_logrow_limit = True for timeline_container in timeline_containers: tree = timeline_container.aggr_tree these_spans = timeline_container.timeline timewarp_tree_state = timeline_container.timewarp_state spans += these_spans # render selected time warp for the corresponding aggregation row (should be matched by only one) if timewarp and timewarp_tree_state: state, assumed_state, node, _subtrees = timewarp_tree_state eff_state = state if assumed_state is not None: eff_state = assumed_state row = { "aggr_tree": tree, "aggr_treestate": timewarp_tree_state, "aggr_state": state, # state disregarding assumptions "aggr_assumed_state": assumed_state, # is None, if there are no assumptions "aggr_effective_state": eff_state, # is assumed_state, if there are assumptions, else real state "aggr_name": node["title"], "aggr_output": eff_state["output"], "aggr_hosts": node["reqhosts"], "aggr_function": node["func"], "aggr_group": html.request.var("aggr_group"), } renderer = bi.FoldableTreeRendererTree( row, omit_root=False, expansion_level=config.user.bi_expansion_level, only_problems=False, lazy=False) tdclass, htmlcode = renderer.css_class(), renderer.render() with html.plugged(): # TODO: SOMETHING IS WRONG IN HERE (used to be the same situation in original code!) # FIXME: WHAT is wrong in here?? html.open_h3() # render icons for back and forth button_back_shown = False button_forth_shown = False if int(these_spans[0]["from"]) == timewarp: html.disabled_icon_button("back_off") button_back_shown = True previous_span = None for span in these_spans: if not button_back_shown and int( span["from"]) == timewarp and previous_span is not None: html.icon_button( html.makeuri([("timewarp", str(int(previous_span["from"])))]), _("Jump one phase back"), "back") button_back_shown = True # Multiple followup spans can have the same "from" time # We only show one forth-arrow with an actual time difference elif not button_forth_shown and previous_span and int( previous_span["from"]) == timewarp and int( span["from"]) != timewarp: html.icon_button(html.makeuri([("timewarp", str(int(span["from"])))]), _("Jump one phase forth"), "forth") button_forth_shown = True previous_span = span if not button_forth_shown: html.disabled_icon_button("forth_off") html.write_text(" ") html.icon_button(html.makeuri([("timewarp", "")]), _("Close Timewarp"), "closetimewarp") html.write_text("%s %s" % (_("Timewarp to "), time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timewarp)))) html.close_h3() html.open_table(class_=["data", "table", "timewarp"]) html.open_tr(class_=["data", "odd0"]) html.open_td(class_=tdclass) html.write_html(htmlcode) html.close_td() html.close_tr() html.close_table() timewarpcode += html.drain() # Note: 'spans_by_object' returns two arguments which are used by # all availability views but not by BI. There we have to take # only complete aggregations av_rawdata = availability.spans_by_object(spans, None)[0] av_data = availability.compute_availability("bi", av_rawdata, avoptions) # If we abolish the limit we have to fetch the data again # with changed logrow_limit = 0, which means no limit if has_reached_logrow_limit: text = _( "Your query matched more than %d log entries. " "<b>Note:</b> The shown data does not necessarily reflect the " "matched entries and the result might be incomplete. ") % avoptions["logrow_limit"] text += html.render_a(_('Repeat query without limit.'), html.makeuri([("_unset_logrow_limit", "1")])) html.show_warning(text) if html.output_format == "csv_export" and config.user.may("general.csv_export"): _output_csv("bi", av_mode, av_data, avoptions) return html.write(timewarpcode) do_render_availability("bi", av_rawdata, av_data, av_mode, None, avoptions) html.bottom_footer() html.body_end()
def make_main_menu_breadcrumb(menu: MegaMenu) -> Breadcrumb: """Create a breadcrumb for the main menu level""" return Breadcrumb([BreadcrumbItem( title=menu.title, url=None, )])
if parent_cls := self.parent_mode(): # For some reason pylint does not understand that this is a class type breadcrumb = parent_cls().breadcrumb() # pylint: disable=not-callable else: breadcrumb = Breadcrumb() # For the currently active mode use the same link as the "page title click" if html.request.get_ascii_input("mode") == self.name(): breadcrumb_url = "javascript:window.location.reload(false)" else: breadcrumb_url = self._breadcrumb_url() breadcrumb.append( BreadcrumbItem( title=self.title(), url=breadcrumb_url, )) return breadcrumb def _breadcrumb_url(self) -> str: """Override this method to implement a custom breadcrumb URL This can be useful when a mode needs some more contextual information to link to the correct page. """ return html.makeuri_contextless([("mode", self.name())], filename="wato.py") def buttons(self) -> None: global_buttons()
def make_current_page_breadcrumb_item(title: str) -> BreadcrumbItem: """Helper to create a breadcrumb link to the current page""" return BreadcrumbItem( title=title, url="javascript:document.location.reload(false)", )