コード例 #1
0
    def _format_element_aux(val, depth=0):
        if val is None:
            return ""

        try:
            int(val)
            return val
        except:
            pass

        try:
            float(val)
            return val
        except:
            pass

        if isinstance(val, dict):
            subTable = ""
            # TODO: Consider using six.iteritems.
            for subKey, subVal in val.items():
                subTd = _format_pair(subKey, subVal, depth + 1)
                if subTd:
                    subTable += "<tr>%s</tr>" % subTd
            return "<table border='0'>%s</table>" % subTable

        # Note: Recursive list are not very visible.
        if isinstance(val, (list, tuple)):
            # If this is an empty list or tuple.
            if not val:
                # return "(Empty)"
                # Empty set character in UTF8
                return "{" + "&#x2205;" + "}"
            if depth % 2 == 0:
                subTable = ""
                for subElement in val:
                    subTd = _format_element(subElement, depth + 1)
                    subTable += "<tr>%s</tr>" % subTd
                return "<table border='0'>%s</table>" % subTable
            else:
                subTable = ""
                for subElement in val:
                    subTd = _format_element(subElement, depth + 1)
                    subTable += subTd
                return "<table border='0'><tr>%s</tr></table>" % subTable

        try:
            decodVal = json.loads(val)
            return _format_element_aux(decodVal, depth + 1)

        except ValueError:
            # It is a string which cannot be converted to json.
            val = lib_util.html_escape(val)
            return lib_exports.StrWithBr(val)
        except TypeError:
            # "Expected a string or buffer"
            # It is not a string, so it could be a datetime.datetime
            val = lib_util.html_escape(str(val))
            return lib_exports.StrWithBr(val)
        return "_format_element failure"
コード例 #2
0
 def KeyValuePairEncode(kvPair):
     (aKeyWrd, aVal) = re.split(" *= *", kvPair)
     # sys.stderr.write("KeyValuePairEncode aKeyWrd=%s\n"%aKeyWrd)
     if aKeyWrd in odbcKeysConfidential:
         aVal = lib_util.html_escape(aVal)  # SHOULD BE CRYPTED
     elif aKeyWrd not in odbcKeysUncoded:
         aVal = lib_util.html_escape(aVal)
     return aKeyWrd.upper() + "~" + aVal
コード例 #3
0
def ErrorMessageHtml(message):
    if globalErrorMessageEnabled:
        ERROR("ErrorMessageHtml %s. Exiting.", message)
        # If we are in Json mode, this returns a special json document with the error message.
        try:
            qry = os.environ["QUERY_STRING"]
            # FIXME: The cgi variable might be anywhere in the query string, not only at the end.
            isJson = qry.endswith("mode=json")
            if isJson:
                lib_exports.WriteJsonError(message)
                sys.exit(0)

        except KeyError:
            pass

        # 'SERVER_SOFTWARE': 'SimpleHTTP/0.6 Python/2.7.10', 'WSGIServer/0.2'
        try:
            server_software = os.environ['SERVER_SOFTWARE']
        except KeyError:
            server_software = "Unknown_SERVER_SOFTWARE"
        lib_util.InfoMessageHtml(message)
        DEBUG("ErrorMessageHtml about to leave. server_software=%s" %
              server_software)

        if server_software.find('WSGIServer') >= 0:
            # WSGI server is persistent and should not exit.
            raise RuntimeError("Server software=" + server_software)
        else:
            sys.exit(0)
    else:
        # Instead of exiting, it throws an exception which can be used by merge_scripts.py
        DEBUG("ErrorMessageHtml DISABLED")
        # It might be displayed in a HTML document.
        message_clean = lib_util.html_escape(message)
        raise Exception("ErrorMessageHtml raised:%s\n" % message_clean)
コード例 #4
0
ファイル: __init__.py プロジェクト: rchateauneu/survol
def EntityName(entity_ids_arr):
    entity_id = entity_ids_arr[0]
    try:
        resu = lib_util.html_escape(entity_id)
        return resu
    except TypeError as exc:
        logging.error("CANNOT DECODE: symbol=(%s):%s", entity_id, str(exc))
        return entity_id
