def _show_item_title(self, item: TopicMenuItem) -> None: item_title: Union[HTML, str] = item.title if not item.button_title: html.write_text(item_title) return html.span(item.title) html.button(item.name, item.button_title)
def _show_link( self, url: str, onclick: Optional[str], target: Optional[str], icon: Icon, title: str ) -> None: html.open_a(href=url, onclick=onclick, target=target) html.icon(icon or "trans") html.span(title) html.close_a()
def show(self, breadcrumb: Breadcrumb) -> None: html.open_div(class_="breadcrumb") for item in breadcrumb: if item.url: html.a(escape_to_html(str(item.title)), href=item.url) else: html.span(escape_to_html(str(item.title))) html.close_div()
def page(self) -> cmk.gui.pages.PageResult: breadcrumb = make_simple_page_breadcrumb( mega_menu_registry["help_links"], _("Info")) make_header( html, self._title(), breadcrumb=breadcrumb, ) html.open_div(id_="info_title") html.h1(_("Your monitoring machine")) html.a( HTMLWriter.render_img(theme.url("images/tribe29.svg")), "https://tribe29.com", target="_blank", ) html.close_div() html.div(None, id_="info_underline") html.open_div(id_="info_intro_text") html.span(_("Open. Effective. Awesome.")) html.span( _("May we present? Monitoring as it's supposed to be: " "incredibly quick to install, infinetely scalable, highly customizable and " "designed for admins.")) html.span( _("Visit our %s to learn more about Checkmk and about the %s.") % ( HTMLWriter.render_a( _("website"), "https://checkmk.com", target="_blank"), HTMLWriter.render_a( _("latest version"), "https://checkmk.com/product/latest-version", target="_blank", ), )) html.close_div() version_major_minor = re.sub(r".\d+$", "", Version(__version__).version_base) if version_major_minor: current_version_link = "https://checkmk.com/product/checkmk-%s" % version_major_minor else: current_version_link = "https://checkmk.com/product/latest-version" html.open_div(id="info_image") html.open_a(href=current_version_link, target="_blank") html.img(theme.url("images/monitoring-machine.png")) html.close_a() html.close_div() html.close_div() html.open_div(id_="info_footer") html.span( _("© %s tribe29 GmbH. All Rights Reserved.") % time.strftime("%Y")) html.a(_("License agreement"), href="https://checkmk.com/legal.html", target="_blank") html.close_div()
def _show_topic_title(self, menu_id: str, topic_id: str, topic: TopicMenuTopic) -> None: html.open_h2() html.open_a( class_="show_all_topics", href="", onclick="cmk.popup_menu.mega_menu_show_all_topics('%s')" % topic_id, ) html.icon(icon="collapse_arrow", title=_("Show all %s topics") % menu_id) html.close_a() if not user.get_attribute("icons_per_item") and topic.icon: html.icon(topic.icon) html.span(topic.title) html.close_h2()
def user_profile_async_replication_dialog(sites: Sequence[SiteId], back_url: str) -> None: html.p( _("In order to activate your changes available on all remote sites, your user profile needs " "to be replicated to the remote sites. This is done on this page now. Each site " "is being represented by a single image which is first shown gray and then fills " "to green during synchronisation.")) html.h3(_("Replication States")) html.open_div(id_="profile_repl") num_replsites = 0 for site_id in sites: site = active_config.sites[site_id] if "secret" not in site: status_txt = _("Not logged in.") start_sync = False icon = "repl_locked" else: status_txt = _("Waiting for replication to start") start_sync = True icon = "repl_pending" html.open_div(class_="site", id_="site-%s" % site_id) html.div("", title=status_txt, class_=["icon", "repl_status", icon]) if start_sync: changes_manager = ActivateChanges() changes_manager.load() estimated_duration = changes_manager.get_activation_time( site_id, ACTIVATION_TIME_PROFILE_SYNC, 2.0) html.javascript("cmk.profile_replication.start(%s, %d, %s);" % ( json.dumps(site_id), int(estimated_duration * 1000.0), json.dumps(_("Replication in progress")), )) num_replsites += 1 else: _add_profile_replication_change(site_id, status_txt) html.span(site.get("alias", site_id)) html.close_div() html.javascript("cmk.profile_replication.prepare(%d, %s);\n" % (num_replsites, json.dumps(back_url))) html.close_div()
def section( title: Union[None, HTML, str] = None, checkbox: Union[None, HTML, str, Tuple[str, bool, str]] = None, section_id: Optional[str] = None, simple: bool = False, hide: bool = False, legend: bool = True, css: Optional[str] = None, is_show_more: bool = False, is_changed: bool = False, is_required: bool = False, ) -> None: global g_section_open section_close() html.open_tr( id_=section_id, class_=([] if css is None else [css]) + ["show_more_mode" if is_show_more and not is_changed else "basic"], style="display:none;" if hide else None, ) if legend: html.open_td(class_=["legend"] + (["simple"] if simple else [])) if title: html.open_div( class_=["title"] + (["withcheckbox"] if checkbox else []), title=escaping.strip_tags(title), ) html.write_text(title) html.span("." * 200, class_=["dots"] + (["required"] if is_required else [])) html.close_div() if checkbox: html.open_div(class_="checkbox") if isinstance(checkbox, (str, HTML)): html.write_text(checkbox) else: name, active, attrname = checkbox html.checkbox( name, active, onclick="cmk.wato.toggle_attribute(this, '%s')" % attrname ) html.close_div() html.close_td() html.open_td(class_=["content"] + (["simple"] if simple else [])) g_section_open = True
def render_mobile_list(rows, view, group_cells, cells, num_columns, show_checkboxes): if not is_mobile(request, response): html.show_error(_("This view can only be used in mobile mode.")) return # Force relative timestamp always. This saves space. painter_options = PainterOptions.get_instance() painter_options.set("ts_format", "rel") html.open_ul(class_="mobilelist", **{"data-role": "listview"}) # Paint data rows for row in rows: html.open_li() rendered_cells = [cell.render(row) for cell in cells] if rendered_cells: # First cell (assumedly state) is left rendered_class, rendered_content = rendered_cells[0] html.p(rendered_content, class_=["ui-li-aside", "ui-li-desc", rendered_class]) if len(rendered_cells) > 1: content = HTML(" · ").join([ rendered_cell[1] for rendered_cell in rendered_cells[1:num_columns + 1] ]) html.h3(content) for rendered_cell, cell in zip( rendered_cells[num_columns + 1:], cells[num_columns + 1:]): rendered_class, rendered_content = rendered_cell html.open_p(class_="ui-li-desc") cell.paint_as_header() html.write_text(": ") html.span(rendered_content, class_=rendered_class) html.close_p() html.close_li() html.close_ul() html.javascript('$("ul.mobilelist a").attr("data-ajax", "false");')
def show(self, menu: MegaMenu) -> None: more_id = "main_menu_" + menu.name show_more = user.get_show_more_setting(more_id) html.open_div(id_=more_id, class_=["main_menu", "more" if show_more else "less"]) hide_entries_js = "cmk.popup_menu.mega_menu_hide_entries('%s')" % more_id html.open_div(class_="navigation_bar") html.open_div(class_="search_bar") if menu.search: menu.search.show_search_field() html.close_div() if menu.info_line: html.span(menu.info_line(), id_="info_line_%s" % menu.name, class_="info_line") topics = menu.topics() if any_show_more_items(topics): html.open_div() html.more_button(id_=more_id, dom_levels_up=3, additional_js=hide_entries_js, with_text=True) html.close_div() html.close_div() html.open_div(class_="content inner", id="content_inner_%s" % menu.name) for topic in topics: if not topic.items: continue self._show_topic(topic, menu.name) html.close_div() html.close_div() html.javascript(hide_entries_js) html.javascript("cmk.popup_menu.initialize_mega_menus();") html.open_div(class_="content inner", id="content_inner_%s_search" % menu.name) html.close_div()
def _download_table(self, title: str, paths: List[str]) -> None: forms.header(title) forms.container() for path in paths: os_path = path relpath = path.replace(cmk.utils.paths.agents_dir + "/", "") filename = path.split("/")[-1] file_size = os.stat(os_path).st_size # FIXME: Rename classes etc. to something generic html.open_div(class_="ruleset") html.open_div(style="width:300px;", class_="text") html.a(filename, href="agents/%s" % relpath, download=filename) html.span("." * 200, class_="dots") html.close_div() html.div(cmk.utils.render.fmt_bytes(file_size), style="width:60px;", class_="rulecount") html.close_div() html.close_div() forms.end()
def _render_headers(self, actions_enabled: bool, actions_visible: bool, empty_columns: List[bool]) -> None: if self.options["omit_headers"]: return table_id = self.id html.open_tr() first_col = True for nr, header in enumerate(self.headers): if self.options["omit_empty_columns"] and empty_columns[nr]: continue if header.help_txt: header_title: HTML = HTMLWriter.render_span( header.title, title=header.help_txt) else: header_title = header.title if not isinstance(header.css, list): css_class: "CSSSpec" = [header.css] else: css_class = header.css assert isinstance(css_class, list) css_class = [("header_%s" % c) for c in css_class if c is not None] if not self.options["sortable"] or not header.sortable: html.open_th(class_=css_class) else: css_class.insert(0, "sort") reverse = 0 sort = request.get_ascii_input("_%s_sort" % table_id) if sort: sort_col, sort_reverse = map(int, sort.split(",", 1)) if sort_col == nr: reverse = 1 if sort_reverse == 0 else 0 action_uri = makeactionuri(request, transactions, [("_%s_sort" % table_id, "%d,%d" % (nr, reverse))]) html.open_th( class_=css_class, title=_("Sort by %s") % header.title, onclick="location.href='%s'" % action_uri, ) # Add the table action link if first_col: first_col = False if actions_enabled: if not header_title: header_title = HTML( " " ) # Fixes layout problem with white triangle if actions_visible: state = "0" help_txt = _("Hide table actions") img = "table_actions_on" else: state = "1" help_txt = _("Display table actions") img = "table_actions_off" html.open_div(class_=["toggle_actions"]) html.icon_button( makeuri(request, [("_%s_actions" % table_id, state)]), help_txt, img, cssclass="toggle_actions", ) html.span(header_title) html.close_div() else: html.write_text(header_title) else: html.write_text(header_title) html.close_th() html.close_tr()
def _show_node(self, tree, show_host, mousecode=None, img_class=None): # Check if we have an assumed state: comparing assumed state (tree[1]) with state (tree[0]) if tree[1] and tree[0] != tree[1]: addclass = ["assumed"] effective_state = tree[1] else: addclass = [] effective_state = tree[0] class_ = [ "content", "state", "state%d" % (effective_state["state"] if effective_state["state"] is not None else -1), ] + addclass html.open_span(class_=class_) html.write_text(self._render_bi_state(effective_state["state"])) html.close_span() if mousecode: if img_class: html.img( src=theme.url("images/tree_closed.svg"), class_=["treeangle", img_class], onclick=mousecode, ) html.open_span(class_=["content", "name"]) icon_name, icon_title = None, None if tree[0]["in_downtime"] == 2: icon_name = "downtime" icon_title = _( "This element is currently in a scheduled downtime.") elif tree[0]["in_downtime"] == 1: # only display host downtime if the service has no own downtime icon_name = "derived_downtime" icon_title = _( "One of the subelements is in a scheduled downtime.") if tree[0]["acknowledged"]: icon_name = "ack" icon_title = _("This problem has been acknowledged.") if not tree[0]["in_service_period"]: icon_name = "outof_serviceperiod" icon_title = _( "This element is currently not in its service period.") if icon_name and icon_title: html.icon(icon_name, title=icon_title, class_=["icon", "bi"]) yield if mousecode: if str(effective_state["state"]) in tree[2].get( "state_messages", {}): html.b(HTML("♦"), class_="bullet") html.write_text(tree[2]["state_messages"][str( effective_state["state"])]) html.close_span() output: HTML = cmk.gui.view_utils.format_plugin_output( effective_state["output"], shall_escape=active_config.escape_plugin_output) if output: output = HTMLWriter.render_b(HTML("♦"), class_="bullet") + output else: output = HTML() html.span(output, class_=["content", "output"])
def _output_analysed_ruleset(self, all_rulesets, rulespec, svc_desc_or_item, svc_desc, known_settings=None): if known_settings is None: known_settings = self._PARAMETERS_UNKNOWN def rule_url(rule: Rule) -> str: return folder_preserving_link([ ("mode", "edit_rule"), ("varname", varname), ("rule_folder", rule.folder.path()), ("rule_id", rule.id), ("host", self._hostname), ( "item", mk_repr(svc_desc_or_item).decode() if svc_desc_or_item else "", ), ("service", mk_repr(svc_desc).decode() if svc_desc else ""), ]) varname = rulespec.name valuespec = rulespec.valuespec url = folder_preserving_link([ ("mode", "edit_ruleset"), ("varname", varname), ("host", self._hostname), ("item", mk_repr(svc_desc_or_item).decode()), ("service", mk_repr(svc_desc).decode()), ]) forms.section(HTMLWriter.render_a(rulespec.title, url)) ruleset = all_rulesets.get(varname) setting, rules = ruleset.analyse_ruleset(self._hostname, svc_desc_or_item, svc_desc) html.open_table(class_="setting") html.open_tr() html.open_td(class_="reason") # Show reason for the determined value if len(rules) == 1: rule_folder, rule_index, rule = rules[0] url = rule_url(rule) html.a(_("Rule %d in %s") % (rule_index + 1, rule_folder.title()), href=rule_url(rule)) elif len(rules) > 1: html.a("%d %s" % (len(rules), _("Rules")), href=url) else: html.span(_("Default value")) html.close_td() # Show the resulting value or factory setting html.open_td( class_=["settingvalue", "used" if len(rules) > 0 else "unused"]) if isinstance(known_settings, dict) and "tp_computed_params" in known_settings: computed_at = known_settings["tp_computed_params"]["computed_at"] html.write_text( _("Timespecific parameters computed at %s") % cmk.utils.render.date_and_time(computed_at)) html.br() known_settings = known_settings["tp_computed_params"]["params"] # In some cases we now the settings from a check_mk automation if known_settings is self._PARAMETERS_OMIT: return # Special handling for logwatch: The check parameter is always None. The actual # patterns are configured in logwatch_rules. We do not have access to the actual # patterns here but just to the useless "None". In order not to complicate things # we simply display nothing here. if varname == "logwatch_rules": pass elif known_settings is not self._PARAMETERS_UNKNOWN: try: html.write_text(valuespec.value_to_html(known_settings)) except Exception as e: if active_config.debug: raise html.write_text( _("Invalid parameter %r: %s") % (known_settings, e)) else: # For match type "dict" it can be the case the rule define some of the keys # while other keys are taken from the factory defaults. We need to show the # complete outcoming value here. if rules and ruleset.match_type() == "dict": if (rulespec.factory_default is not Rulespec.NO_FACTORY_DEFAULT and rulespec.factory_default is not Rulespec.FACTORY_DEFAULT_UNUSED): fd = rulespec.factory_default.copy() fd.update(setting) setting = fd if valuespec and not rules: # show the default value if rulespec.factory_default is Rulespec.FACTORY_DEFAULT_UNUSED: # Some rulesets are ineffective if they are empty html.write_text(_("(unused)")) elif rulespec.factory_default is not Rulespec.NO_FACTORY_DEFAULT: # If there is a factory default then show that one setting = rulespec.factory_default html.write_text(valuespec.value_to_html(setting)) elif ruleset.match_type() in ("all", "list"): # Rulesets that build lists are empty if no rule matches html.write_text(_("(no entry)")) else: # Else we use the default value of the valuespec html.write_text( valuespec.value_to_html(valuespec.default_value())) # We have a setting elif valuespec: if ruleset.match_type() == "all": for s in setting: html.write_text(valuespec.value_to_html(s)) else: html.write_text(valuespec.value_to_html(setting)) # Binary rule, no valuespec, outcome is True or False else: icon_name = "rule_%s%s" % ("yes" if setting else "no", "_off" if not rules else "") html.icon(icon_name, title=_("yes") if setting else _("no")) html.close_td() html.close_tr() html.close_table()
def _show_test_row(self, table, test_id, test_results_by_site, site_ids): table.row() table.cell(_("Actions"), css="buttons", sortable=False) html.icon_button( None, _("Toggle result details"), "toggle_details", onclick="cmk.wato.toggle_container('test_result_details_%s')" % test_id, ) worst_result = sorted(test_results_by_site["site_results"].values(), key=lambda result: result.status)[0] # Disabling of test in total is_test_disabled = self._is_test_disabled(test_id) if is_test_disabled: html.icon_button( makeactionuri( request, transactions, [ ("_do", "enable"), ("_test_id", worst_result.test_id), ], ), _("Reenable this test"), "enable_test", ) else: html.icon_button( makeactionuri( request, transactions, [ ("_do", "disable"), ("_test_id", worst_result.test_id), ], ), _("Disable this test"), "disable_test", ) # assume all have the same test meta information (title, help, ...) table.cell(_("Title"), css="title " + "stale" if is_test_disabled else "") html.write_text(test_results_by_site["test"]["title"]) # Now loop all sites to display their results for site_id in site_ids: if is_test_disabled: table.cell(site_id, "") table.cell("", "") continue result = test_results_by_site["site_results"].get(site_id) if result is None: table.cell(site_id, css="state state-1") table.cell("", css="buttons") continue is_acknowledged = self._is_acknowledged(result) if is_acknowledged or result.status == -1: css = "state stale" else: css = "state state%d" % result.status table.cell(site_id, css=css) html.span(result.status_name(), title=result.text, class_="state_rounded_fill") table.cell("", css="buttons") if result.status != 0: if is_acknowledged: html.icon_button( makeactionuri( request, transactions, [ ("_do", "unack"), ("_site_id", result.site_id), ("_status_id", result.status), ("_test_id", result.test_id), ], ), _("Unacknowledge this test result for site %s") % site_id, "unacknowledge_test", ) else: html.icon_button( makeactionuri( request, transactions, [ ("_do", "ack"), ("_site_id", result.site_id), ("_status_id", result.status), ("_test_id", result.test_id), ], ), _("Acknowledge this test result for site %s") % site_id, "acknowledge_test", ) else: html.write_text("") # Add toggleable notitication context table.row(css="ac_test_details hidden", id_="test_result_details_%s" % test_id) table.cell(colspan=2 + 2 * len(site_ids)) html.write_text(test_results_by_site["test"]["help"]) if not is_test_disabled: html.open_table() for site_id in site_ids: result = test_results_by_site["site_results"].get(site_id) if result is None: continue html.open_tr() html.td(escaping.escape_attribute(site_id)) html.td("%s: %s" % (result.status_name(), result.text)) html.close_tr() html.close_table() # This dummy row is needed for not destroying the odd/even row highlighting table.row(css="hidden")
def render_snapin(self, snapin: UserSidebarSnapin) -> str: snapin_class = snapin.snapin_type name = snapin_class.type_name() snapin_instance = snapin_class() more_id = "sidebar_snapin_%s" % name show_more = user.get_show_more_setting(more_id) html.open_div(id_="snapin_container_%s" % name, class_=["snapin", ("more" if show_more else "less")]) self._render_snapin_styles(snapin_instance) # When not permitted to open/close snapins, the snapins are always opened if snapin.visible == SnapinVisibility.OPEN or not user.may( "general.configure_sidebar"): style = None else: style = "display:none" toggle_url = "sidebar_openclose.py?name=%s&state=" % name # If the user may modify the sidebar then add code for dragging the snapin head_actions: Dict[str, str] = {} if user.may("general.configure_sidebar"): head_actions = { "onmouseover": "document.body.style.cursor='move';", "onmouseout ": "document.body.style.cursor='';", "onmousedown": "cmk.sidebar.snapin_start_drag(event)", "onmouseup": "cmk.sidebar.snapin_stop_drag(event)", } html.open_div(class_=["head", snapin.visible.value], **head_actions) show_more = snapin_instance.has_show_more_items() may_configure = user.may("general.configure_sidebar") if show_more or may_configure: html.open_div(class_="snapin_buttons") if show_more: html.open_span(class_="moresnapin") html.more_button(more_id, dom_levels_up=4) html.close_span() if may_configure: # Button for closing (removing) a snapin html.open_span(class_="closesnapin") close_url = "sidebar_openclose.py?name=%s&state=off" % name html.icon_button( url=None, title=_("Remove this element"), icon="close", onclick="cmk.sidebar.remove_sidebar_snapin(this, '%s')" % close_url, ) html.close_span() html.close_div() # The heading. A click on the heading mini/maximizes the snapin toggle_actions: Dict[str, str] = {} if user.may("general.configure_sidebar"): toggle_actions = { "onclick": "cmk.sidebar.toggle_sidebar_snapin(this,'%s')" % toggle_url, "onmouseover": "this.style.cursor='pointer'", "onmouseout": "this.style.cursor='auto'", } html.b( textwrap.shorten(snapin_class.title(), width=27, placeholder="..."), class_=["heading"], **toggle_actions, ) if may_configure: # Icon for mini/maximizing html.span( "", class_="minisnapin", title=_("Open/close this element"), onclick="cmk.sidebar.toggle_sidebar_snapin(this, '%s')" % toggle_url, ) # End of header html.close_div() # Now comes the content html.open_div(class_="content", id_="snapin_%s" % name, style=style) refresh_url = "" try: # TODO: Refactor this confusing special case. Add deddicated method or something # to let the snapins make the sidebar know that there is a URL to fetch. url = snapin_instance.show() if url is not None: # Fetch the contents from an external URL. Don't render it on our own. refresh_url = url html.javascript( 'cmk.ajax.get_url("%s", cmk.utils.update_contents, "snapin_%s")' % (refresh_url, name)) except Exception as e: logger.exception("error rendering snapin %s", name) write_snapin_exception(e) html.close_div() html.close_div() return refresh_url