diff --git a/frontend_tests/node_tests/filter.js b/frontend_tests/node_tests/filter.js index 447157a48b..7845621e6c 100644 --- a/frontend_tests/node_tests/filter.js +++ b/frontend_tests/node_tests/filter.js @@ -1614,3 +1614,39 @@ test("error_cases", ({override}) => { const predicate = get_predicate([["pm-with", "Joe@example.com"]]); assert.ok(!predicate({type: "private"})); }); + +run_test("is_web_public_compatible", () => { + // tests same as test_is_web_public_compatible from test_message_fetch.py + assert.ok(Filter.is_web_public_compatible([])); + assert.ok(Filter.is_web_public_compatible([{operator: "has", operand: "attachment"}])); + assert.ok(Filter.is_web_public_compatible([{operator: "has", operand: "image"}])); + assert.ok(Filter.is_web_public_compatible([{operator: "search", operand: "magic"}])); + assert.ok(Filter.is_web_public_compatible([{operator: "near", operand: "15"}])); + assert.ok( + Filter.is_web_public_compatible([ + {operator: "id", operand: "15"}, + {operator: "has", operand: "attachment"}, + ]), + ); + assert.ok(Filter.is_web_public_compatible([{operator: "sender", operand: "hamlet@zulip.com"}])); + assert.ok( + !Filter.is_web_public_compatible([{operator: "pm-with", operand: "hamlet@zulip.com"}]), + ); + assert.ok( + !Filter.is_web_public_compatible([ + {operator: "group-pm-with", operand: "hamlet@zulip.com"}, + ]), + ); + assert.ok(Filter.is_web_public_compatible([{operator: "stream", operand: "Denmark"}])); + assert.ok( + Filter.is_web_public_compatible([ + {operator: "stream", operand: "Denmark"}, + {operator: "topic", operand: "logic"}, + ]), + ); + assert.ok(!Filter.is_web_public_compatible([{operator: "is", operand: "starred"}])); + assert.ok(!Filter.is_web_public_compatible([{operator: "is", operand: "private"}])); + assert.ok(Filter.is_web_public_compatible([{operator: "streams", operand: "public"}])); + // Malformed input not allowed + assert.ok(!Filter.is_web_public_compatible([{operator: "has"}])); +}); diff --git a/static/js/filter.js b/static/js/filter.js index a1ca94a714..97405ccdf1 100644 --- a/static/js/filter.js +++ b/static/js/filter.js @@ -1,6 +1,7 @@ import Handlebars from "handlebars/runtime"; import _ from "lodash"; +import * as hash_util from "./hash_util"; import {$t} from "./i18n"; import * as message_edit from "./message_edit"; import * as message_parser from "./message_parser"; @@ -1005,4 +1006,16 @@ export class Filter { static describe(operators) { return Handlebars.Utils.escapeExpression(Filter.describe_unescaped(operators)); } + + static is_web_public_compatible(ops) { + for (const op of ops) { + if (op.operand === undefined) { + return false; + } + if (!hash_util.allowed_web_public_narrows.includes(op.operator)) { + return false; + } + } + return true; + } } diff --git a/static/js/hash_util.js b/static/js/hash_util.js index c788f6769c..0747bc7c53 100644 --- a/static/js/hash_util.js +++ b/static/js/hash_util.js @@ -283,6 +283,8 @@ export const allowed_web_public_narrows = [ ]; export function is_web_public_compatible(hash) { + // Defines which views are supported for spectators. + // This implementation should agree with the similar function in zerver/lib/narrow.py. const web_public_allowed_hashes = [ "", "narrow", // full #narrow hash handled in narrow.is_web_public_compatible diff --git a/zerver/lib/narrow.py b/zerver/lib/narrow.py index d623ff4fec..6cbe5b5e40 100644 --- a/zerver/lib/narrow.py +++ b/zerver/lib/narrow.py @@ -30,6 +30,7 @@ def check_supported_events_narrow_filter(narrow: Iterable[Sequence[str]]) -> Non def is_web_public_compatible(narrow: Iterable[Dict[str, Any]]) -> bool: + # This implementation should agree with the similar function in static/js/hash_utl.js. for element in narrow: operator = element["operator"] if "operand" not in element: