예제 #1
0
    def page(self):
        with table_element(title=self._table_title(),
                           searchable=False,
                           sortable=False) as table:

            for key_id, key in sorted(self.keys.items()):
                cert = crypto.load_certificate(crypto.FILETYPE_PEM,
                                               key["certificate"])

                table.row()
                table.cell(_("Actions"), css="buttons")
                if self._may_edit_config():
                    delete_url = html.makeactionuri([("_delete", key_id)])
                    html.icon_button(delete_url, _("Delete this key"),
                                     "delete")
                download_url = html.makeuri_contextless([("mode",
                                                          self.download_mode),
                                                         ("key", key_id)])
                html.icon_button(download_url, _("Download this key"),
                                 "download")
                table.cell(_("Description"), html.render_text(key["alias"]))
                table.cell(_("Created"), cmk.utils.render.date(key["date"]))
                table.cell(_("By"), html.render_text(key["owner"]))
                table.cell(
                    _("Digest (MD5)"),
                    html.render_text(cert.digest("md5").decode("ascii")))
예제 #2
0
파일: utils.py 프로젝트: surajrb/checkmk
def mk_eval(s):
    # type: (Union[bytes, Text]) -> Any
    try:
        d = base64.b64decode(s)
        return pickle.loads(d) if config.wato_legacy_eval else ast.literal_eval(six.ensure_text(d))
    except Exception:
        raise MKGeneralException(_('Unable to parse provided data: %s') % html.render_text(repr(s)))
예제 #3
0
    def _display_log(self, log):
        with table_element(css="data wato auditlog audit",
                           limit=None,
                           sortable=False,
                           searchable=False) as table:
            for entry in log:
                table.row()
                table.cell(_("Time"),
                           html.render_nobr(
                               render.date_and_time(float(entry.time))),
                           css="narrow")
                user = (
                    '<i>%s</i>' %
                    _('internal')) if entry.user_id == '-' else entry.user_id
                table.cell(_("User"),
                           html.render_text(user),
                           css="nobreak narrow")

                table.cell(_("Object type"),
                           entry.object_ref.object_type.name
                           if entry.object_ref else "",
                           css="narrow")
                table.cell(_("Object"),
                           render_object_ref(entry.object_ref) or "",
                           css="narrow")

                text = escaping.escape_text(entry.text).replace("\n", "<br>\n")
                table.cell(_("Summary"), text)

                if self._show_details:
                    diff_text = entry.diff_text.replace(
                        "\n", "<br>\n") if entry.diff_text else ""
                    table.cell(_("Details"), diff_text)
예제 #4
0
    def _show_entry_cells(self, table, ident, entry):
        table.cell(_("Title"), html.render_text(entry["title"]))

        table.cell(_("Conditions"))
        html.open_ul(class_="conditions")
        html.open_li()
        html.write(
            "%s: %s" %
            (_("Folder"), Folder.folder(
                entry["conditions"]["host_folder"]).alias_path()))
        html.close_li()
        html.close_ul()
        html.write(vs_conditions().value_to_text(entry["conditions"]))

        table.cell(_("Editable by"))
        if entry["owned_by"] is None:
            html.write_text(
                _("Administrators (having the permission "
                  "\"Write access to all predefined conditions\")"))
        else:
            html.write_text(self._contact_group_alias(entry["owned_by"]))

        table.cell(_("Shared with"))
        if not entry["shared_with"]:
            html.write_text(_("Not shared"))
        else:
            html.write_text(", ".join(
                [self._contact_group_alias(g) for g in entry["shared_with"]]))
예제 #5
0
파일: utils.py 프로젝트: m4c3/checkMK
def mk_eval(s):
    try:
        if not config.wato_legacy_eval:
            return ast.literal_eval(base64.b64decode(s))
        return pickle.loads(base64.b64decode(s))
    except:
        raise MKGeneralException(_('Unable to parse provided data: %s') % html.render_text(repr(s)))
