refactor: Rename most of "filter" to "linkifier".

After this only the database table, events,
and API endpoints remain.
This commit is contained in:
Abhijeet Prasad Bodas 2021-03-30 15:45:39 +05:30 committed by Tim Abbott
parent f896a7667f
commit 68fe912c63
11 changed files with 144 additions and 149 deletions

View File

@ -232,8 +232,8 @@ from zerver.models import (
get_user_by_id_in_realm_including_cross_realm,
get_user_profile_by_id,
is_cross_realm_bot_email,
linkifiers_for_realm,
query_for_ids,
realm_filters_for_realm,
validate_attachment_request,
)
from zerver.tornado.django_api import send_event
@ -6492,8 +6492,8 @@ def do_mark_hotspot_as_read(user: UserProfile, hotspot: str) -> None:
send_event(user.realm, event, [user.id])
def notify_realm_filters(realm: Realm) -> None:
realm_filters = realm_filters_for_realm(realm.id)
def notify_linkifiers(realm: Realm) -> None:
realm_filters = linkifiers_for_realm(realm.id)
event = dict(type="realm_filters", realm_filters=realm_filters)
send_event(realm, event, active_user_ids(realm.id))
@ -6502,25 +6502,25 @@ def notify_realm_filters(realm: Realm) -> None:
# RegExp syntax. In addition to JS-compatible syntax, the following features are available:
# * Named groups will be converted to numbered groups automatically
# * Inline-regex flags will be stripped, and where possible translated to RegExp-wide flags
def do_add_realm_filter(realm: Realm, pattern: str, url_format_string: str) -> int:
def do_add_linkifier(realm: Realm, pattern: str, url_format_string: str) -> int:
pattern = pattern.strip()
url_format_string = url_format_string.strip()
linkifier = RealmFilter(realm=realm, pattern=pattern, url_format_string=url_format_string)
linkifier.full_clean()
linkifier.save()
notify_realm_filters(realm)
notify_linkifiers(realm)
return linkifier.id
def do_remove_realm_filter(
def do_remove_linkifier(
realm: Realm, pattern: Optional[str] = None, id: Optional[int] = None
) -> None:
if pattern is not None:
RealmFilter.objects.get(realm=realm, pattern=pattern).delete()
else:
RealmFilter.objects.get(realm=realm, pk=id).delete()
notify_realm_filters(realm)
notify_linkifiers(realm)
def get_emails_from_user_ids(user_ids: Sequence[int]) -> Dict[int, str]:

View File

@ -58,7 +58,7 @@ from zerver.models import (
custom_profile_fields_for_realm,
get_default_stream_groups,
get_realm_domains,
realm_filters_for_realm,
linkifiers_for_realm,
)
from zerver.tornado.django_api import get_user_events, request_event_queue
from zproject.backends import email_auth_enabled, password_auth_enabled
@ -252,7 +252,7 @@ def fetch_initial_state_data(
state["realm_emoji"] = realm.get_emoji()
if want("realm_filters"):
state["realm_filters"] = realm_filters_for_realm(realm.id)
state["realm_filters"] = linkifiers_for_realm(realm.id)
if want("realm_user_groups"):
state["realm_user_groups"] = user_groups_in_realm_serialized(realm)

View File

@ -72,9 +72,9 @@ from zerver.models import (
UserGroup,
UserGroupMembership,
UserProfile,
all_realm_filters,
all_linkifiers_for_installation,
get_active_streams,
realm_filters_for_realm,
linkifiers_for_realm,
)
ReturnT = TypeVar("ReturnT")
@ -1780,7 +1780,7 @@ class MarkdownListPreprocessor(markdown.preprocessors.Preprocessor):
OUTER_CAPTURE_GROUP = "linkifier_actual_match"
def prepare_realm_pattern(source: str) -> str:
def prepare_linkifier_pattern(source: str) -> str:
"""Augment a linkifier so it only matches after start-of-string,
whitespace, or opening delimiters, won't match if there are word
characters directly after, and saves what was matched as
@ -1790,8 +1790,8 @@ def prepare_realm_pattern(source: str) -> str:
# Given a regular expression pattern, linkifies groups that match it
# using the provided format string to construct the URL.
class RealmFilterPattern(markdown.inlinepatterns.Pattern):
""" Applied a given realm filter to the input """
class LinkifierPattern(markdown.inlinepatterns.Pattern):
""" Applied a given linkifier to the input """
def __init__(
self,
@ -1799,7 +1799,7 @@ class RealmFilterPattern(markdown.inlinepatterns.Pattern):
format_string: str,
markdown_instance: Optional[markdown.Markdown] = None,
) -> None:
self.pattern = prepare_realm_pattern(source_pattern)
self.pattern = prepare_linkifier_pattern(source_pattern)
self.format_string = format_string
markdown.inlinepatterns.Pattern.__init__(self, self.pattern, markdown_instance)
@ -2088,8 +2088,8 @@ def get_sub_registry(r: markdown.util.Registry, keys: List[str]) -> markdown.uti
return new_r
# These are used as keys ("realm_filters_keys") to md_engines and the respective
# realm filter caches
# These are used as keys ("linkifiers_keys") to md_engines and the respective
# linkifier caches
DEFAULT_MARKDOWN_KEY = -1
ZEPHYR_MIRROR_MARKDOWN_KEY = -2
@ -2103,12 +2103,12 @@ class Markdown(markdown.Markdown):
def __init__(
self,
realm_filters: List[Tuple[str, str, int]],
realm_filters_key: int,
linkifiers: List[Tuple[str, str, int]],
linkifiers_key: int,
email_gateway: bool,
) -> None:
self.realm_filters = realm_filters
self.realm_filters_key = realm_filters_key
self.linkifiers = linkifiers
self.linkifiers_key = linkifiers_key
self.email_gateway = email_gateway
super().__init__(
@ -2231,8 +2231,8 @@ class Markdown(markdown.Markdown):
)
reg.register(LinkInlineProcessor(markdown.inlinepatterns.LINK_RE, self), "link", 60)
reg.register(AutoLink(get_web_link_regex(), self), "autolink", 55)
# Reserve priority 45-54 for realm filters
reg = self.register_realm_filters(reg)
# Reserve priority 45-54 for linkifiers
reg = self.register_linkifiers(reg)
reg.register(markdown.inlinepatterns.HtmlInlineProcessor(ENTITY_RE, self), "entity", 40)
reg.register(
markdown.inlinepatterns.SimpleTagPattern(r"(\*\*)([^\n]+?)\2", "strong"), "strong", 35
@ -2248,12 +2248,12 @@ class Markdown(markdown.Markdown):
reg.register(UnicodeEmoji(unicode_emoji_regex), "unicodeemoji", 0)
return reg
def register_realm_filters(
self, inlinePatterns: markdown.util.Registry
) -> markdown.util.Registry:
for (pattern, format_string, id) in self.realm_filters:
def register_linkifiers(self, inlinePatterns: markdown.util.Registry) -> markdown.util.Registry:
for (pattern, format_string, id) in self.linkifiers:
inlinePatterns.register(
RealmFilterPattern(pattern, format_string, self), f"realm_filters/{pattern}", 45
LinkifierPattern(pattern, format_string, self),
f"linkifiers/{pattern}",
45,
)
return inlinePatterns
@ -2281,7 +2281,7 @@ class Markdown(markdown.Markdown):
return postprocessors
def handle_zephyr_mirror(self) -> None:
if self.realm_filters_key == ZEPHYR_MIRROR_MARKDOWN_KEY:
if self.linkifiers_key == ZEPHYR_MIRROR_MARKDOWN_KEY:
# Disable almost all inline patterns for zephyr mirror
# users' traffic that is mirrored. Note that
# inline_interesting_links is a treeprocessor and thus is
@ -2302,18 +2302,18 @@ class Markdown(markdown.Markdown):
md_engines: Dict[Tuple[int, bool], Markdown] = {}
realm_filter_data: Dict[int, List[Tuple[str, str, int]]] = {}
linkifier_data: Dict[int, List[Tuple[str, str, int]]] = {}
def make_md_engine(realm_filters_key: int, email_gateway: bool) -> None:
md_engine_key = (realm_filters_key, email_gateway)
def make_md_engine(linkifiers_key: int, email_gateway: bool) -> None:
md_engine_key = (linkifiers_key, email_gateway)
if md_engine_key in md_engines:
del md_engines[md_engine_key]
realm_filters = realm_filter_data[realm_filters_key]
linkifiers = linkifier_data[linkifiers_key]
md_engines[md_engine_key] = Markdown(
realm_filters=realm_filters,
realm_filters_key=realm_filters_key,
linkifiers=linkifiers,
linkifiers_key=linkifiers_key,
email_gateway=email_gateway,
)
@ -2326,18 +2326,18 @@ basic_link_splitter = re.compile(r"[ !;\?\),\'\"]")
# function on the URLs; they are expected to be HTML-escaped when
# rendered by clients (just as links rendered into message bodies
# are validated and escaped inside `url_to_a`).
def topic_links(realm_filters_key: int, topic_name: str) -> List[Dict[str, str]]:
def topic_links(linkifiers_key: int, topic_name: str) -> List[Dict[str, str]]:
matches: List[Dict[str, Union[str, int]]] = []
realm_filters = realm_filters_for_realm(realm_filters_key)
linkifiers = linkifiers_for_realm(linkifiers_key)
for realm_filter in realm_filters:
raw_pattern = realm_filter[0]
url_format_string = realm_filter[1]
pattern = prepare_realm_pattern(raw_pattern)
for linkifier in linkifiers:
raw_pattern = linkifier[0]
url_format_string = linkifier[1]
pattern = prepare_linkifier_pattern(raw_pattern)
for m in re.finditer(pattern, topic_name):
match_details = m.groupdict()
match_text = match_details["linkifier_actual_match"]
# We format the realm_filter's url string using the matched text.
# We format the linkifier's url string using the matched text.
# Also, we include the matched text in the response, so that our clients
# don't have to implement any logic of their own to get back the text.
matches += [
@ -2371,35 +2371,32 @@ def topic_links(realm_filters_key: int, topic_name: str) -> List[Dict[str, str]]
return [{k: str(v) for k, v in match.items() if k != "index"} for match in matches]
def maybe_update_markdown_engines(realm_filters_key: Optional[int], email_gateway: bool) -> None:
# If realm_filters_key is None, load all filters
global realm_filter_data
if realm_filters_key is None:
all_filters = all_realm_filters()
all_filters[DEFAULT_MARKDOWN_KEY] = []
for realm_filters_key, filters in all_filters.items():
realm_filter_data[realm_filters_key] = filters
make_md_engine(realm_filters_key, email_gateway)
# Hack to ensure that realm_filters_key is right for mirrored Zephyrs
realm_filter_data[ZEPHYR_MIRROR_MARKDOWN_KEY] = []
def maybe_update_markdown_engines(linkifiers_key: Optional[int], email_gateway: bool) -> None:
# If linkifiers_key is None, load all linkifiers
global linkifier_data
if linkifiers_key is None:
all_linkifiers = all_linkifiers_for_installation()
all_linkifiers[DEFAULT_MARKDOWN_KEY] = []
for linkifiers_key, linkifiers in all_linkifiers.items():
linkifier_data[linkifiers_key] = linkifiers
make_md_engine(linkifiers_key, email_gateway)
# Hack to ensure that linkifiers_key is right for mirrored Zephyrs
linkifier_data[ZEPHYR_MIRROR_MARKDOWN_KEY] = []
make_md_engine(ZEPHYR_MIRROR_MARKDOWN_KEY, False)
else:
realm_filters = realm_filters_for_realm(realm_filters_key)
if (
realm_filters_key not in realm_filter_data
or realm_filter_data[realm_filters_key] != realm_filters
):
# Realm filters data has changed, update `realm_filter_data` and any
# of the existing Markdown engines using this set of realm filters.
realm_filter_data[realm_filters_key] = realm_filters
linkifiers = linkifiers_for_realm(linkifiers_key)
if linkifiers_key not in linkifier_data or linkifier_data[linkifiers_key] != linkifiers:
# Linkifier data has changed, update `linkifier_data` and any
# of the existing Markdown engines using this set of linkifiers.
linkifier_data[linkifiers_key] = linkifiers
for email_gateway_flag in [True, False]:
if (realm_filters_key, email_gateway_flag) in md_engines:
if (linkifiers_key, email_gateway_flag) in md_engines:
# Update only existing engines(if any), don't create new one.
make_md_engine(realm_filters_key, email_gateway_flag)
make_md_engine(linkifiers_key, email_gateway_flag)
if (realm_filters_key, email_gateway) not in md_engines:
if (linkifiers_key, email_gateway) not in md_engines:
# Markdown engine corresponding to this key doesn't exists so create one.
make_md_engine(realm_filters_key, email_gateway)
make_md_engine(linkifiers_key, email_gateway)
# We want to log Markdown parser failures, but shouldn't log the actual input
@ -2561,9 +2558,9 @@ def do_convert(
if message_realm is None:
message_realm = message.get_realm()
if message_realm is None:
realm_filters_key = DEFAULT_MARKDOWN_KEY
linkifiers_key = DEFAULT_MARKDOWN_KEY
else:
realm_filters_key = message_realm.id
linkifiers_key = message_realm.id
if message and hasattr(message, "id") and message.id:
logging_message_id = "id# " + str(message.id)
@ -2575,16 +2572,16 @@ def do_convert(
if message.sending_client.name == "zephyr_mirror":
# Use slightly customized Markdown processor for content
# delivered via zephyr_mirror
realm_filters_key = ZEPHYR_MIRROR_MARKDOWN_KEY
linkifiers_key = ZEPHYR_MIRROR_MARKDOWN_KEY
maybe_update_markdown_engines(realm_filters_key, email_gateway)
md_engine_key = (realm_filters_key, email_gateway)
maybe_update_markdown_engines(linkifiers_key, email_gateway)
md_engine_key = (linkifiers_key, email_gateway)
if md_engine_key in md_engines:
_md_engine = md_engines[md_engine_key]
else:
if DEFAULT_MARKDOWN_KEY not in md_engines:
maybe_update_markdown_engines(realm_filters_key=None, email_gateway=False)
maybe_update_markdown_engines(linkifiers_key=None, email_gateway=False)
_md_engine = md_engines[(DEFAULT_MARKDOWN_KEY, email_gateway)]
# Reset the parser; otherwise it will get slower over time.

View File

@ -2,9 +2,9 @@ import sys
from argparse import ArgumentParser
from typing import Any
from zerver.lib.actions import do_add_realm_filter, do_remove_realm_filter
from zerver.lib.actions import do_add_linkifier, do_remove_linkifier
from zerver.lib.management import CommandError, ZulipBaseCommand
from zerver.models import all_realm_filters
from zerver.models import all_linkifiers_for_installation
class Command(ZulipBaseCommand):
@ -41,7 +41,7 @@ Example: ./manage.py realm_filters --realm=zulip --op=show
realm = self.get_realm(options)
assert realm is not None # Should be ensured by parser
if options["op"] == "show":
print(f"{realm.string_id}: {all_realm_filters().get(realm.id, [])}")
print(f"{realm.string_id}: {all_linkifiers_for_installation().get(realm.id, [])}")
sys.exit(0)
pattern = options["pattern"]
@ -54,10 +54,10 @@ Example: ./manage.py realm_filters --realm=zulip --op=show
if not url_format_string:
self.print_help("./manage.py", "realm_filters")
raise CommandError
do_add_realm_filter(realm, pattern, url_format_string)
do_add_linkifier(realm, pattern, url_format_string)
sys.exit(0)
elif options["op"] == "remove":
do_remove_realm_filter(realm, pattern=pattern)
do_remove_linkifier(realm, pattern=pattern)
sys.exit(0)
else:
self.print_help("./manage.py", "realm_filters")

View File

@ -887,61 +887,61 @@ class RealmFilter(models.Model):
return f"<RealmFilter({self.realm.string_id}): {self.pattern} {self.url_format_string}>"
def get_realm_filters_cache_key(realm_id: int) -> str:
return f"{cache.KEY_PREFIX}:all_realm_filters:{realm_id}"
def get_linkifiers_cache_key(realm_id: int) -> str:
return f"{cache.KEY_PREFIX}:all_linkifiers_for_realm:{realm_id}"
# We have a per-process cache to avoid doing 1000 remote cache queries during page load
per_request_realm_filters_cache: Dict[int, List[Tuple[str, str, int]]] = {}
per_request_linkifiers_cache: Dict[int, List[Tuple[str, str, int]]] = {}
def realm_in_local_realm_filters_cache(realm_id: int) -> bool:
return realm_id in per_request_realm_filters_cache
def realm_in_local_linkifiers_cache(realm_id: int) -> bool:
return realm_id in per_request_linkifiers_cache
def realm_filters_for_realm(realm_id: int) -> List[Tuple[str, str, int]]:
if not realm_in_local_realm_filters_cache(realm_id):
per_request_realm_filters_cache[realm_id] = realm_filters_for_realm_remote_cache(realm_id)
return per_request_realm_filters_cache[realm_id]
def linkifiers_for_realm(realm_id: int) -> List[Tuple[str, str, int]]:
if not realm_in_local_linkifiers_cache(realm_id):
per_request_linkifiers_cache[realm_id] = linkifiers_for_realm_remote_cache(realm_id)
return per_request_linkifiers_cache[realm_id]
@cache_with_key(get_realm_filters_cache_key, timeout=3600 * 24 * 7)
def realm_filters_for_realm_remote_cache(realm_id: int) -> List[Tuple[str, str, int]]:
@cache_with_key(get_linkifiers_cache_key, timeout=3600 * 24 * 7)
def linkifiers_for_realm_remote_cache(realm_id: int) -> List[Tuple[str, str, int]]:
filters = []
for realm_filter in RealmFilter.objects.filter(realm_id=realm_id):
filters.append((realm_filter.pattern, realm_filter.url_format_string, realm_filter.id))
for linkifier in RealmFilter.objects.filter(realm_id=realm_id):
filters.append((linkifier.pattern, linkifier.url_format_string, linkifier.id))
return filters
def all_realm_filters() -> Dict[int, List[Tuple[str, str, int]]]:
def all_linkifiers_for_installation() -> Dict[int, List[Tuple[str, str, int]]]:
filters: DefaultDict[int, List[Tuple[str, str, int]]] = defaultdict(list)
for realm_filter in RealmFilter.objects.all():
filters[realm_filter.realm_id].append(
(realm_filter.pattern, realm_filter.url_format_string, realm_filter.id)
for linkifier in RealmFilter.objects.all():
filters[linkifier.realm_id].append(
(linkifier.pattern, linkifier.url_format_string, linkifier.id)
)
return filters
def flush_realm_filter(sender: Any, **kwargs: Any) -> None:
def flush_linkifiers(sender: Any, **kwargs: Any) -> None:
realm_id = kwargs["instance"].realm_id
cache_delete(get_realm_filters_cache_key(realm_id))
cache_delete(get_linkifiers_cache_key(realm_id))
try:
per_request_realm_filters_cache.pop(realm_id)
per_request_linkifiers_cache.pop(realm_id)
except KeyError:
pass
post_save.connect(flush_realm_filter, sender=RealmFilter)
post_delete.connect(flush_realm_filter, sender=RealmFilter)
post_save.connect(flush_linkifiers, sender=RealmFilter)
post_delete.connect(flush_linkifiers, sender=RealmFilter)
def flush_per_request_caches() -> None:
global per_request_display_recipient_cache
per_request_display_recipient_cache = {}
global per_request_realm_filters_cache
per_request_realm_filters_cache = {}
global per_request_linkifiers_cache
per_request_linkifiers_cache = {}
# The Recipient table is used to map Messages to the set of users who

View File

@ -12,8 +12,8 @@ from typing import Any, Callable, Dict, List, Optional, Set, Tuple
from django.utils.timezone import now as timezone_now
from zerver.lib.actions import (
do_add_linkifier,
do_add_reaction,
do_add_realm_filter,
do_create_user,
update_user_presence,
)
@ -253,7 +253,7 @@ def get_temp_user_group_id() -> Dict[str, object]:
@openapi_param_value_generator(["/realm/filters/{filter_id}:delete"])
def remove_realm_filters() -> Dict[str, object]:
filter_id = do_add_realm_filter(
filter_id = do_add_linkifier(
get_realm("zulip"), "#(?P<id>[0-9]{2,8})", "https://github.com/zulip/zulip/pull/%(id)s"
)
return {

View File

@ -24,9 +24,9 @@ from zerver.lib.actions import (
check_send_typing_notification,
do_add_alert_words,
do_add_default_stream,
do_add_linkifier,
do_add_reaction,
do_add_realm_domain,
do_add_realm_filter,
do_add_streams_to_default_stream_group,
do_add_submessage,
do_change_avatar_fields,
@ -64,11 +64,11 @@ from zerver.lib.actions import (
do_remove_alert_words,
do_remove_default_stream,
do_remove_default_stream_group,
do_remove_linkifier,
do_remove_reaction,
do_remove_realm_custom_profile_field,
do_remove_realm_domain,
do_remove_realm_emoji,
do_remove_realm_filter,
do_remove_streams_from_default_stream_group,
do_rename_stream,
do_revoke_multi_use_invite,
@ -1318,13 +1318,11 @@ class NormalActionsTest(BaseAction):
regex = "#(?P<id>[123])"
url = "https://realm.com/my_realm_filter/%(id)s"
events = self.verify_action(
lambda: do_add_realm_filter(self.user_profile.realm, regex, url)
)
events = self.verify_action(lambda: do_add_linkifier(self.user_profile.realm, regex, url))
check_realm_filters("events[0]", events[0])
events = self.verify_action(
lambda: do_remove_realm_filter(self.user_profile.realm, "#(?P<id>[123])")
lambda: do_remove_linkifier(self.user_profile.realm, "#(?P<id>[123])")
)
check_realm_filters("events[0]", events[0])

View File

@ -55,13 +55,13 @@ from zerver.models import (
UserGroup,
UserMessage,
UserProfile,
flush_linkifiers,
flush_per_request_caches,
flush_realm_filter,
get_client,
get_realm,
get_stream,
realm_filters_for_realm,
realm_in_local_realm_filters_cache,
linkifiers_for_realm,
realm_in_local_linkifiers_cache,
)
@ -1429,13 +1429,13 @@ class MarkdownTest(ZulipTestCase):
import zerver.lib.markdown
zerver.lib.markdown.realm_filter_data = {}
zerver.lib.markdown.linkifier_data = {}
maybe_update_markdown_engines(None, False)
all_filters = zerver.lib.markdown.realm_filter_data
zulip_filters = all_filters[realm.id]
self.assertEqual(len(zulip_filters), 1)
all_linkifiers = zerver.lib.markdown.linkifier_data
zulip_linkifiers = all_linkifiers[realm.id]
self.assertEqual(len(zulip_linkifiers), 1)
self.assertEqual(
zulip_filters[0],
zulip_linkifiers[0],
("#(?P<id>[0-9]{2,8})", "https://trac.example.com/ticket/%(id)s", linkifier.id),
)
@ -1444,7 +1444,7 @@ class MarkdownTest(ZulipTestCase):
def flush() -> None:
"""
flush_realm_filter is a post-save hook, so calling it
flush_linkifiers is a post-save hook, so calling it
directly for testing is kind of awkward
"""
@ -1453,30 +1453,28 @@ class MarkdownTest(ZulipTestCase):
instance = Instance()
instance.realm_id = realm.id
flush_realm_filter(sender=None, instance=instance)
flush_linkifiers(sender=None, instance=instance)
def save_new_realm_filter() -> None:
realm_filter = RealmFilter(
realm=realm, pattern=r"whatever", url_format_string="whatever"
)
realm_filter.save()
def save_new_linkifier() -> None:
linkifier = RealmFilter(realm=realm, pattern=r"whatever", url_format_string="whatever")
linkifier.save()
# start fresh for our realm
flush()
self.assertFalse(realm_in_local_realm_filters_cache(realm.id))
self.assertFalse(realm_in_local_linkifiers_cache(realm.id))
# call this just for side effects of populating the cache
realm_filters_for_realm(realm.id)
self.assertTrue(realm_in_local_realm_filters_cache(realm.id))
linkifiers_for_realm(realm.id)
self.assertTrue(realm_in_local_linkifiers_cache(realm.id))
# Saving a new RealmFilter should have the side effect of
# flushing the cache.
save_new_realm_filter()
self.assertFalse(realm_in_local_realm_filters_cache(realm.id))
save_new_linkifier()
self.assertFalse(realm_in_local_linkifiers_cache(realm.id))
# and flush it one more time, to make sure we don't get a KeyError
flush()
self.assertFalse(realm_in_local_realm_filters_cache(realm.id))
self.assertFalse(realm_in_local_linkifiers_cache(realm.id))
def test_realm_patterns_negative(self) -> None:
realm = get_realm("zulip")

View File

@ -1,6 +1,6 @@
import re
from zerver.lib.actions import do_add_realm_filter
from zerver.lib.actions import do_add_linkifier
from zerver.lib.test_classes import ZulipTestCase
from zerver.models import RealmFilter, get_realm
@ -9,7 +9,7 @@ class RealmFilterTest(ZulipTestCase):
def test_list(self) -> None:
self.login("iago")
realm = get_realm("zulip")
do_add_realm_filter(realm, "#(?P<id>[123])", "https://realm.com/my_realm_filter/%(id)s")
do_add_linkifier(realm, "#(?P<id>[123])", "https://realm.com/my_realm_filter/%(id)s")
result = self.client_get("/json/realm/filters")
self.assert_json_success(result)
self.assertEqual(200, result.status_code)
@ -104,13 +104,13 @@ class RealmFilterTest(ZulipTestCase):
def test_delete(self) -> None:
self.login("iago")
realm = get_realm("zulip")
filter_id = do_add_realm_filter(
linkifier_id = do_add_linkifier(
realm, "#(?P<id>[123])", "https://realm.com/my_realm_filter/%(id)s"
)
filters_count = RealmFilter.objects.count()
result = self.client_delete(f"/json/realm/filters/{filter_id + 1}")
linkifiers_count = RealmFilter.objects.count()
result = self.client_delete(f"/json/realm/filters/{linkifier_id + 1}")
self.assert_json_error(result, "Filter not found")
result = self.client_delete(f"/json/realm/filters/{filter_id}")
result = self.client_delete(f"/json/realm/filters/{linkifier_id}")
self.assert_json_success(result)
self.assertEqual(RealmFilter.objects.count(), filters_count - 1)
self.assertEqual(RealmFilter.objects.count(), linkifiers_count - 1)

View File

@ -3,41 +3,43 @@ from django.http import HttpRequest, HttpResponse
from django.utils.translation import ugettext as _
from zerver.decorator import require_realm_admin
from zerver.lib.actions import do_add_realm_filter, do_remove_realm_filter
from zerver.lib.actions import do_add_linkifier, do_remove_linkifier
from zerver.lib.request import REQ, has_request_variables
from zerver.lib.response import json_error, json_success
from zerver.models import RealmFilter, UserProfile, realm_filters_for_realm
from zerver.models import RealmFilter, UserProfile, linkifiers_for_realm
# Custom realm filters
def list_filters(request: HttpRequest, user_profile: UserProfile) -> HttpResponse:
filters = realm_filters_for_realm(user_profile.realm_id)
# Custom realm linkifiers
def list_linkifiers(request: HttpRequest, user_profile: UserProfile) -> HttpResponse:
filters = linkifiers_for_realm(user_profile.realm_id)
return json_success({"filters": filters})
@require_realm_admin
@has_request_variables
def create_filter(
def create_linkifier(
request: HttpRequest,
user_profile: UserProfile,
pattern: str = REQ(),
url_format_string: str = REQ(),
) -> HttpResponse:
try:
filter_id = do_add_realm_filter(
linkifier_id = do_add_linkifier(
realm=user_profile.realm,
pattern=pattern,
url_format_string=url_format_string,
)
return json_success({"id": filter_id})
return json_success({"id": linkifier_id})
except ValidationError as e:
return json_error(e.messages[0], data={"errors": dict(e)})
@require_realm_admin
def delete_filter(request: HttpRequest, user_profile: UserProfile, filter_id: int) -> HttpResponse:
def delete_linkifier(
request: HttpRequest, user_profile: UserProfile, filter_id: int
) -> HttpResponse:
try:
do_remove_realm_filter(realm=user_profile.realm, id=filter_id)
do_remove_linkifier(realm=user_profile.realm, id=filter_id)
except RealmFilter.DoesNotExist:
return json_error(_("Filter not found"))
return json_success()

View File

@ -119,7 +119,7 @@ from zerver.views.realm_domains import (
from zerver.views.realm_emoji import delete_emoji, list_emoji, upload_emoji
from zerver.views.realm_export import delete_realm_export, export_realm, get_realm_exports
from zerver.views.realm_icon import delete_icon_backend, get_icon_backend, upload_icon
from zerver.views.realm_linkifiers import create_filter, delete_filter, list_filters
from zerver.views.realm_linkifiers import create_linkifier, delete_linkifier, list_linkifiers
from zerver.views.realm_logo import delete_logo_backend, get_logo_backend, upload_logo
from zerver.views.registration import (
accounts_home,
@ -266,8 +266,8 @@ v1_api_and_json_patterns = [
# realm/logo -> zerver.views.realm_logo
rest_path("realm/logo", POST=upload_logo, DELETE=delete_logo_backend, GET=get_logo_backend),
# realm/filters -> zerver.views.realm_linkifiers
rest_path("realm/filters", GET=list_filters, POST=create_filter),
rest_path("realm/filters/<int:filter_id>", DELETE=delete_filter),
rest_path("realm/filters", GET=list_linkifiers, POST=create_linkifier),
rest_path("realm/filters/<int:filter_id>", DELETE=delete_linkifier),
# realm/profile_fields -> zerver.views.custom_profile_fields
rest_path(
"realm/profile_fields",