diff --git a/Makefile b/Makefile index 00667ac..cdc9c09 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,10 @@ help: ## This help. run: ## Run the development environment from tox. tox -e devenv +.PHONY: shell +shell: ## Run the django shell using some additional tools. + venv/bin/python manage.py shell_plus + .PHONY: migrate migrate: makemigrations ## Run migrate on the DB, updating schema per migration files. venv/bin/python manage.py migrate @@ -37,9 +41,10 @@ update-flatpages: venv/bin/django-admin ## Update the flatpages from the markdow collectstatic: ## Collect static files into the STATIC_ROOT directory. venv/bin/python manage.py collectstatic -.PHONY: check-deploy -check-deploy: ## Run a check against the project for deployability. +.PHONY: check +check: ## Run various checks against the project venv/bin/python manage.py check --deploy + venv/bin/python manage.py validate_templates .PHONY: reestdb resetdb: ## Remove the development database and regenerate it, loading fixtures. @@ -91,14 +96,16 @@ test-travis: ## Test target for travis-ci use. manage.py test --verbosity=2 $(TEST_SUITE) coverage report -m --skip-covered -.PHONY: sloccount -sloccount: ## Get sloc count from all Python, html, markdown, Makefile, and shell files. +.PHONY: metadata +metadata: ## Get metadata about the project (sloc, models, urls) git ls-files \ | grep -v static \ | grep -v manage.py \ | grep -v migrations \ | grep -E '(.py|.html|.md|Makefile|sh)' \ | xargs python sloc.py > sloc.tsv + venv/bin/python manage.py graph_models -a -o models.png + venv/bin/python manage.py show_urls > urls.tsv .PHONY: clean clean: ## Remove virtualenv and tox environments, along with compiled/optimized python files. diff --git a/administration/flag_views.py b/administration/flag_views.py index a8cc1ba..77c37f3 100644 --- a/administration/flag_views.py +++ b/administration/flag_views.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.contrib.admin.views.decorators import staff_member_required from django.contrib.auth.decorators import ( login_required, @@ -67,14 +68,34 @@ def list_content_flags(request): @login_required def create_flag(request): + # Ensure that we have both content type and object id if 'content_type' not in request.GET or 'object_id' not in request.GET: return render(request, 'permission_denied.html', { 'title': 'Cannot create flag without a subject', 'additional_error': 'Flags must be related to an object', }, status=403) - ctype = get_object_or_404(ContentType, pk=request.GET.get('content_type')) + + # Ensure that we can flag the given content type + if request.GET.get('content_type') not in \ + settings.FLAGGABLE_CONTENT_TYPES: + return render(request, 'permission_denied.html', { + 'title': 'That content type is not flaggable', + 'additional_error': + 'The flaggable content types are '.format( + ''.join([ + '
  • '+c+'
  • ' for c in + settings.FLAGGABLE_CONTENT_TYPES + ]) + ), + }, status=403) + + # Retrieve the content type and object + parts = request.GET.get('content_type').split(':') + ctype = get_object_or_404(ContentType, app_label=parts[0], model=parts[1]) obj = ctype.get_object_for_this_type(pk=request.GET.get('object_id')) - if obj.owner == request.user: + + # Ensure that we can flag the given object + if hasattr(obj, 'owner') and obj.owner == request.user: return render(request, 'permission_denied.html', { 'title': 'Permission denied', 'additional_error': 'You cannot flag your own objects', @@ -83,19 +104,24 @@ def create_flag(request): content_type=ctype, object_id=obj.id, )) + + # Try to save any POSTed data if request.method == 'POST': form = FlagForm(request.POST) if form.is_valid(): flag = form.save(commit=False) flag.flagged_by = request.user - flag.flagged_object_owner = flag.object_model.owner + flag.flagged_object_owner = (request.user if not + hasattr(flag.object_model, 'owner') + else flag.object_model.owner) flag.save() form.save_m2m() flag.participants.add(request.user) - flag.participants.add(flag.object_model.owner) + flag.participants.add(flag.flagged_object_owner) return redirect(flag.get_absolute_url()) return render(request, 'create_flag.html', { 'title': 'Flag {}'.format(ctype.model), + 'subtitle': str(obj), 'form': form, }) diff --git a/administration/templatetags/__init__.py b/administration/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/administration/templatetags/flag_extras.py b/administration/templatetags/flag_extras.py new file mode 100644 index 0000000..91ff84b --- /dev/null +++ b/administration/templatetags/flag_extras.py @@ -0,0 +1,19 @@ +from django import template + +from administration.models import Flag + +register = template.Library() + + +@register.filter +def can_view_flagged_item(user, flag): + """A filter for deciding if a user can view a flagged item.""" + if user in flag.participants.all(): + return True + if user.has_perm('administration.can_view_social_flags') and \ + flag.flag_type == Flag.SOCIAL: + return True + if user.has_perm('administration.can_view_content_flags') and \ + flag.flag_type == Flag.CONTENT: + return True + return False diff --git a/core/templates/coverage-badge.svg b/core/templates/coverage-badge.svg index fb84b7b..90da4ca 100644 --- a/core/templates/coverage-badge.svg +++ b/core/templates/coverage-badge.svg @@ -14,7 +14,7 @@ coverage coverage - 93% - 93% + 92% + 92% diff --git a/honeycomb/revno.py b/honeycomb/revno.py index d56fa96..c25ecf2 100644 --- a/honeycomb/revno.py +++ b/honeycomb/revno.py @@ -1,2 +1,2 @@ -GIT_REVNO = 'e92c76811e1796f0ff6818a44405ca4335b95129' +GIT_REVNO = 'cdf58cf04cd17c91fd5d981b43babd72e267764f' VERSION = 'pre-release' diff --git a/honeycomb/settings.py b/honeycomb/settings.py index c9f6ef4..4a07d54 100644 --- a/honeycomb/settings.py +++ b/honeycomb/settings.py @@ -162,6 +162,7 @@ 'taggit', 'haystack', 'datetimewidget', + 'django_extensions', ] MIDDLEWARE = [ 'honeycomb.middleware.BanMiddleware', @@ -242,3 +243,14 @@ # NB you should also set this up on the server through apache/nginx config; # this is only intended to be a backup MAX_UPLOAD_SIZE = 1024 * 1024 * 10 + +# The content types (in the form app_label:model) which users can flag for +# administrative review. +FLAGGABLE_CONTENT_TYPES = [ + 'promotion:adlifecycle', + 'publishers:publisherpage', + 'social:comment', + 'submissions:submission', + 'taggit:tag', + 'usermgmt:profile', +] diff --git a/models.png b/models.png new file mode 100644 index 0000000..7c33e9a Binary files /dev/null and b/models.png differ diff --git a/promotion/models.py b/promotion/models.py index f9b031b..8bf027d 100644 --- a/promotion/models.py +++ b/promotion/models.py @@ -1,8 +1,10 @@ from __future__ import unicode_literals from django.contrib.auth.models import User +from django.contrib.contenttypes.fields import GenericRelation from django.db import models +from administration.models import Flag from submissions.models import Submission @@ -86,3 +88,5 @@ class AdLifecycle(models.Model): # Information about the ad's activity impressions = models.PositiveIntegerField(default=0) interactions = models.PositiveIntegerField(default=0) + + flags = GenericRelation(Flag) diff --git a/publishers/models.py b/publishers/models.py index 2bad3a3..0416baa 100644 --- a/publishers/models.py +++ b/publishers/models.py @@ -1,8 +1,11 @@ from __future__ import unicode_literals from django.contrib.auth.models import User +from django.contrib.contenttypes.fields import GenericRelation from django.db import models +from administration.models import Flag + class PublisherPage(models.Model): """A page on the site representing a publisher, collecting submissions by @@ -23,3 +26,5 @@ class PublisherPage(models.Model): # Users who have been published by the publisher members = models.ManyToManyField(User) + + flags = GenericRelation(Flag) diff --git a/requirements.txt b/requirements.txt index 19865e8..7d339a0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,30 +1,46 @@ +appnope==0.1.0 +backports.shutil-get-terminal-size==1.0.0 beautifulsoup4==4.5.1 bs4==0.0.1 configparser==3.5.0 coverage==4.0.3 coverage-badge==0.1.2 +decorator==4.0.10 Django==1.10.3 django-datetime-widget==0.9.3 +django-extensions==1.7.4 django-haystack==2.5.1 django-taggit==0.21.3 docutils==0.12 enum34==1.1.6 flake8==3.0.4 funcsigs==1.0.2 +ipython==5.1.0 +ipython-genutils==0.1.0 Markdown==2.6.7 mccabe==0.5.2 mock==2.0.0 +pathlib2==2.1.0 pbr==1.10.0 +pexpect==4.2.1 +pickleshare==0.7.4 Pillow==3.4.1 +prompt-toolkit==1.0.9 prose-wc==0.3.1 +ptyprocess==0.5.1 pycodestyle==2.0.0 pyflakes==1.2.3 Pygments==2.1.3 +pygraphviz==1.3.1 pymdown-extensions==1.1 pypandoc==1.3.3 python-coveralls==2.9.0 pytz==2016.7 PyYAML==3.12 requests==2.11.1 +simplegeneric==0.8.1 six==1.10.0 tblib==1.3.0 +traitlets==4.3.1 +wcwidth==0.1.7 +Werkzeug==0.11.11 diff --git a/sloc.tsv b/sloc.tsv index a490ffd..58bb0a4 100644 --- a/sloc.tsv +++ b/sloc.tsv @@ -1,7 +1,7 @@ Count Location 44 CONTRIBUTING.md 13 DEPLOYING.md - 104 Makefile + 110 Makefile 3 README.md 9 RELEASE.md 1 activitystream/__init__.py @@ -17,7 +17,7 @@ Count Location 194 administration/application_views.py 4 administration/apps.py 123 administration/ban_views.py - 183 administration/flag_views.py + 200 administration/flag_views.py 46 administration/forms.py 189 administration/models.py 106 administration/templates/admin-tabs-snippet.html @@ -34,6 +34,8 @@ Count Location 76 administration/templates/view_application.html 45 administration/templates/view_ban.html 47 administration/templates/view_flag.html + 0 administration/templatetags/__init__.py + 15 administration/templatetags/flag_extras.py 533 administration/test_application.py 413 administration/test_ban.py 134 administration/test_flag.py @@ -87,20 +89,20 @@ Count Location 0 honeycomb/__init__.py 17 honeycomb/middleware.py 2 honeycomb/revno.py - 148 honeycomb/settings.py + 157 honeycomb/settings.py 20 honeycomb/urls.py 10 honeycomb/wsgi.py 104 honeycomb_markdown.py 0 promotion/__init__.py 0 promotion/admin.py 4 promotion/apps.py - 58 promotion/models.py + 61 promotion/models.py 0 promotion/tests.py 0 promotion/views.py 0 publishers/__init__.py 0 publishers/admin.py 4 publishers/apps.py - 13 publishers/models.py + 16 publishers/models.py 0 publishers/tests.py 0 publishers/views.py 7 run-coveralls.sh @@ -109,7 +111,7 @@ Count Location 0 social/admin.py 4 social/apps.py 17 social/forms.py - 64 social/models.py + 74 social/models.py 11 social/templates/confirm_unblock_user.html 123 social/templates/notification-snippet.html 237 social/templates/notifications_categories.html @@ -123,38 +125,39 @@ Count Location 4 submissions/apps.py 238 submissions/folder_views.py 33 submissions/forms.py - 212 submissions/models.py + 220 submissions/models.py + 49 submissions/templates/comment-body-snippet.html 24 submissions/templates/confirm_delete_folder.html 29 submissions/templates/confirm_delete_submission.html 15 submissions/templates/edit_folder.html 159 submissions/templates/edit_submission.html 11 submissions/templates/list_root_folders.html 102 submissions/templates/list_submissions.html - 63 submissions/templates/subcomments-snippet.html + 44 submissions/templates/subcomments-snippet.html 21 submissions/templates/submission-list-snippet.html 38 submissions/templates/update_submission_order_in_folder.html - 163 submissions/templates/view_submission.html + 249 submissions/templates/view_submission.html 478 submissions/test_folders.py 863 submissions/tests.py 48 submissions/urls.py 26 submissions/utils.py - 276 submissions/views.py + 298 submissions/views.py 0 tags/__init__.py 4 tags/apps.py 12 tags/templates/list_tags.html - 56 tags/templates/view_tag.html + 76 tags/templates/view_tag.html 0 tags/templatetags/__init__.py 48 tags/templatetags/tag_extras.py 116 tags/tests.py 27 tags/urls.py - 59 tags/views.py + 78 tags/views.py 0 usermgmt/__init__.py 0 usermgmt/admin.py 4 usermgmt/apps.py 37 usermgmt/forms.py 10 usermgmt/group_models.py 119 usermgmt/group_views.py - 159 usermgmt/models.py + 164 usermgmt/models.py 24 usermgmt/templates/confirm_delete_group.html 20 usermgmt/templates/list_groups.html 107 usermgmt/templates/profile-tabs-snippet.html @@ -182,4 +185,4 @@ Count Location 24 usermgmt/urls.py 9 usermgmt/utils.py 87 usermgmt/views.py -11534 TOTAL +11787 TOTAL diff --git a/social/models.py b/social/models.py index 9925306..838818b 100644 --- a/social/models.py +++ b/social/models.py @@ -2,11 +2,15 @@ import markdown from django.contrib.auth.models import User -from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.contenttypes.fields import ( + GenericForeignKey, + GenericRelation, +) from django.contrib.contenttypes.models import ContentType from django.db import models from django.utils.html import strip_tags +from administration.models import Flag from honeycomb_markdown import HoneycombMarkdown from submissions.models import Submission @@ -39,6 +43,8 @@ class Comment(models.Model): deleted = models.BooleanField(default=False) deleted_by_object_owner = models.BooleanField(default=False) + flags = GenericRelation(Flag) + def save(self, *args, **kwargs): self.body_rendered = markdown.markdown( strip_tags(self.body_raw), @@ -54,6 +60,12 @@ def save(self, *args, **kwargs): ]) super(Comment, self).save(*args, **kwargs) + def get_active_flag(self): + """Retrieve flag if there is an active flag against this submission""" + active_flags = self.flags.filter(resolved=None) + if len(active_flags) > 0: + return active_flags[0] + def get_absolute_url(self): return '{}#comment-{}'.format( self.object_model.get_absolute_url(), diff --git a/social/views.py b/social/views.py index ff90b2f..e1a8281 100644 --- a/social/views.py +++ b/social/views.py @@ -294,7 +294,7 @@ def rate_submission(request, username=None, submission_id=None, reader = request.user author = submission.owner - # MAke sure the rating is valid + # Make sure the rating is valid try: rating = int(request.POST.get('rating', 0)) except ValueError: diff --git a/submissions/models.py b/submissions/models.py index 383d85d..709f888 100644 --- a/submissions/models.py +++ b/submissions/models.py @@ -7,12 +7,14 @@ import tempfile from django.contrib.auth.models import User +from django.contrib.contenttypes.fields import GenericRelation from django.core.urlresolvers import reverse from django.db import models from django.template.defaultfilters import slugify from django.utils.html import strip_tags from taggit.managers import TaggableManager +from administration.models import Flag from honeycomb_markdown import HoneycombMarkdown from usermgmt.group_models import FriendGroup @@ -126,6 +128,7 @@ class Submission(models.Model): rating_count = models.PositiveIntegerField(default=0) counts = models.CharField(max_length=250) tags = TaggableManager() + flags = GenericRelation(Flag) def save(self, *args, **kwargs): """Overridden save method. @@ -220,6 +223,12 @@ def get_average_rating(self): else: return {'stars': '', 'average': 0, 'count': 0} + def get_active_flag(self): + """Retrieve flag if there is an active flag against this submission""" + active_flags = self.flags.filter(resolved=None) + if len(active_flags) > 0: + return active_flags[0] + def get_absolute_url(self): """Gets the absolute URL of the image, reversed from patterns.""" return reverse('submissions:view_submission', kwargs={ diff --git a/submissions/templates/comment-body-snippet.html b/submissions/templates/comment-body-snippet.html new file mode 100644 index 0000000..e122bc5 --- /dev/null +++ b/submissions/templates/comment-body-snippet.html @@ -0,0 +1,49 @@ +{% load form_extras %} +{% load gravatar %} +{% load humanize %} +
    + {{ comment.owner.email|gravatar }} {{ comment.owner.profile.get_display_name }} + • Posted {{ comment.ctime|naturaltime }} + • Direct link + {% if user.is_authenticated and not flag %} + + + + Flag for administrative review + + + {% endif %} + {% if user == comment.owner or user == comment.target_object_owner %} +
    + {% csrf_token %} + + +
    + {% endif %} +
    +
    + {{ comment.body_rendered|safe }} +
    +{% if can_reply and user.is_authenticated %} +
    + +
    +
    +
    + {% csrf_token %} + + {{ comment_form.content_type }} + {{ comment_form.object_id }} +
    + {{ comment_form.body_raw.label }} + {{ comment_form.body_raw|append_form_control }} +
    + +
    +
    +
    +
    +{% endif %} diff --git a/submissions/templates/subcomments-snippet.html b/submissions/templates/subcomments-snippet.html index 7621ec3..ad97460 100644 --- a/submissions/templates/subcomments-snippet.html +++ b/submissions/templates/subcomments-snippet.html @@ -1,63 +1,45 @@ -{% load form_extras %} -{% load gravatar %} -{% load humanize %} +{% load flag_extras %} {% for comment in comments %} -
    -
    -
    - {% if comment.deleted %} - This comment has been deleted by {% if comment.deleted_by_object_owner %}the page owner{% else %}the commenter{% endif %}. - {% else %} -
    - {{ comment.owner.email|gravatar }} {{ comment.owner.profile.get_display_name }} - • Posted {{ comment.ctime|naturaltime }} - • Direct link - {% if user == comment.owner or user == comment.target_object_owner %} -
    - {% csrf_token %} - - -
    + {% with flag=comment.get_active_flag %} +
    +
    +
    + {% if comment.deleted %} + This comment has been deleted by {% if comment.deleted_by_object_owner %}the page owner{% else %}the commenter{% endif %}. + {% elif flag %} + {# XXX funky indentation below, reader beware #} + {# @makyo 2016-11-11 - if anyone finds a better way, by all means... #} + {% if user|can_view_flagged_item:flag %} + {% include 'comment-body-snippet.html' %} +
    +

    + You can view this comment because you are a moderator or participant in this flag. Other users cannot view this comment. + + You can view the flag here. +

    + {% else %} +
    {% endif %} -
    -
    - {{ comment.body_rendered|safe }} -
    - {% if can_reply and user.is_authenticated %} -
    - -
    -
    -
    - {% csrf_token %} - - {{ comment_form.content_type }} - {{ comment_form.object_id }} -
    - {{ comment_form.body_raw.label }} - {{ comment_form.body_raw|append_form_control }} -
    - -
    -
    +

    + This comment has been flagged for administrative review. +

    -
    + {# XXX end funky indentation #} + {% else %} + {% include 'comment-body-snippet.html' %} {% endif %} - {% endif %} -
    -
    - {% if comment.children.count > 0 %} - -
    -
    - {% include 'subcomments-snippet.html' with comments=comment.children.all can_reply=can_reply %}
    - {% endif %} -
    + {% if comment.children.count > 0 %} + +
    +
    + {% include 'subcomments-snippet.html' with comments=comment.children.all can_reply=can_reply %} +
    +
    + {% endif %} +
    + {% endwith %} {% endfor %} diff --git a/submissions/templates/view_submission.html b/submissions/templates/view_submission.html index 91ff1d5..0159b94 100644 --- a/submissions/templates/view_submission.html +++ b/submissions/templates/view_submission.html @@ -4,6 +4,18 @@ {% load gravatar %} {% block content %} +{% if active_flag %} +
    +

    Submission flagged {{ active_flag.subject }}

    +

    This submission has been flagged by {{ active_flag.flagged_by.profile.get_display_name }} for a {{ active_flag.get_flag_type_display|lower }} violation.

    + {% if user in active_flag.participants.all %} +

    You are marked as a participant in this flag; only moderators participants in this flag may view this submission.

    + {% else %} +

    You have permissions to view this submission because of your moderator status; only moderators participants in this flag may view this submission.

    + {% endif %} +

    You may view the flag here

    +
    +{% endif %} {% if submission.cover %}
    @@ -52,19 +64,72 @@ {% endfor %} - {% if user == submission.owner %} + {% if user.is_authenticated %}
    {% endif %} @@ -90,40 +155,61 @@
    -
    - Actions -
    -
    - {% csrf_token %} - - - - - -
    - - {% if submission in user.profile.favorited_submissions.all %} -
    - {% csrf_token %} - -
    - {% else %} -
    - {% csrf_token %} - -
    - {% endif %} - - {% if submission.can_enjoy %} -
    - {% csrf_token %} - -
    +
    +
    +
    + {% csrf_token %} + + + + + +
    +
    +
    + {% if submission in user.profile.favorited_submissions.all %} +
    + {% csrf_token %} + +
    + {% else %} +
    + {% csrf_token %} + +
    + {% endif %} +
    +
    + {% if submission.can_enjoy %} +
    + {% csrf_token %} + +
    + {% endif %} +
    + +
    + {% if perms.promotion.can_highlight_submission %} +
    +
    + Highlight +
    +
    {% endif %}
    diff --git a/submissions/views.py b/submissions/views.py index bbfa943..dadb586 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -26,6 +26,7 @@ filters_for_authenticated_user, ) from activitystream.models import Activity +from administration.models import Flag from core.templatetags.gravatar import gravatar from social.forms import CommentForm from social.models import Comment @@ -146,11 +147,32 @@ def view_submission(request, username=None, submission_id=None, return render(request, 'permission_denied.html', { 'title': 'Permission denied', }, status=403) + active_flag = submission.get_active_flag() # Increment the submission views - submission.views += 1 - submission.save() - Activity.create('submission', 'view', submission) + if request.user != submission.owner: + submission.views += 1 + submission.save() + Activity.create('submission', 'view', submission) + if active_flag is not None: + can_view = False + if request.user in active_flag.participants.all(): + can_view = True + if not can_view and (( + active_flag.flag_type == Flag.SOCIAL and + request.user.has_perm( + 'administration.can_view_social_flags')) or + (active_flag.flag_type == Flag.CONTENT and + request.user.has_perm( + 'administration.can_view_content_flags'))): + can_view = True + if not can_view: + return render(request, 'permission_denied.html', { + 'title': 'Submission flagged', + 'additional_error': 'This submission is flagged for ' + 'administrative review.' + }, status=403) + display_name = '{} {}'.format( gravatar(author.email, size=40), author.profile.get_display_name()) @@ -159,6 +181,7 @@ def view_submission(request, username=None, submission_id=None, 'title': submission.title, 'subtitle': 'by {}'.format(display_name), 'submission': submission, + 'active_flag': active_flag, 'comment_form': CommentForm(instance=Comment( content_type=ctype, object_id=submission.id)), diff --git a/tags/templates/view_tag.html b/tags/templates/view_tag.html index 574443f..76d34b0 100644 --- a/tags/templates/view_tag.html +++ b/tags/templates/view_tag.html @@ -1,10 +1,30 @@ {% extends "base.html" %} {% block content %} +{% if active_flag %} +
    +

    Tag flagged {{ active_flag.subject }}

    +

    This tag has been flagged by {{ active_flag.flagged_by.profile.get_display_name }} for a {{ active_flag.get_flag_type_display|lower }} violation.

    + {% if user in active_flag.participants.all %} +

    You are marked as a participant in this flag; only moderators participants in this flag may view this submission.

    + {% else %} +

    You have permissions to view this submission because of your moderator status; only moderators participants in this flag may view this submission.

    + {% endif %} +

    You may view the flag here

    +
    +{% endif %}

    Showing results {{ submissions.start_index }} through {{ submissions.end_index }} of {{ submissions.paginator.count }} + {% if not active_flag %} + + + + Flag for administrative review + + + {% endif %}

    diff --git a/tags/views.py b/tags/views.py index 7b74d42..eaa5b5f 100644 --- a/tags/views.py +++ b/tags/views.py @@ -1,4 +1,5 @@ from django.contrib.auth.decorators import login_required +from django.contrib.contenttypes.models import ContentType from django.core.paginator import ( EmptyPage, Paginator, @@ -10,6 +11,7 @@ ) from taggit.models import Tag +from administration.models import Flag from submissions.models import Submission from submissions.utils import ( filters_for_anonymous_user, @@ -34,6 +36,25 @@ def view_tag(request, tag_slug=None, page=1): """ tag = get_object_or_404(Tag, slug=tag_slug) + # Check for admin flags, only show the tag if there are none or the user + # has permissions to the flag + ctype = ContentType.objects.get(app_label='taggit', model='tag') + flags = Flag.objects.filter(content_type=ctype, object_id=tag.id, + resolved=None) + if len(flags) > 0: + active_flag = flags[0] + else: + active_flag = None + if active_flag is not None and not ( + request.user in active_flag.participants.all() or + request.user.has_perm('administration.can_view_social_flags') or + request.user.has_perm('administration.can_view_content_flags')): + return render(request, 'permission_denied.html', { + 'title': 'Permission denied', + 'additional_error': 'This tag has been flagged for ' + 'administrative review', + }, status=403) + # Filter submissions visible to the reader filters = filters_for_authenticated_user(request.user) if \ request.user.is_authenticated else filters_for_anonymous_user() @@ -48,6 +69,7 @@ def view_tag(request, tag_slug=None, page=1): return render(request, 'view_tag.html', { 'title': 'Submissions tagged "{}"'.format(tag.name), 'tag': tag, + 'active_flag': active_flag, 'submissions': submissions, }) diff --git a/tox.ini b/tox.ini index 8d8d82f..8a3d017 100644 --- a/tox.ini +++ b/tox.ini @@ -10,16 +10,16 @@ whitelist_externals = make commands = flake8 - ./manage.py git_revno - make sloccount - rm -f core/templates/coverage-badge.svg coverage erase coverage run \ --source 'activitystream,administration,core,promotion,publishers,social,submissions,tags,usermgmt' \ --omit '*migrations*,*urls.py,*apps.py,*admin.py,*__init__.py,*test*.py' \ manage.py test --verbosity=2 coverage report -m --skip-covered + rm -f core/templates/coverage-badge.svg coverage-badge -o core/templates/coverage-badge.svg + ./manage.py git_revno + make metadata [testenv:rapidtest] basepython = python3.5 @@ -28,4 +28,4 @@ commands = python manage.py test --parallel [testenv:devenv] basepython = python3.5 -commands = python manage.py runserver 0.0.0.0:8000 +commands = python manage.py runserver_plus 0.0.0.0:8000 diff --git a/urls.tsv b/urls.tsv new file mode 100644 index 0000000..afe6705 --- /dev/null +++ b/urls.tsv @@ -0,0 +1,140 @@ +/ core.views.front core:front +// submissions.views.view_submission submissions:view_submission +/_admin/ django.contrib.admin.sites.index admin:index +/_admin// django.contrib.admin.sites.app_index admin:app_list +/_admin/auth/group/ django.contrib.admin.options.changelist_view admin:auth_group_changelist +/_admin/auth/group// django.views.generic.base.RedirectView +/_admin/auth/group//change/ django.contrib.admin.options.change_view admin:auth_group_change +/_admin/auth/group//delete/ django.contrib.admin.options.delete_view admin:auth_group_delete +/_admin/auth/group//history/ django.contrib.admin.options.history_view admin:auth_group_history +/_admin/auth/group/add/ django.contrib.admin.options.add_view admin:auth_group_add +/_admin/auth/user/ django.contrib.admin.options.changelist_view admin:auth_user_changelist +/_admin/auth/user// django.views.generic.base.RedirectView +/_admin/auth/user//change/ django.contrib.admin.options.change_view admin:auth_user_change +/_admin/auth/user//delete/ django.contrib.admin.options.delete_view admin:auth_user_delete +/_admin/auth/user//history/ django.contrib.admin.options.history_view admin:auth_user_history +/_admin/auth/user//password/ django.contrib.auth.admin.user_change_password admin:auth_user_password_change +/_admin/auth/user/add/ django.contrib.auth.admin.add_view admin:auth_user_add +/_admin/doc/ django.contrib.admindocs.views.BaseAdminDocsView django-admindocs-docroot +/_admin/doc/bookmarklets/ django.contrib.admindocs.views.BookmarkletsView django-admindocs-bookmarklets +/_admin/doc/filters/ django.contrib.admindocs.views.TemplateFilterIndexView django-admindocs-filters +/_admin/doc/models/ django.contrib.admindocs.views.ModelIndexView django-admindocs-models-index +/_admin/doc/models/./ django.contrib.admindocs.views.ModelDetailView django-admindocs-models-detail +/_admin/doc/tags/ django.contrib.admindocs.views.TemplateTagIndexView django-admindocs-tags +/_admin/doc/templates/