예제 #6
0
파일: ldap.py 프로젝트: tklecker/checkmk
    def page(self):
        html.open_div(id_="ldap")
        html.open_table()
        html.open_tr()

        html.open_td()
        html.begin_form("connection", method="POST")
        html.prevent_password_auto_completion()
        vs = self._valuespec()
        vs.render_input("connection", self._connection_cfg)
        vs.set_focus("connection")
        html.hidden_fields()
        html.end_form()
        html.close_td()

        html.open_td(style="padding-left:10px;vertical-align:top")
        html.h2(_('Diagnostics'))
        if not html.request.var('_test') or not self._connection_id:
            html.show_message(
                HTML(
                    '<p>%s</p><p>%s</p>' %
                    (_('You can verify the single parts of your ldap configuration using this '
                       'dialog. Simply make your configuration in the form on the left side and '
                       'hit the "Save & Test" button to execute the tests. After '
                       'the page reload, you should see the results of the test here.'),
                     _('If you need help during configuration or experience problems, please refer '
                       'to the <a target="_blank" '
                       'href="https://checkmk.com/checkmk_multisite_ldap_integration.html">'
                       'LDAP Documentation</a>.'))))
        else:
            connection = userdb.get_connection(self._connection_id)
            assert isinstance(connection, LDAPUserConnector)

            for address in connection.servers():
                html.h3("%s: %s" % (_('Server'), address))
                with table_element('test', searchable=False) as table:
                    for title, test_func in self._tests():
                        table.row()
                        try:
                            state, msg = test_func(connection, address)
                        except Exception as e:
                            state = False
                            msg = _('Exception: %s') % html.render_text("%s" % e)
                            logger.exception("error testing LDAP %s for %s", title, address)

                        if state:
                            img = html.render_icon("success", _('Success'))
                        else:
                            img = html.render_icon("failed", _("Failed"))

                        table.cell(_("Test"), title)
                        table.cell(_("State"), img)
                        table.cell(_("Details"), msg)

            connection.disconnect()

        html.close_td()
        html.close_tr()
        html.close_table()
        html.close_div()
예제 #7
0
 def title(self):
     if self._topic and not self._search:
         heading = "%s - %s" % (_("Catalog of Check Plugins"), self._topic_title)
     elif self._search:
         heading = html.render_text("%s: %s" % (_("Check plugins matching"), self._search))
     else:
         heading = _("Catalog of Check Plugins")
     return heading
예제 #8
0
def show_annotations(annotations, av_rawdata, what, avoptions, omit_service):
    annos_to_render = get_relevant_annotations(annotations, av_rawdata, what, avoptions)
    render_date = get_annotation_date_render_function(annos_to_render, avoptions)

    with table_element(title=_("Annotations"), omit_if_empty=True) as table:
        for (site_id, host, service), annotation in annos_to_render:
            table.row()
            table.cell("", css="buttons")
            anno_vars = [
                ("anno_site", site_id),
                ("anno_host", host),
                ("anno_service", service or ""),
                ("anno_from", int(annotation["from"])),
                ("anno_until", int(annotation["until"])),
            ]
            edit_url = html.makeuri(anno_vars)
            html.icon_button(edit_url, _("Edit this annotation"), "edit")
            del_anno = [("_delete_annotation", "1")]  # type: HTTPVariables
            delete_url = html.makeactionuri(del_anno + anno_vars)
            html.icon_button(delete_url, _("Delete this annotation"), "delete")

            if not omit_service:
                if "omit_host" not in avoptions["labelling"]:
                    host_url = "view.py?" + html.urlencode_vars([("view_name", "hoststatus"),
                                                                 ("site", site_id), ("host", host)])
                    table.cell(_("Host"), html.render_a(host, host_url))

                if what == "service":
                    if service:
                        service_url = "view.py?" + html.urlencode_vars([("view_name", "service"),
                                                                        ("site", site_id),
                                                                        ("host", host),
                                                                        ("service", service)])
                        # TODO: honor use_display_name. But we have no display names here...
                        service_name = service
                        table.cell(_("Service"), html.render_a(service_name, service_url))
                    else:
                        table.cell(_("Service"), "")  # Host annotation in service table

            table.cell(_("From"), render_date(annotation["from"]), css="nobr narrow")
            table.cell(_("Until"), render_date(annotation["until"]), css="nobr narrow")
            table.cell("", css="buttons")
            if annotation.get("downtime") is True:
                html.icon(_("This period has been reclassified as a scheduled downtime"),
                          "downtime")
            elif annotation.get("downtime") is False:
                html.icon(
                    _("This period has been reclassified as a not being a scheduled downtime"),
                    "nodowntime")
            table.cell(_("Annotation"), html.render_text(annotation["text"]))
            table.cell(_("Author"), annotation["author"])
            table.cell(_("Entry"), render_date(annotation["date"]), css="nobr narrow")
            if not cmk_version.is_raw_edition():
                table.cell(_("Hide in report"),
                           _("Yes") if annotation.get("hide_from_report") else _("No"))