コード例 #5
0
ファイル: __init__.py プロジェクト: rchateauneu/survol
def EntityName(entity_ids_arr):
    """This is dynamically called from the function _entity_array_to_label() in lib_naming.py.
    It returns a printable string, given the url arguments."""

    # TODO: Problem, this is not compatible with variable arguments.
    resu = entity_ids_arr[0]
    resu = lib_util.html_escape(resu)
    resu = _stripblanks(resu)
    return resu
コード例 #6
0
def EntityName(entity_ids_arr):
    entity_id = entity_ids_arr[0]
    try:
        # Trailing padding.
        resu = lib_util.Base64Decode(entity_id)
        # TODO: Should have a more generic solution: i.e. always b64 encode CGI-incompatible strings.
        # See lib_uris.SymbolUri which does the encoding.
        resu = lib_util.html_escape(resu)
        return resu
    except TypeError:
        exc = sys.exc_info()[1]
        ERROR("CANNOT DECODE: symbol=(%s):%s", entity_id, str(exc))
        return entity_id
コード例 #7
0
def EntityName(entity_ids_arr):
    entity_id = entity_ids_arr[0]
    try:
        # Trailing padding.
        # TODO: Encoding is done in lib_uris.ClassUri : The encoding should be more generic.
        # TODO: ... and done only when the content is CGI-incompatible.
        # TODO: Or do just like sources_types.sql.query.MakeUri
        resu = lib_util.Base64Decode(entity_id)
        resu = lib_util.html_escape(resu)
        return resu
    except TypeError as exc:
        ERROR("CANNOT DECODE: class=(%s):%s", entity_id, str(exc))
        return entity_id
コード例 #8
0
def EntityNameUtil(textPrefix, sqlQuery):
    resu = lib_util.Base64Decode(sqlQuery)
    resu = lib_util.html_escape(resu)
    resu = stripblanks(resu)

    lenFilNam = len(textPrefix)
    lenResu = len(resu)
    lenTot = lenFilNam + lenResu
    lenMaxi = 50
    lenDiff = lenTot - lenMaxi
    if lenDiff > 0:
        lenResu -= lenDiff
        if lenResu < 30:
            lenResu = 30

        return textPrefix + ":" + resu[:lenResu] + "..."
    else:
        return textPrefix + ":" + resu
コード例 #9
0
ファイル: __init__.py プロジェクト: rchateauneu/survol
def EntityNameUtil(text_prefix, sql_query):
    """Only cosmetic reasons: The displayed text should not be too long, when used as a title."""
    resu = lib_util.html_escape(sql_query)
    resu = _stripblanks(resu)

    len_fil_nam = len(text_prefix)
    len_resu = len(resu)
    len_tot = len_fil_nam + len_resu
    len_maxi = 50
    len_diff = len_tot - len_maxi
    if len_diff > 0:
        len_resu -= len_diff
        if len_resu < 30:
            len_resu = 30

        return text_prefix + ":" + resu[:len_resu] + "..."
    else:
        return text_prefix + ":" + resu
