Repository: zulip/zulip

Issue:
Add support for `hash` based URLS in Filter patterns
[Related source code](https://github.com/zulip/zulip/blob/0212113569748ecac083db85aa40b974d2d53d2a/zerver/models.py#L512)

Snippet:
```python
def filter_format_validator(value: str) -> None:
    regex = re.compile(r'^[\.\/:a-zA-Z0-9_?=-]+%\(([a-zA-Z0-9_-]+)\)s[a-zA-Z0-9_-]*$') # THE REGEX <<<

    if not regex.match(value):
        raise ValidationError('URL format string must be in the following format: '
                              '`https://example.com/%(\w+)s`')
```

Problem
---------

I'm trying to add a filter to a site that uses a hash based router, which basically makes the url look like:

`http://some-site.domain.com/#/c/$(id)s`

And looking at `THE REGEX`, it does not allow a hash (`#`) in the url.

Solution
---------

I'm currently working directly on the container since I can't build the image offline,
So my solution was to change the source code inside the container, so basically the new regex is something like:

```python
r'^[\.\/:a-zA-Z0-9#_?=-]+%\(([a-zA-Z0-9_-]+)\)s[a-zA-Z0-9_-]*$'
                #^^^
```

And the url is then accepted.

I'm willing to submit a PR if needed.


Discussion:
Adding a hash in fine; feel free to submit a PR with a test in `test_realm_filters.py`.  We generally want to make the rules for creating filters less unnecessarily limited, but we can fix this now and regardless it will be a helpful test case.
Hello @zulip/server-settings members, this issue was labeled with the "area: settings (admin/org)" label, so you may want to check it out!

<!-- areaLabelNotification -->
@timabbot, I had a hard time working out the dev env. I can run manual tests, due to running on docker, but since I'm in windows, I'm having a hard time running the unit tests. Any advice on how to set up a proper dev env? 
@gioragutt one option is to just get a Linux remote dev VM; if you ask on "#development help", @hackerkid can create one for you in Zulip's Digital Ocean account (or you can create your own).  
@timabbott, I can alternatively use the docker compose way. I'm using docker for windows, so if I can either use docker OR a vm, not both. 

 Can I run tests inside it? I mean, it sounds possible. I'll test it out and tell you.

Thanks for the replys, I appreciate it. I'm constantly looking for feedback from our people, I'll surely add issues for the existing points, and future ones as well. 
The Docker production environment doesn't support running unit tests (we don't ship the files required in production images).  

For this specific issue, @aero31aero can you do a quick PR?  You're working on that subsystem anyway and it seems like it'd be really quick.

Resolving patch:
diff --git a/zerver/models.py b/zerver/models.py
--- a/zerver/models.py
+++ b/zerver/models.py
@@ -548,7 +548,7 @@ def filter_pattern_validator(value: str) -> None:
         raise ValidationError(error_msg)
 
 def filter_format_validator(value: str) -> None:
-    regex = re.compile(r'^[\.\/:a-zA-Z0-9_?=-]+%\(([a-zA-Z0-9_-]+)\)s[a-zA-Z0-9_-]*$')
+    regex = re.compile(r'^[\.\/:a-zA-Z0-9#_?=-]+%\(([a-zA-Z0-9_-]+)\)s[a-zA-Z0-9_-]*$')
 
     if not regex.match(value):
         raise ValidationError('URL format string must be in the following format: '


Test patch:
diff --git a/zerver/tests/test_realm_filters.py b/zerver/tests/test_realm_filters.py
--- a/zerver/tests/test_realm_filters.py
+++ b/zerver/tests/test_realm_filters.py
@@ -44,7 +44,7 @@ def test_create(self) -> None:
         result = self.client_post("/json/realm/filters", info=data)
         self.assert_json_error(result, 'URL format string must be in the following format: `https://example.com/%(\\w+)s`')
 
-        data['url_format_string'] = 'https://realm.com/my_realm_filter/%(id)s'
+        data['url_format_string'] = 'https://realm.com/my_realm_filter/#hashtag/%(id)s'
         result = self.client_post("/json/realm/filters", info=data)
         self.assert_json_success(result)