diff --git a/biostar/__init__.py b/biostar/__init__.py index de77620ca..a459ed057 100644 --- a/biostar/__init__.py +++ b/biostar/__init__.py @@ -3,4 +3,4 @@ __all__ = ['celery_app'] -VERSION = '2.3.3' +VERSION = '2.3.4' diff --git a/biostar/accounts/views.py b/biostar/accounts/views.py index 2e0714faf..2ca107eb3 100644 --- a/biostar/accounts/views.py +++ b/biostar/accounts/views.py @@ -36,6 +36,7 @@ RATELIMIT_KEY = settings.RATELIMIT_KEY + def edit_profile(request): if request.user.is_anonymous: messages.error(request, "Must be logged in to edit profile") @@ -57,7 +58,6 @@ def edit_profile(request): username = form.cleaned_data["username"] email = form.cleaned_data['email'] User.objects.filter(pk=user.pk).update(username=username, email=email) - # Update user information in Profile object. Profile.objects.filter(user=user).update(name=form.cleaned_data['name'], watched_tags=form.cleaned_data['watched_tags'], location=form.cleaned_data['location'], @@ -69,6 +69,8 @@ def edit_profile(request): message_prefs=form.cleaned_data["message_prefs"], html=markdown(form.cleaned_data["text"]), digest_prefs=form.cleaned_data['digest_prefs']) + # Recompute watched tags + Profile.objects.filter(user=user).first().add_watched() return redirect(reverse("user_profile", kwargs=dict(uid=user.profile.uid))) diff --git a/biostar/forum/api.py b/biostar/forum/api.py index 8eb07d75f..a7dc0b611 100644 --- a/biostar/forum/api.py +++ b/biostar/forum/api.py @@ -192,6 +192,17 @@ def traffic(request): return data +@json_response +def api_tag(request, tag): + """ + Return list of post uids that have a tag. + """ + posts = Post.objects.filter(tags__name=tag.lower()).values_list('uid', flat=True) + posts = list(posts) + + return posts + + @json_response def user_email(request, email): user = User.objects.filter(email__iexact=email.lower()) diff --git a/biostar/forum/auth.py b/biostar/forum/auth.py index 4d2a9464f..92ad4f045 100644 --- a/biostar/forum/auth.py +++ b/biostar/forum/auth.py @@ -287,9 +287,10 @@ def post_tree(user, root): # Get all posts that belong to post root. query = Post.objects.valid_posts(u=user, root=root).exclude(pk=root.id) + # Filter quarantined and deleted comments or answers. if user.is_anonymous or not user.profile.is_moderator: - query = query.exclude(Q(spam=Post.SUSPECT) | Q(status=Post.DELETED)) + query = query.exclude(Q(spam=Post.SUSPECT) | Q(status=Post.DELETED) | Q(spam=Post.SPAM)) query = query.select_related("lastedit_user__profile", "author__profile", "root__author__profile") diff --git a/biostar/forum/models.py b/biostar/forum/models.py index c3ff87448..085913261 100644 --- a/biostar/forum/models.py +++ b/biostar/forum/models.py @@ -35,8 +35,11 @@ def valid_posts(self, u=None, **kwargs): if u and u.is_authenticated and u.profile.is_moderator: return query + # Users get to see their own quarantined posts. + # Filter for open posts that are not spam. query = query.filter(status=Post.OPEN, root__status=Post.OPEN) + query = query.filter(models.Q(spam=Post.NOT_SPAM) | models.Q(spam=Post.DEFAULT) | models.Q(root__spam=Post.NOT_SPAM) | models.Q(root__spam=Post.DEFAULT)) diff --git a/biostar/forum/search.py b/biostar/forum/search.py index 0e87a4e8c..9b8dd532c 100644 --- a/biostar/forum/search.py +++ b/biostar/forum/search.py @@ -13,6 +13,7 @@ from whoosh.searching import Results import html2markdown +import bleach from whoosh.qparser import MultifieldParser, OrGroup from whoosh.analysis import STOP_WORDS from whoosh.index import create_in, open_dir, exists_in @@ -107,8 +108,9 @@ def index_exists(dirname=settings.INDEX_DIR, indexname=settings.INDEX_NAME): def add_index(post, writer): - # user html2markdown to clean text. - content = html2markdown.convert(post.content) + + # Ensure the content is stripped of any html. + content = bleach.clean(post.content, styles=[], attributes={}, tags=[], strip=True) writer.update_document(title=post.title, url=post.get_absolute_url(), type_display=post.get_type_display(), content_length=len(content), diff --git a/biostar/forum/static/forum.css b/biostar/forum/static/forum.css index 0cb9674d7..cbd509912 100644 --- a/biostar/forum/static/forum.css +++ b/biostar/forum/static/forum.css @@ -52,6 +52,10 @@ body > .widen.container { } +.ui.form .form-wrap .field>.selection.dropdown { + min-width: 0 !important; +} + .voting .ui.button { background: none; padding: 5px; diff --git a/biostar/forum/tasks.py b/biostar/forum/tasks.py index b90b045b2..a3892538c 100644 --- a/biostar/forum/tasks.py +++ b/biostar/forum/tasks.py @@ -42,19 +42,6 @@ def spam_scoring(post): message(exc) -def tpatt(tag): - """ - Return pattern matching a tag found in comma separated string. - (?i) : case-insensitive flag - ^{tag}\\s*, : matches beginning - ,\\s*{tag}\\s*, : matches middle - ,\\s*{tag}$ : matches end - ^{tag}[^\\w+] : matches single entry ( no commas ) - """ - patt = fr"(?i)(^{tag}\s*,|,\s*{tag}\s*,|,\s*{tag}$|^{tag}$)" - return patt - - @task def notify_watched_tags(post, extra_context): """ @@ -63,7 +50,7 @@ def notify_watched_tags(post, extra_context): from biostar.accounts.models import User from django.conf import settings - users = [User.objects.filter(profile__watched_tags__iregex=tpatt(tag.name)).distinct() + users = [User.objects.filter(profile__watched__name=tag.name).distinct() for tag in post.root.tags.all()] # Flatten nested users queryset and get email. diff --git a/biostar/forum/templates/accounts/edit_profile.html b/biostar/forum/templates/accounts/edit_profile.html index b53f8f669..ad7e59389 100644 --- a/biostar/forum/templates/accounts/edit_profile.html +++ b/biostar/forum/templates/accounts/edit_profile.html @@ -1,9 +1,26 @@ {% extends "forum_base.html" %} {% load forum_tags %} {% load accounts_tags %} +{% load static %} {% block headtitle %}Edit Profile{% endblock %} +{% block head %} + + + + + +{% endblock %} + +{% block js %} + + + + + +{% endblock %} + {% block content %}