コード例 #10
0
def ErrorMessageHtml(message):
    """
    This is called by CGI scripts to leave with an error message.
    At this stage, the CGI scripts did not write anything to stdout.
    Therefore, it is possible to return any MIME document.
    The challenge is to return an error message in the expected output format: html, json, rdf etc...
    """
    exc_stack = traceback.format_exc()
    for one_line in exc_stack.split("\n"):
        logging.error("Exception line: %s", one_line)

    if globalErrorMessageEnabled:
        logging.error("ErrorMessageHtml %s. Exiting.", message)
        try:
            # Use RequestUri() instead of "REQUEST_URI", because this CGI environment variable
            # is not set in minimal HTTP servers such as CGIHTTPServer.
            request_uri = lib_util.RequestUri()
            url_mode = lib_util.get_url_mode(request_uri)
            logging.error("request_uri=%s url_mode=%s" % (request_uri, url_mode))
            if url_mode == "json":
                # If we are in Json mode, this returns a special json document with the error message.
                lib_export_json.write_json_error(message)
                sys.exit(0)
            if url_mode == "rdf":
                # If we are in Json mode, this returns a special RDF document with the error message.
                lib_export_ontology.WriteRdfError(message, request_uri)
                sys.exit(0)
        except KeyError:
            pass

        lib_util.InfoMessageHtml(message)

        if _is_wsgi():
            # WSGI server is persistent and should not exit.
            raise RuntimeError("WSGI server should not exit")
        else:
            sys.exit(0)
    else:
        # Instead of exiting, it throws an exception which can be used by merge_scripts.py
        logging.debug("ErrorMessageHtml DISABLED")
        # It might be displayed in a HTML document.
        message_clean = lib_util.html_escape(message)
        raise Exception("ErrorMessageHtml raised:%s" % message_clean)
コード例 #11
0
    def ProcessCollapsedProperties(propNam):
        dictCollapsedSubjectsToObjectLists = dict_props_collapsed_subjects_to_object_lists[
            propNam]
        logfil.write(TimeStamp() +
                     " Rdf2Dot: dictCollapsedSubjectsToObjectLists=%d.\n" %
                     (len(dictCollapsedSubjectsToObjectLists)))

        for subjUrl, nodLst in lib_util.six_iteritems(
                dictCollapsedSubjectsToObjectLists):
            subjNam = _rdf_node_to_dot_label(subjUrl)

            subjNamTab = CollapsedLabel(propNam, subjNam)
            try:
                # TODO: This logic adds an extra level of node: Try to flatten the tree.
                subjNam = SubjNamFromCollapsed(propNam, subjNam)
            except KeyError:
                pass

            # This points from the subject to the table containing the objects.
            # TODO: This color should be a parameter.
            stream.write(_pattern_edge_oriented %
                         (subjNam, subjNamTab, "GREEN", propNam))

            (labText, subjEntityGraphicClass,
             entity_id) = lib_naming.ParseEntityUri(subjUrl)

            # At the moment, two passes are necessary:
            # * A first pass to create the compte list of fields, because they might be a bit different
            #   from one record to the other. The column names pf these fields get an unique index number
            #   and can therefore be sorted.
            # * A second pass uses these result, to display the lines.
            #
            # This could be faster by assuming that the first ten columns have all the fields.
            # We could then start the second pass, and if an undetected column is found,
            # then restart from scratch.

            # Unique columns of the descendant of this subject.
            rawFieldsKeys = set()
            for obj in nodLst:
                # One table per node.
                rawFieldsKeys.update(fld[0] for fld in fieldsSet[obj])

            # sys.stderr.write("rawFieldsKeys BEFORE =%s\n" % str(rawFieldsKeys) )

            # Mandatory properties must come at the beginning of the columns of the header, with first indices.
            # BUG: Si on retire html de cette liste alors qu il y a des valeurs, colonnes absentes.
            # S il y a du html ou du RDF, on veut que ca vienne en premier.
            fieldsKeysOrdered = []
            for fldPriority in _flat_properties_list:
                try:
                    # Must always be appended. BUT IF THERE IS NO html_data, IS IT WORTH ?
                    # TODO: Remove if not HTML and no sub-rdf. CGIPROP

                    # If the property is never used, exception then next property.
                    rawFieldsKeys.remove(fldPriority)
                    fieldsKeysOrdered.append(fldPriority)
                except KeyError:
                    pass

            # This one is always removed because its content is concatenated at the first column.
            for fldToRemove in [pc.property_information]:
                try:
                    rawFieldsKeys.remove(fldToRemove)
                except KeyError:
                    pass

            # Appends rest of properties, sorted.
            fieldsKeys = fieldsKeysOrdered + sorted(rawFieldsKeys)

            # sys.stderr.write("fieldsKeys=%s\n" % str(fieldsKeys) )

            # This assumes that the header columns are sorted.
            keyIndices = {
                nameKey: indexKey
                for (indexKey, nameKey) in enumerate(fieldsKeys, 1)
            }

            numberKeys = len(keyIndices) + 1

            # Apparently, no embedded tables.
            dictHtmlLines = dict()
            for objUri in nodLst:
                # One table per node.
                subObjId = _rdf_node_to_dot_label(objUri)

                # Beware "\L" which should not be replaced by "<TABLE>" but this is not the right place.
                subNodUri = objUri.replace('&', '&amp;')

                try:
                    (subObjNam, subEntityGraphicClass,
                     subEntityId) = lib_naming.ParseEntityUriShort(objUri)
                except UnicodeEncodeError:
                    WARNING("UnicodeEncodeError error:%s", objUri)
                    (subObjNam, subEntityGraphicClass,
                     subEntityId) = ("Utf problem1", "Utf problem2",
                                     "Utf problem3")

                # sys.stderr.write("subEntityGraphicClass=%s\n"%subEntityGraphicClass)

                # If this is a script, always displayed on white, even if related to a specific entity.
                # THIS IS REALLY A SHAME BECAUSE WE JUST NEED THE ORIGINAL PROPERTY.
                if objUri.find("entity.py") < 0:
                    objColor = "#FFFFFF"
                else:
                    objColor = lib_patterns.EntityClassToColor(
                        subEntityGraphicClass)
                # This lighter cololor for the first column.
                objColorLight = lib_patterns.ColorLighter(objColor)

                # Some colors a bit clearer ? Or take the original color of the class ?
                td_bgcolor_plain = '<td BGCOLOR="%s" ' % objColor
                td_bgcolor_light = '<td BGCOLOR="%s" ' % objColorLight
                td_bgcolor = td_bgcolor_plain

                # Some columns might not have a value. The first column is for the key.
                columns = [td_bgcolor + " ></td>"] * numberKeys

                # Just used for the vertical order of lines, one line per object.
                title = ""

                # TODO: CGIPROP. This is not a dict, the same key can appear several times ?
                for (key, val) in fieldsSet[objUri]:
                    if key == pc.property_information:
                        # This can be a short string only.
                        title += val
                        continue

                    # TODO: This is hard-coded.
                    if is_flat_property(key):

                        # In fact, it might also be an internal URL with "entity.py"
                        if lib_kbase.IsLiteral(val):
                            if isinstance(val.value, (list, tuple)):
                                strHtml = _format_element_aux(val.value)
                                DEBUG("val.value=%s", strHtml)
                                tmpCell = td_bgcolor + 'align="left">%s</td>' % strHtml
                            else:
                                tmpCell = td_bgcolor + 'align="left">%s</td>' % val.value
                        else:
                            # This displays objects in a table: The top-level object must be
                            # in the same host, so there is no need to display a long label.
                            valTitle = lib_naming.ParseEntityUriShort(val)[0]
                            assert isinstance(valTitle, lib_util.six_text_type)

                            # There might be non-ascii characters such as accents etc...
                            try:
                                valTitle.encode('ascii')
                            except UnicodeEncodeError:
                                valTitle = "Not ascii"

                            valTitleUL = lib_exports.DotUL(valTitle)
                            tmpCell = td_bgcolor + 'href="%s" align="left" >%s</td>' % (
                                val, valTitleUL)

                    else:
                        try:
                            float(val)
                            tmpCell = td_bgcolor + 'align="right">%s</td>' % val
                        except:
                            # Wraps the string if too long. Can happen only with a literal.
                            tmpCell = td_bgcolor + 'align="left">%s</td>' % lib_exports.StrWithBr(
                                val)

                    idxKey = keyIndices[key]
                    columns[idxKey] = tmpCell

                if title:
                    title_key = title
                else:
                    title_key = subObjNam

                # Maybe the first column is a literal ?
                if subEntityId != "PLAINTEXTONLY":
                    # WE SHOULD PROBABLY ESCAPE HERE TOO.
                    columns[
                        0] = td_bgcolor_light + 'port="%s" href="%s" align="LEFT" >%s</td>' % (
                            subObjId, subNodUri, title_key)
                else:
                    subNodUri = lib_util.html_escape(subNodUri)
                    columns[
                        0] = td_bgcolor_light + 'port="%s" align="LEFT" >%s</td>' % (
                            subObjId, subNodUri)

                # Several scripts might have the same help text, so add a number.
                # "Title" => "Title"
                # "Title" => "Title/2"
                # "Title" => "Title/3" etc...
                # Beware that it is quadratic with the number of scripts with identical info.
                title_idx = 2
                title_uniq = title_key
                while title_uniq in dictHtmlLines:
                    title_uniq = "%s/%d" % (title_key, title_idx)
                    title_idx += 1

                # TODO: L'ordre est base sur les chaines mais devrait etre base sur le contenu. Exemple:
                # TODO: "(TUT_UnixProcess) Handle=10" vient avant "(TUT_UnixProcess) Handle=2"
                # TODO: title_uniq devrait etre plutot la liste des proprietes.
                # TODO: By clicking on the column names, we could change the order.
                dictHtmlLines[title_uniq] = "".join(columns)

            # Replace the first column by more useful information.
            numNodLst = len(nodLst)

            # WBEM and WMI classes have the syntax: "ns1/ns2/ns3:class" and the class it self can have base classes.
            # Survol classes have the syntax: "dir/dir/dir/class": This considers that namespaces are not really
            # necessary and can be replaced by classes. Also, there is a one-to-one match between the class inheritance
            # tree and its directory.
            # If Survol had to be started from scratch, there would be one Python class per survol class,
            # and they would be stored in the top dir "root/cimv2" ... it is not too late !
            #
            # This strips the upper directories: "mysql/instance" or "oracle/table", if this is a Survol class
            eltNam = subEntityGraphicClass.split("/")[-1]
            # This strips the namespace: "root/cimv2:CIM_LogicalElement", if this is a WBEM or WMI class.
            eltNam = eltNam.split(":")[-1]
            if not eltNam:
                # TODO: This is not the right criteria. Must select if we are listing scripts.
                eltNam = "script"

            eltNamPlural = lib_grammar.ToPlural(eltNam, numNodLst)
            txtElements = "%d %s" % (numNodLst, eltNamPlural)
            header = '<td border="1">' + lib_exports.DotBold(
                txtElements) + "</td>"

            # TODO: Replace each column name with a link which sorts the line based on this column.
            # The order of columns could be specified with an extra cgi argument with the columns names.
            for key in fieldsKeys:
                columnTitle = lib_kbase.qname(key, grph)
                columnTitle = columnTitle.replace("_", " ").capitalize()
                header += "<td border='1'>" + lib_exports.DotBold(
                    columnTitle) + "</td>"
            # With an empty key, it comes first when sorting.
            dictHtmlLines[""] = header

            # MAYBE SHOULD BE DONE TWICE !!!!! SEE ALSO ELSEWHERE !!!!
            subjUrlClean = subjUrl.replace('&', '&amp;')

            # BEWARE: The shape and the color of this HTML table is from the subjects,
            # because the elements can be of different classes, even if the share the same predicate.
            # TODO: Each row should have its own color according to its class.
            numFields = len(fieldsKeys) + 1

            # The rows of this HTML table could belong to different classes:
            # What the shared is the predicate. Hence, the predicate, property name is used as a title.
            propNamPlural = lib_grammar.ToPlural(propNam, None)
            helpText = "List of " + propNamPlural + " in " + labText

            # TODO: Le title and the content are not necessarily of the same class.
            # labTextWithBr is the first line of the table containing nodes linked with the
            # same property. Unfortunately we have lost this property.
            labText = lib_exports.TruncateInSpace(labText, 30)
            labTextWithBr = lib_exports.StrWithBr(labText)
            labTextWithBr += ": " + propNam

            if entity_id == "PLAINTEXTONLY":
                subjUrlClean = ""

            # This color is the table's contour.
            lib_patterns.WritePatterned(stream, subjEntityGraphicClass,
                                        subjNamTab, helpText, '"#000000"',
                                        subjUrlClean, numFields, labTextWithBr,
                                        dictHtmlLines)
コード例 #12
0
def EntityName(entity_ids_arr):
    resu = lib_util.Base64Decode(entity_ids_arr[0])
    resu = lib_util.html_escape(resu)
    resu = stripblanks(resu)
    return resu
コード例 #13
0
 def ValueDisplay(self, valueClear):
     return lib_util.html_escape(valueClear)
コード例 #14
0
    def _process_collapsed_properties(prop_nam):
        dict_collapsed_subjects_to_object_lists = dict_props_collapsed_subjects_to_object_lists[prop_nam]
        logfil.write(lib_util.TimeStamp()+" Rdf2Dot: dict_collapsed_subjects_to_object_lists=%d.\n"
                     % len(dict_collapsed_subjects_to_object_lists))

        for subj_url, nod_lst in six.iteritems(dict_collapsed_subjects_to_object_lists):
            subj_nam = _rdf_node_to_dot_label(subj_url)

            subj_nam_tab = collapsed_label(prop_nam, subj_nam)
            try:
                # TODO: This logic adds an extra level of node: Try to flatten the tree.
                subj_nam = subj_nam_from_collapsed(prop_nam, subj_nam)
            except KeyError:
                pass

            # This points from the subject to the table containing the objects.
            # TODO: This color should be a parameter.
            stream.write(_pattern_edge_oriented % (subj_nam, subj_nam_tab, "GREEN", prop_nam))

            lab_text, subj_entity_graphic_class, entity_id = lib_naming.ParseEntityUri(subj_url)

            # At the moment, two passes are necessary:
            # * A first pass to create the total list of fields, because they might be a bit different
            #   from one record to the other. The column names of these fields get an unique index number
            #   and can therefore be sorted.
            # * A second pass uses these result, to display the lines.
            #
            # This could be faster by assuming that the first ten or twenty columns have all the fields.
            # We could then start the second pass, and if an undetected column is found,
            # then restart from the beginning, as it is done now.

            # Unique columns of the descendant of this subject.
            raw_fields_keys = set()
            for obj in nod_lst:
                # One table per node.
                raw_fields_keys.update(fld[0] for fld in fields_set[obj])

            # Mandatory properties must come at the beginning of the columns of the header, with first indices.
            fields_keys_ordered = []
            for fld_priority in _flat_properties_list:
                try:
                    # Must always be appended. BUT IF THERE IS NO html_data, IS IT WORTH ?
                    # If the property is never used, exception then next property.
                    raw_fields_keys.remove(fld_priority)
                    fields_keys_ordered.append(fld_priority)
                except KeyError:
                    pass

            # This one is always removed because its content is concatenated at the first column.
            for fld_to_remove in [pc.property_information]:
                try:
                    raw_fields_keys.remove(fld_to_remove)
                except KeyError:
                    pass

            # Appends rest of properties which are the column names, alphabetically sorted.
            fields_keys = fields_keys_ordered + sorted(raw_fields_keys)

            # This assumes that the header columns are sorted by alphabetical order.
            key_indices = {name_key: index_key for (index_key, name_key) in enumerate(fields_keys, 1)}

            number_keys = len(key_indices)+1

            # Apparently, no embedded tables.
            dict_html_lines = dict()
            for obj_uri in nod_lst:
                # One table per node.
                sub_obj_id = _rdf_node_to_dot_label(obj_uri)

                # Beware "\L" which should not be replaced by "<TABLE>" but this is not the right place.
                sub_nod_uri = obj_uri.replace('&', '&amp;')

                try:
                    sub_obj_nam, sub_entity_graphic_class, sub_entity_id = lib_naming.parse_entity_uri_short(obj_uri)
                except UnicodeEncodeError:
                    logging.warning("UnicodeEncodeError error:%s", obj_uri)
                    sub_obj_nam, sub_entity_graphic_class, sub_entity_id = ("Utf err 1", "Utf err 2", "Utf err 3")

                # If this is a script, always displayed on white, even if related to a specific entity.
                # THIS IS REALLY A SHAME BECAUSE WE JUST NEED THE ORIGINAL PROPERTY.
                if obj_uri.find("entity.py") < 0:
                    obj_color_plain = "#FFFFFF"
                else:
                    obj_color_plain = lib_patterns.EntityClassToColor(sub_entity_graphic_class)
                # This lighter color for the first column.
                obj_color_light = lib_patterns.color_lighter(obj_color_plain)

                # Some colors a bit clearer ? Or take the original color of the class ?
                td_bgcolor_plain = '<td BGCOLOR="%s" ' % obj_color_plain
                td_bgcolor_light = '<td BGCOLOR="%s" ' % obj_color_light

                # Some columns might not have a value. The first column is for the key.
                html_columns = [td_bgcolor_plain + " ></td>"] * number_keys

                # Just used for the vertical order of lines, one line per object.
                concatenated_info_values = ""

                for key, val in fields_set[obj_uri]:
                    # TODO: This property is by default the sorting key:
                    # TODO: This can be a parameter for lists of classes <MY_Class>
                    # TODO: ... by adding triplets of the form: (<MY_Class>, sorting_key, pc.property_information)
                    if key == pc.property_information:
                        # This can be a short string only.
                        # Instead of concatenation, consider a list, or use an unique delimiter.
                        concatenated_info_values += val
                        # If there is a key, it overrides
                        sub_entity_id = "NOT_" + "PLAINTEXTONLY" # val
                        continue

                    # TODO: This is hard-coded.
                    if is_flat_property(key) :
                        # In fact, it might also be an internal URL with "entity.py"
                        if lib_kbase.IsLiteral(val):
                            if isinstance(val.value, (list, tuple)):
                                str_html = _format_element_aux(val.value)
                                tmp_cell = 'align="left">%s</td>' % str_html
                            else:
                                tmp_cell = 'align="left">%s</td>' % val.value
                        else:
                            # This displays objects in a table: The top-level object must be
                            # in the same host, so there is no need to display a long label.
                            val_title = lib_naming.parse_entity_uri_short(val)[0]

                            assert isinstance(val_title, (six.text_type, six.binary_type))

                            # This could probably be replaced by "str"
                            # There might be non-ascii characters such as accents etc...
                            try:
                                val_title.encode('ascii')
                            except UnicodeEncodeError:
                                val_title = "Not ascii"

                            val_title_ul = _dot_ul(val_title)
                            tmp_cell = 'href="%s" align="left" >%s</td>' % (val, val_title_ul)
                    else:
                        try:
                            float(val)
                            tmp_cell = 'align="right">%s</td>' % val
                        except:
                            # Wraps the string if too long. Can happen only with a literal.
                            tmp_cell = 'align="left">%s</td>' % _str_with_br(val)

                    idx_key = key_indices[key]
                    html_columns[idx_key] = td_bgcolor_plain + tmp_cell

                if concatenated_info_values:
                    title_key = concatenated_info_values
                else:
                    title_key = sub_obj_nam

                # Maybe the first column is a literal, and not an object ?
                if sub_entity_id != "PLAINTEXTONLY":
                    # TODO: WE SHOULD PROBABLY ESCAPE HERE TOO.
                    # For example, this displays the column labelled with pc.property_information
                    tmp_col_0 = 'port="%s" href="%s" align="LEFT" >%s</td>' % (sub_obj_id, sub_nod_uri, title_key)
                else:
                    sub_nod_uri = lib_util.html_escape(sub_nod_uri)
                    # For example, this displays the title of another table: Typically sub-scripts.
                    # The title itself is not an URL.
                    tmp_col_0 = 'port="%s" align="LEFT" >%s</td>' % (sub_obj_id, sub_nod_uri)
                html_columns[0] = td_bgcolor_light + tmp_col_0

                # concatenated_info_values

                # Several scripts might have the same help text, so add a number.
                # "Title" => "Title"
                # "Title" => "Title/2"
                # "Title" => "Title/3" etc...
                # Beware that it is quadratic with the number of scripts with identical info.
                title_idx = 2
                title_key_uniq = title_key
                while title_key_uniq in dict_html_lines:
                    title_key_uniq = "%s/%d" % (title_key, title_idx)
                    title_idx += 1

                # TODO: The sorting order is based on these strings but should rather be based on the content.
                # TODO: For example, "(TUT_UnixProcess) Handle=10" comes before "(TUT_UnixProcess) Handle=2".
                # TODO: This is later sorted by the function lib_util.natural_sort_list.
                # TODO: Or: title_key_uniq should rather be replaced by the list of properties, for example.
                # TODO: By clicking on the column names, we could change the order.
                # TODO: Another possibility is to have a "key" metadata which would replace title_key_uniq.
                dict_html_lines[title_key_uniq] = "".join(html_columns)

            # Replace the first column by more useful information.
            num_nod_lst = len(nod_lst)

            # WBEM and WMI classes have the syntax: "ns1/ns2/ns3:class" and the class it self can have base classes.
            # Survol classes have the syntax: "dir/dir/dir/class": This considers that namespaces are not really
            # necessary and can be replaced by classes. Also, there is a one-to-one match between the class inheritance
            # tree and its directory.
            # If Survol had to be started from scratch, there would be one Python class per survol class,
            # and they would be stored in the top dir "root/cimv2" ... it is not too late !
            #
            # This strips the upper directories: "mysql/instance" or "oracle/table", if this is a Survol class
            elt_nam = sub_entity_graphic_class.split("/")[-1]
            # This strips the namespace: "root/cimv2:CIM_LogicalElement", if this is a WBEM or WMI class.
            elt_nam = elt_nam.split(":")[-1]
            if not elt_nam:
                # TODO: This is not the right criteria. Must select if we are listing scripts.
                elt_nam = "script"

            elt_nam_plural = lib_grammar.to_plural(elt_nam, num_nod_lst)
            txt_elements = "%d %s" % (num_nod_lst, elt_nam_plural)
            header = '<td border="1">%s</td>' % _dot_bold(txt_elements)

            # TODO: Replace each column name with a link which sorts the line based on this column.
            # The order of columns could be specified with an extra cgi argument with the columns names.
            for key in fields_keys:
                column_title = lib_kbase.qname(key, grph)
                column_title = column_title.replace("_"," ").capitalize()
                header += "<td border='1'>%s</td>" % _dot_bold(column_title)
            # With an empty key, it comes first when sorting.
            dict_html_lines[""] = header

            # MAYBE SHOULD BE DONE TWICE !!!!! SEE ALSO ELSEWHERE !!!!
            subj_url_clean = subj_url.replace('&', '&amp;')

            # BEWARE: The shape and the color of this HTML table is from the subjects,
            # because the elements can be of different classes, even if the share the same predicate.
            # TODO: Each row should have its own color according to its class.
            num_fields = len(fields_keys)+1

            # The rows of this HTML table could belong to different classes:
            # What the shared is the predicate. Hence, the predicate, property name is used as a title.
            prop_nam_plural = lib_grammar.to_plural(prop_nam, None)
            help_text = "List of " + prop_nam_plural + " in " + lab_text

            # TODO: The title and the content are not necessarily of the same class.
            # lab_text_with_br is the first line of the table containing nodes linked with the
            # same property. Unfortunately we have lost this property.
            lab_text = _truncate_in_space(lab_text, 30)
            lab_text_with_br = _str_with_br(lab_text)
            lab_text_with_br += ": " + prop_nam

            # No object with this script.
            if entity_id == "PLAINTEXTONLY":
                subj_url_clean = ""

            # This color is the table's contour.
            lib_patterns.WritePatterned(
                stream,
                subj_entity_graphic_class,
                subj_nam_tab,
                help_text,
                '"#000000"',
                subj_url_clean,
                num_fields,
                lab_text_with_br,
                dict_html_lines)