예제 #9
0
    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 = html.render_text(
                str(text) if not isinstance(text, str) else text)

        htmlcode: HTML = content + HTML(html.drain())

        if isinstance(title, HTML):
            header_title = title
        else:
            if title is None:
                title = ""
            header_title = html.render_text(
                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))
예제 #10
0
    def _display_log(self, log):
        with table_element(css="data wato auditlog audit",
                           limit=None,
                           sortable=False,
                           searchable=False) as table:
            for t, linkinfo, user, _action, text in log:
                table.row()
                table.cell(_("Object"), self._render_logfile_linkinfo(linkinfo))
                table.cell(_("Time"), html.render_nobr(render.date_and_time(float(t))))
                user = ('<i>%s</i>' % _('internal')) if user == '-' else user
                table.cell(_("User"), html.render_text(user), css="nobreak")

                # This must not be attrencoded: The entries are encoded when writing to the log.
                table.cell(_("Change"), text.replace("\\n", "<br>\n"), css="fill")
예제 #11
0
 def _show_entry_cells(self, table, ident, entry):
     table.cell(_("Title"), html.render_text(entry["title"]))
     table.cell(_("Editable by"))
     if entry["owned_by"] is None:
         html.write_text(
             _("Administrators (having the permission "
               "\"Write access to all passwords\")"))
     else:
         html.write_text(self._contact_group_alias(entry["owned_by"]))
     table.cell(_("Shared with"))
     if not entry["shared_with"]:
         html.write_text(_("Not shared"))
     else:
         html.write_text(", ".join([self._contact_group_alias(g) for g in entry["shared_with"]]))
예제 #12
0
    def page(self):
        with table_element(title=self._table_title(),
                           searchable=False,
                           sortable=False) as table:

            for key_id, key in sorted(self.keys.items()):
                cert = crypto.load_certificate(crypto.FILETYPE_PEM,
                                               key["certificate"])

                table.row()
                table.cell(_("Actions"), css="buttons")
                if self._may_edit_config():
                    message = self._delete_confirm_msg()
                    if key["owner"] != config.user.id:
                        message += _(
                            "<br><b>Note</b>: this key has created by user <b>%s</b>"
                        ) % key["owner"]

                    delete_url = make_confirm_link(
                        url=html.makeactionuri([("_delete", key_id)]),
                        message=message,
                    )
                    html.icon_button(delete_url, _("Delete this key"),
                                     "delete")
                download_url = makeuri_contextless(
                    request,
                    [("mode", self.download_mode), ("key", key_id)],
                )
                html.icon_button(download_url, _("Download this key"),
                                 "download")
                table.cell(_("Description"), html.render_text(key["alias"]))
                table.cell(_("Created"), cmk.utils.render.date(key["date"]))
                table.cell(_("By"), html.render_text(key["owner"]))
                table.cell(
                    _("Digest (MD5)"),
                    html.render_text(cert.digest("md5").decode("ascii")))
예제 #13
0
파일: folders.py 프로젝트: tklecker/checkmk
    def page(self):
        if not self._folder.may("read"):
            reason = self._folder.reason_why_may_not("read")
            if reason:
                html.show_message(
                    html.render_icon("autherr", cssclass="authicon") + html.render_text(reason))

        self._folder.show_locking_information()
        self._show_subfolders_of()
        if self._folder.may("read"):
            self._show_hosts()

        if not self._folder.has_hosts():
            if self._folder.is_search_folder():
                html.show_message(_("No matching hosts found."))
            elif not self._folder.has_subfolders() and self._folder.may("write"):
                self._show_empty_folder_menu()
