LP#1923225: have search highlighting stored procedure do HTML-escaping
authorGalen Charlton <gmc@equinoxinitiative.org>
Wed, 14 Apr 2021 18:30:39 +0000 (14:30 -0400)
committerGalen Charlton <gmc@equinoxOLI.org>
Tue, 25 May 2021 14:44:38 +0000 (10:44 -0400)
This patch builds on the previous work to have the stored procedures
that produced highlighted and unhighlighted versions of display
attributes HTML-escape the source values, then adjusts the TPAC
and Bootstrap templates to avoid double-escaping.

Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>
Open-ILS/src/sql/Pg/300.schema.staged_search.sql
Open-ILS/src/sql/Pg/upgrade/XXXX.schema.search_highlight_escape_html.sql [new file with mode: 0644]

index ee91178..b1f59d6 100644 (file)
@@ -1385,7 +1385,7 @@ BEGIN
         SELECT  de.id,
                 de.source,
                 de.field,
-                de.value AS value,
+                evergreen.escape_for_html(de.value) AS value,
                 ts_headline(
                     ts_config::REGCONFIG,
                     evergreen.escape_for_html(de.value),
@@ -1462,8 +1462,8 @@ BEGIN
         SELECT  id,
                 source,
                 field,
-                value,
-                value AS highlight
+                evergreen.escape_for_html(value) AS value,
+                evergreen.escape_for_html(value) AS highlight
           FROM  metabib.display_entry
           WHERE source = rid
                 AND NOT (field = ANY (seen));
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.search_highlight_escape_html.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.search_highlight_escape_html.sql
new file mode 100644 (file)
index 0000000..14f7788
--- /dev/null
@@ -0,0 +1,118 @@
+BEGIN;
+
+-- SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+CREATE OR REPLACE FUNCTION search.highlight_display_fields_impl(
+    rid         BIGINT,
+    tsq         TEXT,
+    field_list  INT[] DEFAULT '{}'::INT[],
+    css_class   TEXT DEFAULT 'oils_SH',
+    hl_all      BOOL DEFAULT TRUE,
+    minwords    INT DEFAULT 5,
+    maxwords    INT DEFAULT 25,
+    shortwords  INT DEFAULT 0,
+    maxfrags    INT DEFAULT 0,
+    delimiter   TEXT DEFAULT ' ... '
+) RETURNS SETOF search.highlight_result AS $f$
+DECLARE
+    opts            TEXT := '';
+    v_css_class     TEXT := css_class;
+    v_delimiter     TEXT := delimiter;
+    v_field_list    INT[] := field_list;
+    hl_query        TEXT;
+BEGIN
+    IF v_delimiter LIKE $$%'%$$ OR v_delimiter LIKE '%"%' THEN --"
+        v_delimiter := ' ... ';
+    END IF;
+
+    IF NOT hl_all THEN
+        opts := opts || 'MinWords=' || minwords;
+        opts := opts || ', MaxWords=' || maxwords;
+        opts := opts || ', ShortWords=' || shortwords;
+        opts := opts || ', MaxFragments=' || maxfrags;
+        opts := opts || ', FragmentDelimiter="' || delimiter || '"';
+    ELSE
+        opts := opts || 'HighlightAll=TRUE';
+    END IF;
+
+    IF v_css_class LIKE $$%'%$$ OR v_css_class LIKE '%"%' THEN -- "
+        v_css_class := 'oils_SH';
+    END IF;
+
+    opts := opts || $$, StopSel=</b>, StartSel="<b class='$$ || v_css_class; -- "
+
+    IF v_field_list = '{}'::INT[] THEN
+        SELECT ARRAY_AGG(id) INTO v_field_list FROM config.metabib_field WHERE display_field;
+    END IF;
+
+    hl_query := $$
+        SELECT  de.id,
+                de.source,
+                de.field,
+                evergreen.escape_for_html(de.value) AS value,
+                ts_headline(
+                    ts_config::REGCONFIG,
+                    evergreen.escape_for_html(de.value),
+                    $$ || quote_literal(tsq) || $$,
+                    $1 || ' ' || mf.field_class || ' ' || mf.name || $xx$'>"$xx$ -- "'
+                ) AS highlight
+          FROM  metabib.display_entry de
+                JOIN config.metabib_field mf ON (mf.id = de.field)
+                JOIN search.best_tsconfig t ON (t.id = de.field)
+          WHERE de.source = $2
+                AND field = ANY ($3)
+          ORDER BY de.id;$$;
+
+    RETURN QUERY EXECUTE hl_query USING opts, rid, v_field_list;
+END;
+$f$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION search.highlight_display_fields(
+    rid         BIGINT,
+    tsq_map     TEXT, -- { '(a | b) & c' => '1,2,3,4', ...}
+    css_class   TEXT DEFAULT 'oils_SH',
+    hl_all      BOOL DEFAULT TRUE,
+    minwords    INT DEFAULT 5,
+    maxwords    INT DEFAULT 25,
+    shortwords  INT DEFAULT 0,
+    maxfrags    INT DEFAULT 0,
+    delimiter   TEXT DEFAULT ' ... '
+) RETURNS SETOF search.highlight_result AS $f$
+DECLARE
+    tsq_hstore  TEXT;
+    tsq         TEXT;
+    fields      TEXT;
+    afields     INT[];
+    seen        INT[];
+BEGIN
+    IF (tsq_map ILIKE 'hstore%') THEN
+        EXECUTE 'SELECT ' || tsq_map INTO tsq_hstore;
+    ELSE
+        tsq_hstore := tsq_map::HSTORE;
+    END IF;
+
+    FOR tsq, fields IN SELECT key, value FROM each(tsq_hstore::HSTORE) LOOP
+        SELECT  ARRAY_AGG(unnest::INT) INTO afields
+          FROM  unnest(regexp_split_to_array(fields,','));
+        seen := seen || afields;
+
+        RETURN QUERY
+            SELECT * FROM search.highlight_display_fields_impl(
+                rid, tsq, afields, css_class, hl_all,minwords,
+                maxwords, shortwords, maxfrags, delimiter
+            );
+    END LOOP;
+
+    RETURN QUERY
+        SELECT  id,
+                source,
+                field,
+                evergreen.escape_for_html(value) AS value,
+                evergreen.escape_for_html(value) AS highlight
+          FROM  metabib.display_entry
+          WHERE source = rid
+                AND NOT (field = ANY (seen));
+END;
+$f$ LANGUAGE PLPGSQL ROWS 10;
+
+COMMIT;