def _add_cell( self, title: 'HTMLContent' = "", text: 'HTMLContent' = "", css: 'CSSSpec' = None, help_txt: Optional[str] = None, colspan: Optional[int] = None, sortable: bool = True, ): if isinstance(text, HTML): content = text else: content = escape_html_permissive(str(text) if not isinstance(text, str) else text) htmlcode: HTML = content + HTML(output_funnel.drain()) if isinstance(title, HTML): header_title = title else: if title is None: title = "" header_title = escape_html_permissive( str(title) if not isinstance(title, str) else title) if self.options["collect_headers"] is True: # small helper to make sorting introducion easier. Cells which contain # buttons are never sortable if css and 'buttons' in css and sortable: sortable = False self.headers.append( TableHeader(title=header_title, css=css, help_txt=help_txt, sortable=sortable)) current_row = self.rows[-1] assert isinstance(current_row, TableRow) current_row.cells.append(CellSpec(htmlcode, css, colspan))
def render_werk_title(werk) -> HTML: title = werk["title"] # if the title begins with the name or names of check plugins, then # we link to the man pages of those checks if ":" in title: parts = title.split(":", 1) return insert_manpage_links(parts[0]) + escape_html_permissive(":" + parts[1]) return escape_html_permissive(title)
def show(self) -> None: html.open_table(cellspacing="0", class_="sitestate") sites.update_site_states_from_dead_sites() for sitename, _sitealias in sites.sorted_sites(): site = sites.get_site_config(sitename) state = sites.states().get(sitename, sites.SiteStatus({})).get("state") if state is None: state = "missing" switch = "missing" text = escape_html_permissive(sitename) else: if state == "disabled": switch = "on" text = escape_html_permissive(site["alias"]) else: switch = "off" text = render_link( site["alias"], "view.py?view_name=sitehosts&site=%s" % sitename) html.open_tr() html.td(text, class_="left") html.open_td(class_="state") if switch == "missing": html.status_label(content=state, status=state, title=_("Site is missing")) else: url = makeactionuri_contextless( request, transactions, [ ("_site_switch", "%s:%s" % (sitename, switch)), ], filename="switch_site.py", ) html.status_label_button( content=state, status=state, title=_("enable this site") if state == "disabled" else _("disable this site"), onclick="cmk.sidebar.switch_site(%s)" % (json.dumps(url)), ) html.close_tr() html.close_table()
def mk_eval(s: Union[bytes, str]) -> Any: try: return ast.literal_eval(base64.b64decode(s).decode()) except Exception: raise MKGeneralException( _("Unable to parse provided data: %s") % escape_html_permissive(repr(s)))
def action(self) -> ActionResult: if request.var("_reset"): if not transactions.check_transaction(): return None try: del self._current_settings[self._varname] except KeyError: pass msg = escape_html_permissive( _("Resetted configuration variable %s to its default.") % self._varname) else: new_value = self._valuespec.from_html_vars("ve") self._valuespec.validate_value(new_value, "ve") self._current_settings[self._varname] = new_value msg = HTML( _("Changed global configuration variable %s to %s.") % ( escaping.escape_attribute(self._varname), self._valuespec.value_to_html(new_value), )) self._save() watolib.add_change( "edit-configvar", msg, sites=self._affected_sites(), domains=[self._config_variable.domain()], need_restart=self._config_variable.need_restart(), ) return redirect(self._back_url())
def query_limit_exceeded_warn(limit: Optional[int], user_config: LoggedInUser) -> None: """Compare query reply against limits, warn in the GUI about incompleteness""" text = HTML(_("Your query produced more than %d results. ") % limit) if request.get_ascii_input( "limit", "soft") == "soft" and user_config.may("general.ignore_soft_limit"): text += html.render_a( _("Repeat query and allow more results."), target="_self", href=makeuri(request, [("limit", "hard")]), ) elif request.get_ascii_input("limit") == "hard" and user_config.may( "general.ignore_hard_limit"): text += html.render_a( _("Repeat query without limit."), target="_self", href=makeuri(request, [("limit", "none")]), ) text += escaping.escape_html_permissive(" " + _( "<b>Note:</b> the shown results are incomplete and do not reflect the sort order." )) html.show_warning(text)
def text_with_links_to_user_translated_html( elements: Iterable[Tuple[str, Optional[str]]], separator: str = "", ) -> HTML: return HTML(separator).join( html.render_a(user_translation, href=url ) if url else escape_html_permissive(user_translation) for txt, url in elements for user_translation in [_u(txt)] if txt)
def test_escape_html_permissive() -> None: assert isinstance(escaping.escape_html_permissive(""), HTML) assert str(escaping.escape_html_permissive("")) == "" assert str(escaping.escape_html_permissive("<script>")) == "<script>" assert str(escaping.escape_html_permissive("<b>")) == "<b>"
def title(self): if self._new: return _("Add LDAP connection") assert self._connection_id is not None return _("Edit LDAP connection: %s") % escape_html_permissive( self._connection_id)
def _show_patterns(self): import cmk.gui.logwatch as logwatch collection = watolib.SingleRulesetRecursively("logwatch_rules") collection.load() ruleset = collection.get("logwatch_rules") html.h3(_('Logfile patterns')) if ruleset.is_empty(): html.open_div(class_="info") html.write_text('There are no logfile patterns defined. You may create ' 'logfile patterns using the <a href="%s">Rule Editor</a>.' % watolib.folder_preserving_link([ ('mode', 'edit_ruleset'), ('varname', 'logwatch_rules'), ])) html.close_div() # Loop all rules for this ruleset already_matched = False abs_rulenr = 0 for folder, rulenr, rule in ruleset.get_rules(): # Check if this rule applies to the given host/service if self._hostname: service_desc = self._get_service_description(self._hostname, "logwatch", self._item) # If hostname (and maybe filename) try match it rule_matches = rule.matches_host_and_item(watolib.Folder.current(), self._hostname, self._item, service_desc) else: # If no host/file given match all rules rule_matches = True with foldable_container(treename="rule", id_=str(abs_rulenr), isopen=True, title=HTML("<b>Rule #%d</b>" % (abs_rulenr + 1)), indent=False), table_element("pattern_editor_rule_%d" % abs_rulenr, sortable=False, css="logwatch") as table: abs_rulenr += 1 # TODO: What's this? pattern_list = rule.value if isinstance(pattern_list, dict): pattern_list = pattern_list["reclassify_patterns"] # Each rule can hold no, one or several patterns. Loop them all here for state, pattern, comment in pattern_list: match_class = '' disp_match_txt = HTML('') match_img = '' if rule_matches: # Applies to the given host/service matched = re.search(pattern, self._match_txt) if matched: # Prepare highlighted search txt match_start = matched.start() match_end = matched.end() disp_match_txt = escape_html_permissive(self._match_txt[:match_start]) \ + html.render_span(self._match_txt[match_start:match_end], class_="match")\ + escape_html_permissive(self._match_txt[match_end:]) if not already_matched: # First match match_class = 'match first' match_img = 'match' match_title = _( 'This logfile pattern matches first and will be used for ' 'defining the state of the given line.') already_matched = True else: # subsequent match match_class = 'match' match_img = 'imatch' match_title = _( 'This logfile pattern matches but another matched first.') else: match_img = 'nmatch' match_title = _('This logfile pattern does not match the given string.') else: # rule does not match match_img = 'nmatch' match_title = _('The rule conditions do not match.') table.row() table.cell(_('Match')) html.icon("rule%s" % match_img, match_title) cls: List[str] = [] if match_class == 'match first': cls = ['state%d' % logwatch.level_state(state), 'fillbackground'] table.cell(_('State'), html.render_span(logwatch.level_name(state)), css=cls) table.cell(_('Pattern'), html.render_tt(pattern)) table.cell(_('Comment'), comment) table.cell(_('Matched line'), disp_match_txt) table.row(fixed=True) table.cell(colspan=5) edit_url = watolib.folder_preserving_link([ ("mode", "edit_rule"), ("varname", "logwatch_rules"), ("rulenr", rulenr), ("item", watolib.mk_repr(self._item).decode()), ("rule_folder", folder.path()), ("rule_id", rule.id), ]) html.icon_button(edit_url, _("Edit this rule"), "edit")
def _process_notify_message(msg): msg["id"] = utils.gen_id() msg["time"] = time.time() if isinstance(msg["dest"], str): dest_what = msg["dest"] else: dest_what = msg["dest"][0] if dest_what == "broadcast": recipients = list(config.multisite_users.keys()) elif dest_what == "online": recipients = userdb.get_online_user_ids() elif dest_what == "list": recipients = msg["dest"][1] else: recipients = [] num_recipients = len(recipients) num_success = {} for method in msg["methods"]: num_success[method] = 0 # Now loop all notitification methods to send the notifications errors: Dict[str, List[Tuple]] = {} for user_id in recipients: for method in msg["methods"]: try: handler = _notify_methods()[method]["handler"] handler(user_id, msg) num_success[method] = num_success[method] + 1 except MKInternalError as e: errors.setdefault(method, []).append((user_id, e)) message = escape_html_permissive(_("The notification has been sent via")) message += html.render_br() parts = [] for method in msg["methods"]: parts.append( html.render_tr( html.render_td(_notify_methods()[method]["title"]) + html.render_td( _("to %d of %d recipients") % (num_success[method], num_recipients) ) ) ) message += html.render_table(HTML().join(parts)) message += html.render_p(_("Sent notification to: %s") % ", ".join(recipients)) message += html.render_a(_("Back to previous page"), href=makeuri(request, [])) html.show_message(message) if errors: error_message = HTML() for method, method_errors in errors.items(): error_message += _("Failed to send %s notifications to the following users:") % method table_rows = HTML() for user_id, exception in method_errors: table_rows += html.render_tr( html.render_td(html.render_tt(user_id)) + html.render_td(exception) ) error_message += html.render_table(table_rows) + html.render_br() html.show_error(error_message)
def parse_file(site, host_name, file_name, hidecontext=False): log_chunks: List[Dict[str, Any]] = [] try: chunk: Optional[Dict[str, Any]] = None lines = get_logfile_lines(site, host_name, file_name) if lines is None: return None # skip hash line. this doesn't exist in older files while lines and lines[0].startswith('#'): lines = lines[1:] for line in lines: line = line.strip() if line == '': continue if line[:3] == '<<<': # new chunk begins log_lines: List[Dict[str, Any]] = [] chunk = {'lines': log_lines} log_chunks.append(chunk) # New header line date, logtime, level = line[3:-3].split(' ') # Save level as integer to make it better comparable if level == 'CRIT': chunk['level'] = 2 elif level == 'WARN': chunk['level'] = 1 elif level == 'OK': chunk['level'] = 0 else: chunk['level'] = 0 # Gather datetime object # Python versions below 2.5 don't provide datetime.datetime.strptime. # Use the following instead: #chunk['datetime'] = datetime.datetime.strptime(date + ' ' + logtime, "%Y-%m-%d %H:%M:%S") chunk['datetime'] = datetime.datetime( *time.strptime(date + ' ' + logtime, "%Y-%m-%d %H:%M:%S")[0:5]) elif chunk: # else: not in a chunk?! # Data line line_display = line[2:] # Classify the line for styling if line[0] == 'W': line_level = 1 line_class = 'WARN' elif line[0] == 'u': line_level = 1 line_class = 'WARN' elif line[0] == 'C': line_level = 2 line_class = 'CRIT' elif line[0] == 'O': line_level = 0 line_class = 'OK' elif not hidecontext: line_level = 0 line_class = 'context' else: continue # ignore this line log_lines.append({ 'level': line_level, 'class': line_class, 'line': line_display }) except Exception as e: if config.debug: raise raise MKGeneralException( escape_html_permissive( _("Cannot parse log file %s: %s") % (file_name, e))) return log_chunks
def _preview(self) -> None: html.begin_form("preview", method="POST") self._preview_form() attributes = self._attribute_choices() # first line could be missing in situation of import error csv_reader = self._open_csv_file() if not csv_reader: return # don't try to show preview when CSV could not be read html.h2(_("Preview")) attribute_list = "<ul>%s</ul>" % "".join( ["<li>%s (%s)</li>" % a for a in attributes if a[0] is not None]) html.help( _("This list shows you the first 10 rows from your CSV file in the way the import is " "currently parsing it. If the lines are not splitted correctly or the title line is " "not shown as title of the table, you may change the import settings above and try " "again.") + "<br><br>" + _("The first row below the titles contains fields to specify which column of the " "CSV file should be imported to which attribute of the created hosts. The import " "progress is trying to match the columns to attributes automatically by using the " "titles found in the title row (if you have some). " "If you use the correct titles, the attributes can be mapped automatically. The " "currently available attributes are:") + attribute_list + _("You can change these assignments according to your needs and then start the " "import by clicking on the <i>Import</i> button above.")) # Wenn bei einem Host ein Fehler passiert, dann wird die Fehlermeldung zu dem Host angezeigt, so dass man sehen kann, was man anpassen muss. # Die problematischen Zeilen sollen angezeigt werden, so dass man diese als Block in ein neues CSV-File eintragen kann und dann diese Datei # erneut importieren kann. if self._has_title_line: try: headers = list(next(csv_reader)) except StopIteration: headers = [] # nope, there is no header else: headers = [] rows = list(csv_reader) # Determine how many columns should be rendered by using the longest column num_columns = max([len(r) for r in [headers] + rows]) with table_element(sortable=False, searchable=False, omit_headers=not self._has_title_line) as table: # Render attribute selection fields table.row() for col_num in range(num_columns): header = headers[col_num] if len(headers) > col_num else None table.cell(escape_html_permissive(header)) attribute_varname = "attribute_%d" % col_num if request.var(attribute_varname): attribute_method = request.get_ascii_input_mandatory( attribute_varname) else: attribute_method = self._try_detect_default_attribute( attributes, header) request.del_var(attribute_varname) html.dropdown("attribute_%d" % col_num, attributes, deflt=attribute_method, autocomplete="off") # Render sample rows for row in rows: table.row() for cell in row: table.cell(None, cell) html.end_form()
def _process_message_message(msg): msg["id"] = utils.gen_id() msg["time"] = time.time() if isinstance(msg["dest"], str): dest_what = msg["dest"] else: dest_what = msg["dest"][0] if dest_what == "all_users": recipients = list(config.multisite_users.keys()) elif dest_what == "online": recipients = userdb.get_online_user_ids() elif dest_what == "list": recipients = msg["dest"][1] else: recipients = [] num_recipients = len(recipients) num_success: Dict[str, int] = {} for method in msg["methods"]: num_success[method] = 0 # Now loop all messaging methods to send the messages errors: Dict[str, List[Tuple]] = {} for user_id in recipients: for method in msg["methods"]: try: handler = _messaging_methods()[method]["handler"] handler(user_id, msg) num_success[method] = num_success[method] + 1 except MKInternalError as e: errors.setdefault(method, []).append((user_id, e)) message = escape_html_permissive( _("The message has successfully been sent...")) message += html.render_br() parts = [] for method in msg["methods"]: parts.append( html.render_li( _messaging_methods()[method]["confirmation_title"] + (_(" for all recipients.") if num_success[method] == num_recipients else _(" for %d of %d recipients.") % (num_success[method], num_recipients)))) message += html.render_ul(HTML().join(parts)) message += html.render_p(_("Recipients: %s") % ", ".join(recipients)) html.show_message(message) if errors: error_message = HTML() for method, method_errors in errors.items(): error_message += _( "Failed to send %s messages to the following users:") % method table_rows = HTML() for user_id, exception in method_errors: table_rows += html.render_tr( html.render_td(html.render_tt(user_id)) + html.render_td(str(exception))) error_message += html.render_table(table_rows) + html.render_br() html.show_error(error_message)
def title(self): if self._search: return _("Global settings matching '%s'") % escape_html_permissive( self._search) return _("Global settings")
def _show_configuration_variables(self) -> None: search = self._search at_least_one_painted = False html.open_div(class_="globalvars") for group, config_variables in self.iter_all_configuration_variables(): header_is_painted = False # needed for omitting empty groups for config_variable in config_variables: varname = config_variable.ident() valuespec = config_variable.valuespec() if self._show_only_modified and varname not in self._current_settings: continue help_text = valuespec.help() or "" title_text = valuespec.title() or "" if (search and search not in group.title().lower() and search not in config_variable.domain().ident().lower() and search not in varname and search not in help_text.lower() and search not in title_text.lower()): continue # skip variable when search is performed and nothing matches at_least_one_painted = True if not header_is_painted: # always open headers when searching forms.header(group.title(), isopen=search or self._show_only_modified) header_is_painted = True default_value = self._default_values[varname] edit_url = watolib.folder_preserving_link([ ("mode", self.edit_mode_name), ("varname", varname), ("site", request.var("site", "")), ]) title = html.render_a( title_text, href=edit_url, class_="modified" if varname in self._current_settings else None, title=escaping.strip_tags(help_text), ) if varname in self._current_settings: value = self._current_settings[varname] elif varname in self._global_settings: value = self._global_settings[varname] else: value = default_value try: to_text = valuespec.value_to_html(value) if isinstance(to_text, str): to_text = escape_html_permissive(to_text) except Exception: logger.exception("error converting %r to text", value) to_text = html.render_error( _("Failed to render value: %r") % value) # Is this a simple (single) value or not? change styling in these cases... simple = True if "\n" in to_text or "<td>" in to_text: simple = False forms.section(title, simple=simple) if varname in self._current_settings: modified_cls: Optional[str] = "modified" value_title: Optional[str] = _( "This option has been modified.") elif varname in self._global_settings: modified_cls = "modified globally" value_title = _( "This option has been modified in global settings.") else: modified_cls = None value_title = None if is_a_checkbox(valuespec): html.open_div(class_=[ "toggle_switch_container", modified_cls, "on" if value else None ]) html.toggle_switch( enabled=value, help_txt=_("Immediately toggle this setting"), href=makeactionuri(request, transactions, [("_action", "toggle"), ("_varname", varname)]), class_=modified_cls, title=value_title, ) html.close_div() else: html.a(to_text, href=edit_url, class_=modified_cls, title=value_title) if header_is_painted: forms.end() if not at_least_one_painted and search: html.show_message( _("Did not find any global setting matching your search.")) html.close_div()