Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom realm filters #2261

Closed
wants to merge 2 commits into from
Closed

Conversation

TigorC
Copy link
Contributor

@TigorC TigorC commented Nov 9, 2016

This is cleanup and rebase of #544

@smarx
Copy link

smarx commented Nov 9, 2016

Automated message from Dropbox CLA bot

@TigorC, it looks like you've already signed the Dropbox CLA. Thanks!

@@ -129,6 +129,29 @@ function render(template_name, args) {
assert.equal(emoji_url.attr('src'), 'http://emojipedia-us.s3.amazonaws.com/cache/46/7f/467fe69069c408e07517621f263ea9b5.png');
}());

(function admin_filter_list() {
// global.use_template('admin_filter_list');
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

global.use_template no longer exists (the tracking is done automatically); you can delete this line entirely.

def create_filter(request, user_profile):
# type: (HttpRequest, UserProfile) -> HttpResponse
pattern = request.POST.get('pattern', None)
url_format_string = request.POST.get('url_format_string', None)
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should be using the has_request_variables / REQ decorators. See http://zulip.readthedocs.io/en/latest/writing-views.html#writing-api-rest-endpoints for details.

Copy link
Sponsor Member

@timabbott timabbott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, this is looking great @TigorC! I posted a bunch of small comments. There are 2 larger things we should change:

  • It needs more extensive backend tests; we should exercise the API fully (including triggering all error conditions) to get full backend test coverage on the new code, as well as having a nice set of tests for all the validation code.
  • I'm not totally sold on the ALLOWED_FILTER_PREFIXES model; I think we ultimately should allow the following model for patterns:

<PREFIX>(<CAPTURE>) where <PREFIX> is an arbitrary string not containing any special characters of meaning to regular expressions and (<CAPTURE>) is something of the form (?P<STR>[CHARSET]+), where STR is a

example:
ZUL-123 via pattern ZUL-(?P<id>[0-9]+)

I don't think we need to fully implement that for merging this, though; maybe for right now we should just eliminate the ALLOWED_FILTER_PREFIXES variable and just validate that the prefix checking that it has just a string of no-special-characters before the (? That seems really easy and would cover most of the issue.

Thanks @TigorC!


def do_remove_realm_filter(realm, pattern=None, id=None):
# type: (Realm, text_type, int) -> None
if pattern:
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use is not None stylistically. Also, in mypy one should use Optional[text_type], Optional[int] for the other arguments.

for realm_filter in RealmFilter.objects.all():
filters[realm_filter.realm.domain].append((realm_filter.pattern, realm_filter.url_format_string))
filters[realm_filter.realm.domain].append((realm_filter.pattern, realm_filter.url_format_string, realm_filter.pk))
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use realm_filter.id, not realm_filter.pk -- as a project, we prefer to be explicit about which field is the primary key being used.

@@ -444,7 +444,7 @@ def test_realm_patterns(self):
url_format_string=url_format_string)
realm_filter.save()
self.assertEqual(
str(realm_filter),
realm_filter.__unicode__(),
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this change needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

str(realm_filter) - this doesn't work with python 3
We need to use python_2_unicode_compatible decorator, but it requires
from __future__ import unicode_literals
and this will require many changes in models.py, for this reason I added this hack

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, makes sense. This is probably fine given that it's test code.


def filter_format_validator(value):
# type: (str) -> None
regex = re.compile(r'.*(%\(\w+\))')
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this regex seems like it could use some work to check the entire string to be of the format suggested in the error message below. I think basically we want to ensure that it contains no regex special characters other than a single instance of %([a-zA-Z0-9_0]+)s.

@@ -0,0 +1,35 @@
<div id="filter-settings" class="settings-section">
<div class="settings-section-title"><i class="icon-vector-filter settings-section-icon"></i>Custom realm filters</div>
<div class="admin-table-wrapper">
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should add a description explaining how "realm filters" work -- i.e. that it is a set of regular expressions that will be automatically linkified when used in Zulip message bodies or topics.


channel.put({
url: "/json/realm/filters",
data: $(this).serialize(),
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code seems to have a bug where it doesn't strip whitespace properly; I accidentally had some trailing whitespace in some entries I created via the web UI and got the following filters (they don't work because of the \t characters):

$ ./manage.py realm_filters --r zulip.com 
zulip.com: [('#(?P<id>[0-9]+)\t', 'https://trac.example.com/ticket/%(id)s', 2), ('T(?P<id>[0-9]+)\t', 'https://trac.example.com/ticket/%(id)s', 3)]

filters = realm_filters_for_domain(user_profile.realm.domain)
return json_success({'filters': filters})

def create_filter(request, user_profile):
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This (and the delete one) should use the @require_realm_admin decorator -- otherwise non-admins will be able to modify these.


def delete_filter(request, user_profile, filter_id):
# type: (HttpRequest, UserProfile, int) -> HttpResponse
do_remove_realm_filter(realm=user_profile.realm, id=filter_id)
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should check the filter exists and report an appropriate error if it doesn't.

url: "/json/realm/filters",
data: $(this).serialize(),
success: function (data) {
filter.id = data.id;
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this line needed?

raise ValidationError('Invalid filter pattern')

if not regex.match(pattern):
raise ValidationError('Pattern format string must be in the following format: (?P<\w+>.+)')
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably come before the re.compile test.

@TigorC
Copy link
Contributor Author

TigorC commented Nov 16, 2016

@timabbott

example:
ZUL-123 via pattern ZUL-(?P<id>[0-9]+)

- this is a special character in regular expression, maybe allow some characters like -#?

@timabbott
Copy link
Sponsor Member

Yeah, we can allow some that aren't special without other characters (e.g. - has this property; you need [] for it to do anything); probably just -_# is good for now.

Copy link
Sponsor Member

@timabbott timabbott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this @TigorC! I fixed several issues (noted in the comments for your reference for future contributions) and merged this. Thanks @TigorC!

</div>
</div>
</form>
</div>
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing the CSS class to make the add-new-filter-box green; we should match styling with the "add-new-realm-emoji" form. (We'll be changing what this standard style looks like eventually, but consistency is ideal for now).

</td>
<td>
<button class="btn delete btn-danger" data-filter-id="{{id}}">
Delete
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should have been tagged for translation

<tbody id="admin_filters_table">
<th>Pattern</th>
<th>URL format</th>
<th>Actions</th>
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should have been tagged for translation

<div class="alert" id="admin-filter-status"></div>
<div class="control-group">
<label for="filter_pattern" class="control-label">Filter pattern</label>
<input type="text" id="filter_pattern" name="pattern" placeholder="{{t '#(?P<id>[0-9]+)' }}" />
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be tagged for translation; instead one should have added this file to the exclude list for that linter rule. See tools/lint-all

# realm/filters -> zerver.views.realm_filters
url(r'^realm/filters$', rest_dispatch,
{'GET': 'zerver.views.realm_filters.list_filters',
'PUT': 'zerver.views.realm_filters.create_filter'}),
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be using POST, not PUT. See e.g. http://restcookbook.com/HTTP%20Methods/put-vs-post/.

Looks like we have some existing code doing this wrong as well to fix.

@timabbott timabbott mentioned this pull request Nov 18, 2016
@timabbott timabbott closed this Nov 18, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants