...
 
Commits (7)
......@@ -16,6 +16,11 @@ class CommentUserMentionGenerator(PgsqlQueueConsumer):
.filter_by(comment_id=msg.body['comment_id'])
.one()
)
# don't generate mentions for deleted/removed comments
if comment.is_deleted or comment.is_removed:
return
new_mentions = CommentNotification.get_mentions_for_comment(
self.db_session, comment)
......
......@@ -195,7 +195,7 @@ summary {
table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
width: auto;
}
td, th {
......
......@@ -76,6 +76,13 @@ def test_url_slug_with_punctuation():
assert convert_to_url_slug(original) == expected
def test_url_slug_with_apostrophes():
"""Ensure url slugs don't replace apostrophes with underscores."""
original = "Here's what we don’t want as underscores"
expected = "heres_what_we_dont_want_as_underscores"
assert convert_to_url_slug(original) == expected
def test_url_slug_truncation():
"""Ensure a simple url slug truncates as expected."""
original = "Here's another string to truncate."
......@@ -119,6 +126,12 @@ def test_word_count_with_apostrophes():
assert word_count(string) == 9
def test_word_count_with_curly_apostrophes():
"""Ensure curly apostrophes don't mess up the word count."""
string = "It’s not always false that apostrophes aren’t counted properly."
assert word_count(string) == 9
def test_word_count_with_lots_of_punctuation():
"""Ensure word count works properly with lots of punctuation."""
string = (
......
......@@ -18,7 +18,11 @@ class SimpleHoursPeriod:
raise ValueError('Period must be at least 1 hour.')
self.hours = hours
self.timedelta = timedelta(hours=hours)
try:
self.timedelta = timedelta(hours=hours)
except OverflowError:
raise ValueError('Time period is too large')
@classmethod
def from_short_form(cls, short_form: str) -> 'SimpleHoursPeriod':
......
......@@ -7,7 +7,7 @@ from urllib.parse import quote
# regex for matching an entire word, handles words that include an apostrophe
WORD_REGEX = re.compile(r"\w[\w']*")
WORD_REGEX = re.compile(r"\w[\w']*")
def word_count(string: str) -> int:
......@@ -20,7 +20,7 @@ def convert_to_url_slug(original: str, max_length: int = 100) -> str:
slug = original.lower()
# remove apostrophes so contractions don't get broken up by underscores
slug = slug.replace("'", '')
slug = re.sub("['’]", '', slug)
# replace all remaining non-word characters with underscores
slug = re.sub(r'\W+', '_', slug)
......
......@@ -170,7 +170,8 @@ class Comment(DatabaseModel):
acl.append((Allow, 'admin', 'view'))
acl.append((Allow, self.user_id, 'view'))
acl.append((Allow, self.user_id, 'reply'))
if not self.topic.is_locked:
acl.append((Allow, self.user_id, 'reply'))
acl.append((Allow, self.user_id, 'edit'))
acl.append((Allow, self.user_id, 'delete'))
......
......@@ -121,7 +121,15 @@ class TopicQuery(PaginatedQuery):
def inside_time_period(self, period: SimpleHoursPeriod) -> 'TopicQuery':
"""Restrict the topics to inside a time period (generative)."""
return self.filter(Topic.created_time > utc_now() - period.timedelta)
# if the time period is too long, this will crash by creating a
# datetime outside the valid range - catch that and just don't filter
# by time period at all if the range is that large
try:
start_time = utc_now() - period.timedelta
except OverflowError:
return self
return self.filter(Topic.created_time > start_time)
def has_tag(self, tag: Ltree) -> 'TopicQuery':
"""Restrict the topics to ones with a specific tag (generative)."""
......
<ul class="topic-tags">
{% for tag in topic.tags %}
<li class="label label-topic-tag label-topic-tag-{{ tag }}">
<li class="label label-topic-tag label-topic-tag-{{ tag.replace(' ', '_') }}">
<a href="/~{{ topic.group.path }}?tag={{ tag.replace(' ', '_') }}">{{ tag }}</a>
</li>
{% else %}
......
......@@ -8,6 +8,7 @@
comment_id36=comment.comment_id36,
) }}"
data-ic-target="#comment-{{ comment.comment_id36 }} .comment-itself:first"
data-ic-replace-target="true"
data-js-confirm-leave-page-unsaved
>
{{ markdown_textarea(
......@@ -25,6 +26,7 @@
comment_id36=comment.comment_id36,
) }}"
data-ic-target="#comment-{{ comment.comment_id36 }} .comment-itself:first"
data-ic-replace-target="true"
>Cancel</button>
</div>
</form>
......@@ -115,6 +115,7 @@
comment_id36=comment.comment_id36,
) }}"
data-ic-target="#comment-{{ comment.comment_id36 }} .comment-itself:first"
data-ic-replace-target="true"
>Voted
{% else %}
<li><a class="post-button" name="vote"
......@@ -123,6 +124,7 @@
comment_id36=comment.comment_id36,
) }}"
data-ic-target="#comment-{{ comment.comment_id36 }} .comment-itself:first"
data-ic-replace-target="true"
>Vote
{% endif %}
{% if comment.num_votes > 0 %}
......@@ -153,6 +155,7 @@
comment_id36=comment.comment_id36,
) }}"
data-ic-target="#comment-{{ comment.comment_id36 }} .comment-itself:first"
data-ic-replace-target="true"
data-ic-confirm="Delete this comment? This cannot be undone."
>Delete</a></li>
{% endif %}
......
......@@ -34,7 +34,7 @@
{% if topic.tags %}
<ul class="topic-tags">
{% for tag in topic.tags %}
<li class="label label-topic-tag label-topic-tag-{{ tag }}">
<li class="label label-topic-tag label-topic-tag-{{ tag.replace(' ', '_') }}">
{% if request.matched_route.name in ('home', 'group') %}
<a href="{{ request.current_listing_normal_url({'tag': tag.replace(' ', '_')}) }}">{{ tag }}</a>
{% else %}
......