예제 #14
0
 def title(self):
     if self._new:
         return _("Add LDAP connection")
     return _("Edit LDAP connection: %s") % html.render_text(
         self._connection_id)
예제 #15
0
파일: utils.py 프로젝트: tklecker/checkmk
def mk_eval(s: Union[bytes, str]) -> Any:
    try:
        return ast.literal_eval(ensure_str(base64.b64decode(s)))
    except Exception:
        raise MKGeneralException(
            _('Unable to parse provided data: %s') % html.render_text(repr(s)))
예제 #16
0
 def title(self):
     if self._search:
         return _("Global settings matching '%s'") % html.render_text(self._search)
     return _("Global settings")
예제 #17
0
    def _preview(self):
        # type: () -> 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(html.render_text(header))
                attribute_varname = "attribute_%d" % col_num
                if html.request.var(attribute_varname):
                    attribute_method = html.request.get_ascii_input_mandatory(
                        "attribute_varname")
                else:
                    attribute_method = self._try_detect_default_attribute(
                        attributes, header)
                    html.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, html.render_text(cell))

        html.end_form()
예제 #18
0
    def show(self):
        # type: () -> None
        filename = Path(cmk.utils.paths.omd_root).joinpath(
            'var/dokuwiki/data/pages/sidebar.txt')

        html.open_form(id_="wiki_search",
                       onsubmit="cmk.sidebar.wiki_search('%s');" %
                       config.omd_site())
        html.input(id_="wiki_search_field", type_="text", name="wikisearch")
        html.icon_button("#",
                         _("Search"),
                         "wikisearch",
                         onclick="cmk.sidebar.wiki_search('%s');" %
                         config.omd_site())
        html.close_form()
        html.div('', id_="wiki_side_clear")

        start_ul = True
        ul_started = False
        try:
            title = None
            for line in filename.open(encoding="utf-8").readlines():
                line = line.strip()
                if line == "":
                    if ul_started:
                        html.end_foldable_container()
                        start_ul = True
                        ul_started = False
                elif line.endswith(":"):
                    title = line[:-1]
                elif line == "----":
                    pass
                    # html.br()

                elif line.startswith("*"):
                    if start_ul:
                        if title:
                            html.begin_foldable_container("wikisnapin",
                                                          title,
                                                          True,
                                                          title,
                                                          indent=True)
                        else:
                            html.open_ul()
                        start_ul = False
                        ul_started = True

                    erg = re.findall(r'\[\[(.*)\]\]', line)
                    if len(erg) == 0:
                        continue
                    erg = erg[0].split('|')
                    if len(erg) > 1:
                        link = erg[0]
                        name = erg[1]
                    else:
                        link = erg[0]
                        name = erg[0]

                    if link.startswith("http://") or link.startswith(
                            "https://"):
                        simplelink(name, link, "_blank")
                    else:
                        erg = name.split(':')
                        if len(erg) > 0:
                            name = erg[-1]
                        else:
                            name = erg[0]
                        bulletlink(
                            name, "/%s/wiki/doku.php?id=%s" %
                            (config.omd_site(), link))

                else:
                    html.write_text(line)

            if ul_started:
                html.close_ul()
        except IOError:
            html.write_html(
                html.render_p(
                    html.render_text(
                        "To get a navigation menu, you have to create a ") +
                    html.render_a("sidebar",
                                  href="/%s/wiki/doku.php?id=%s" %
                                  (config.omd_site(), _("sidebar")),
                                  target="main") +  #
                    html.render_text(" in your wiki first.")))
예제 #19
0
파일: logwatch.py 프로젝트: selten/checkmk
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(
            html.render_text(
                _("Cannot parse log file %s: %s") % (file_name, e)))

    return log_chunks
예제 #20
0
파일: logwatch.py 프로젝트: selten/checkmk
def do_log_ack(site, host_name, file_name):
    logs_to_ack = []
    if not host_name and not file_name:  # all logs on all hosts
        for this_site, this_host, logs in all_logs():
            for int_filename in logs:
                file_display = form_file_to_ext(int_filename)
                logs_to_ack.append(
                    (this_site, this_host, int_filename, file_display))
        ack_msg = _('all logfiles on all hosts')

    elif host_name and not file_name:  # all logs on one host
        for int_filename in logfiles_of_host(site, host_name):
            file_display = form_file_to_ext(int_filename)
            logs_to_ack.append((site, host_name, int_filename, file_display))
        ack_msg = _('all logfiles of host %s') % host_name

    elif host_name and file_name:  # one log on one host
        int_filename = form_file_to_int(file_name)
        logs_to_ack = [(site, host_name, int_filename,
                        form_file_to_ext(int_filename))]
        ack_msg = _('the log file %s on host %s') % (file_name, host_name)

    else:
        for this_site, this_host, logs in all_logs():
            file_display = form_file_to_ext(file_name)
            if file_name in logs:
                logs_to_ack.append(
                    (this_site, this_host, file_name, file_display))
        ack_msg = _('log file %s on all hosts') % file_name

    title = _("Acknowledge %s") % html.render_text(ack_msg)

    if host_name:
        breadcrumb = make_host_breadcrumb(host_name)
    else:
        breadcrumb = make_simple_page_breadcrumb(MegaMenuMonitoring, title)

    html.header(title, breadcrumb)

    html.begin_context_buttons()
    button_all_logfiles()
    if host_name:
        html.context_button(_("All Logfiles of Host"),
                            html.makeuri([('file', '')]))
    if host_name and file_name:
        html.context_button(_("Back to Logfile"), html.makeuri([]))
    html.end_context_buttons()

    ack = html.request.var('_ack')
    if not html.confirm(
            _("Do you really want to acknowledge %s by <b>deleting</b> all stored messages?"
              ) % ack_msg):
        html.footer()
        return

    if not config.user.may("general.act"):
        html.h1(_('Permission denied'), class_=["error"])
        html.div(_('You are not allowed to acknowledge %s') % ack_msg,
                 class_=["error"])
        html.footer()
        return

    # filter invalid values
    if ack != '1':
        raise MKUserError('_ack', _('Invalid value for ack parameter.'))

    for this_site, this_host, int_filename, display_name in logs_to_ack:
        try:
            acknowledge_logfile(this_site, this_host, int_filename,
                                display_name)
        except Exception as e:
            html.show_error(
                _('The log file <tt>%s</tt> of host <tt>%s</tt> could not be deleted: %s.'
                  ) % (display_name, this_host, e))
            html.footer()
            return

    html.show_message('<b>%s</b><p>%s</p>' %
                      (_('Acknowledged %s') % ack_msg,
                       _('Acknowledged all messages in %s.') % ack_msg))
    html.footer()
예제 #21
0
    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

            html.begin_foldable_container("rule",
                                          "%s" % abs_rulenr,
                                          True,
                                          HTML("<b>Rule #%d</b>" % (abs_rulenr + 1)),
                                          indent=False)
            with table_element("pattern_editor_rule_%d" % abs_rulenr, sortable=False) 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
                        reason_class = 'reason'

                        matched = re.search(pattern, self._match_txt)
                        if matched:

                            # Prepare highlighted search txt
                            match_start = matched.start()
                            match_end = matched.end()
                            disp_match_txt = html.render_text(self._match_txt[:match_start]) \
                                             + html.render_span(self._match_txt[match_start:match_end], class_="match")\
                                             + html.render_text(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
                        reason_class = 'noreason'
                        match_img = 'nmatch'
                        match_title = _('The rule conditions do not match.')

                    table.row(css=reason_class)
                    table.cell(_('Match'))
                    html.icon(match_title, "rule%s" % match_img)

                    cls = ''
                    if match_class == 'match first':
                        cls = 'svcstate state%d' % logwatch.level_state(state)
                    table.cell(_('State'), logwatch.level_name(state), css=cls)
                    table.cell(_('Pattern'), html.render_tt(pattern))
                    table.cell(_('Comment'), html.render_text(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),
                    ("host", self._hostname),
                    ("item", ensure_str(watolib.mk_repr(self._item))),
                    ("rule_folder", folder.path()),
                ])
                html.icon_button(edit_url, _("Edit this rule"), "edit")

            html.end_foldable_container()