def warnings(request, profile, page=0): warnings_qs = profile.warnings.order_by('-id') warnings = paginate(warnings_qs, page, 5, 2) items_left = warnings.paginator.count - warnings.end_index() add_acl(request.user, warnings.object_list) warning_level = get_user_warning_level(profile) warning_level_obj = get_user_warning_obj(profile) active_warnings = warning_level - warnings.start_index() + 1 for warning in warnings.object_list: if warning.is_canceled: warning.is_active = False else: warning.is_active = active_warnings > 0 active_warnings -= 1 levels_total = len(get_warning_levels()) - 1 if levels_total and warning_level: warning_progress = 100 - warning_level * 100 / levels_total else: warning_progress = 100 if warning_level: warning_level_obj.level = warning_level return render(request, 'misago/profile/warnings.html', { 'profile': profile, 'warnings': warnings, 'warning_level': warning_level_obj, 'warning_progress': warning_progress, 'page_number': warnings.number, 'items_left': items_left })
def clean_threads_for_merge(request): try: threads_ids = map(int, request.data.get('threads', [])) except (ValueError, TypeError): raise MergeError(_("One or more thread ids received were invalid.")) if len(threads_ids) < 2: raise MergeError(_("You have to select at least two threads to merge.")) elif len(threads_ids) > MERGE_LIMIT: message = ungettext( "No more than %(limit)s thread can be merged at single time.", "No more than %(limit)s threads can be merged at single time.", MERGE_LIMIT) raise MergeError(message % {'limit': MERGE_LIMIT}) threads_queryset = Thread.objects.filter( id__in=threads_ids, category__tree_id=CATEGORIES_TREE_ID, ).select_related('category').order_by('-id') threads = [] for thread in threads_queryset: add_acl(request.user, thread) if can_see_thread(request.user, thread): threads.append(thread) if len(threads) != len(threads_ids): raise MergeError(_("One or more threads to merge could not be found.")) return threads
def decorator(request, *args, **kwargs): User = get_user_model() relations = ('rank', 'online_tracker', 'ban_cache') queryset = User.objects.select_related(*relations) profile = get_object_or_404(queryset, id=kwargs.pop('user_id')) validate_slug(profile, kwargs.pop('user_slug')) kwargs['profile'] = profile add_acl(request.user, profile) if profile.acl_['can_follow']: profile.is_followed = request.user.is_following(profile) else: profile.is_followed = False if profile.acl_['can_block'] and request.user.is_authenticated(): profile.is_blocked = request.user.is_blocking(profile) else: profile.is_blocked = False if request.user.is_authenticated and request.method == "GET": read_user_notification(request.user, "profile_%s" % profile.pk) return f(request, *args, **kwargs)
def check_forum_permissions(self, request, forum): if forum.special_role: raise Http404() add_acl(request.user, forum) allow_see_forum(request.user, forum) allow_browse_forum(request.user, forum)
def __init__(self, request, thread, page): try: thread_model = thread.unwrap() except AttributeError: thread_model = thread posts_queryset = self.get_queryset(request, thread_model) list_page = paginate( posts_queryset, page, settings.MISAGO_POSTS_PER_PAGE, settings.MISAGO_POSTS_TAIL) paginator = pagination_dict(list_page, include_page_range=False) posts = list(list_page.object_list) posters = [] for post in posts: post.category = thread.category post.thread = thread_model if post.poster: posters.append(post.poster) add_acl(request.user, posts) make_posts_read_aware(request.user, thread_model, posts) make_users_status_aware(request.user, posters) if thread.category.acl['can_see_posts_likes']: add_likes_to_posts(request.user, posts) self._user = request.user self.posts = posts self.paginator = paginator
def patch_acl(request, event, value): """useful little op that updates event acl to current state""" if value: add_acl(request.user, event) return {'acl': event.acl} else: return {'acl': None}
def __init__(self, request, thread, page): posts_queryset = self.get_queryset(request, thread.model) list_page = paginate(posts_queryset, page, settings.MISAGO_POSTS_PER_PAGE, settings.MISAGO_POSTS_TAIL) paginator = pagination_dict(list_page, include_page_range=False) posts = list(list_page.object_list) posters = [] for post in posts: post.category = thread.category post.thread = thread.model if post.poster: posters.append(post.poster) add_acl(request.user, posts) make_posts_read_aware(request.user, thread.model, posts) make_users_status_aware(request.user, posters) self._user = request.user self.posts = posts self.paginator = paginator
def clean(self): data = super(MovePostsForm, self).clean() new_thread_url = data.get('new_thread_url') try: if not new_thread_url: raise Http404() resolution = resolve(urlparse(new_thread_url).path) if not 'thread_id' in resolution.kwargs: raise Http404() queryset = Thread.objects.select_related('forum') self.new_thread = queryset.get(id=resolution.kwargs['thread_id']) add_acl(self.user, self.new_thread.forum) add_acl(self.user, self.new_thread) allow_see_forum(self.user, self.new_thread.forum) allow_browse_forum(self.user, self.new_thread.forum) allow_see_thread(self.user, self.new_thread) except (Http404, Thread.DoesNotExist): message = _("You have to enter valid link to thread.") raise forms.ValidationError(message) if self.thread == self.new_thread: message = _("New thread is same as current one.") raise forms.ValidationError(message) if self.new_thread.forum.special_role: message = _("You can't move posts to special threads.") raise forms.ValidationError(message) return data
def poll_vote_create(request, thread, poll): poll.make_choices_votes_aware(request.user) allow_vote_poll(request.user, poll) serializer = NewVoteSerializer( data={ 'choices': request.data, }, context={ 'allowed_choices': poll.allowed_choices, 'choices': poll.choices, }, ) if not serializer.is_valid(): return Response( { 'detail': serializer.errors['choices'][0], }, status=400, ) remove_user_votes(request.user, poll, serializer.data['choices']) set_new_votes(request, poll, serializer.data['choices']) add_acl(request.user, poll) serialized_poll = PollSerializer(poll).data poll.choices = list(map(presave_clean_choice, deepcopy(poll.choices))) poll.save() return Response(serialized_poll)
def create(self, request, thread_pk): thread = self.get_thread_for_update(request, thread_pk) allow_start_poll(request.user, thread) try: if thread.poll and thread.poll.pk: raise PermissionDenied(_("There's already a poll in this thread.")) except Poll.DoesNotExist: pass instance = Poll( thread=thread, category=thread.category, poster=request.user, poster_name=request.user.username, poster_slug=request.user.slug, poster_ip=request.user_ip, ) serializer = NewPollSerializer(instance, data=request.data) if serializer.is_valid(): serializer.save() add_acl(request.user, instance) for choice in instance.choices: choice['selected'] = False return Response(PollSerializer(instance).data) else: return Response(serializer.errors, status=400)
def patch_acl(request, thread, value): """useful little op that updates thread acl to current state""" if value: add_acl(request.user, thread) return {'acl': thread.acl} else: return {'acl': None}
def get_posts(self, user, forum, thread, kwargs): queryset = self.get_posts_queryset(user, forum, thread) queryset = self.exclude_invisible_posts(queryset, user, forum, thread) page = paginate(queryset, kwargs.get('page', 0), settings.MISAGO_POSTS_PER_PAGE, settings.MISAGO_THREAD_TAIL) posts = [] for post in page.object_list: post.forum = forum post.thread = thread add_acl(user, post) if post.poster: poster_state = get_user_state(post.poster, user.acl) post.poster.online_state = poster_state posts.append(post) if page.next_page_first_item: add_events_to_posts( user, thread, posts, page.next_page_first_item.posted_on) else: add_events_to_posts(user, thread, posts) return page, posts
def create_attachment(self, request): upload = request.FILES.get('upload') if not upload: raise ValidationError(_("No file has been uploaded.")) user_roles = set(r.pk for r in request.user.get_roles()) filetype = validate_filetype(upload, user_roles) validate_filesize(upload, filetype, request.user.acl['max_attachment_size']) attachment = Attachment( secret=Attachment.generate_new_secret(), filetype=filetype, size=upload.size, uploader=request.user, uploader_name=request.user.username, uploader_slug=request.user.slug, uploader_ip=request.user_ip, filename=upload.name, ) if is_upload_image(upload): try: attachment.set_image(upload) except IOError: raise ValidationError(_("Uploaded image was corrupted or invalid.")) else: attachment.set_file(upload) attachment.save() add_acl(request.user, attachment) return Response(AttachmentSerializer(attachment, context={'user': request.user}).data)
def __init__(self, request, **kwargs): self._categories = self.get_categories(request) add_acl(request.user, self._categories) self._model = self.get_category(request, self._categories, **kwargs) self._subcategories = list(filter(self._model.has_child, self._categories)) self._children = list(filter(lambda s: s.parent_id == self._model.pk, self._subcategories))
def get_categories_tree(user, parent=None): if not user.acl['visible_categories']: return [] if parent: queryset = parent.get_descendants().order_by('lft') else: queryset = Category.objects.all_categories() queryset_with_acl = queryset.filter(id__in=user.acl['visible_categories']) visible_categories = list(queryset_with_acl) categories_dict = {} categories_list = [] parent_level = parent.level + 1 if parent else 1 for category in visible_categories: category.subcategories = [] categories_dict[category.pk] = category categories_list.append(category) if category.parent_id and category.level > parent_level: categories_dict[category.parent_id].subcategories.append(category) add_acl(user, categories_list) categoriestracker.make_read_aware(user, categories_list) for category in reversed(visible_categories): if category.acl['can_browse']: category.parent = categories_dict.get(category.parent_id) if category.parent: category.parent.threads += category.threads category.parent.posts += category.posts if category.parent.last_post_on and category.last_post_on: parent_last_post = category.parent.last_post_on category_last_post = category.last_post_on update_last_thead = parent_last_post < category_last_post elif not category.parent.last_post_on and category.last_post_on: update_last_thead = True else: update_last_thead = False if update_last_thead: category.parent.last_post_on = category.last_post_on category.parent.last_thread_id = category.last_thread_id category.parent.last_thread_title = category.last_thread_title category.parent.last_thread_slug = category.last_thread_slug category.parent.last_poster_name = category.last_poster_name category.parent.last_poster_slug = category.last_poster_slug if not category.is_read: category.parent.is_read = False flat_list = [] for category in categories_list: if category.level == parent_level: flat_list.append(category) return flat_list
def post_editor(self, request, thread_pk, pk): thread = self.get_thread( request, get_int_or_404(thread_pk), read_aware=False, subscription_aware=False ) post = self.get_post(request, thread, get_int_or_404(pk)).model allow_edit_post(request.user, post) attachments = [] for attachment in post.attachment_set.order_by('-id'): add_acl(request.user, attachment) attachments.append(attachment) attachments_json = AttachmentSerializer( attachments, many=True, context={'user': request.user}).data return Response({ 'id': post.pk, 'api': post.get_api_url(), 'post': post.original, 'attachments': attachments_json, 'can_protect': bool(thread.category.acl['can_protect_posts']), 'is_protected': post.is_protected, 'poster': post.poster_name })
def __init__(self, request, category, list_type, page): self.allow_see_list(request, category, list_type) base_queryset = self.get_base_queryset(request, category.categories, list_type) threads_categories = [category.category] + category.subcategories threads_queryset = self.get_remaining_threads_queryset(base_queryset, category.category, threads_categories) list_page = paginate(threads_queryset, page, settings.MISAGO_THREADS_PER_PAGE, settings.MISAGO_THREADS_TAIL) paginator = pagination_dict(list_page, include_page_range=False) if list_page.number > 1: threads = list(list_page.object_list) else: pinned_threads = list(self.get_pinned_threads(base_queryset, category.category, threads_categories)) threads = list(pinned_threads) + list(list_page.object_list) if list_type in ('new', 'unread'): # we already know all threads on list are unread threadstracker.make_unread(threads) else: threadstracker.make_threads_read_aware(request.user, threads) add_categories_to_threads(category.category, category.categories, threads) add_acl(request.user, threads) make_subscription_aware(request.user, threads) # set state on object for easy access from hooks self.category = category self.threads = threads self.list_type = list_type self.paginator = paginator
def get_initial_attachments(self, mode, user, post): attachments = [] if mode == PostingEndpoint.EDIT: queryset = post.attachment_set.select_related('filetype') attachments = list(queryset) add_acl(user, attachments) return attachments
def posts_merge_endpoint(request, thread): if not thread.acl['can_merge_posts']: raise PermissionDenied(_("You can't merge posts in this thread.")) try: posts = clean_posts_for_merge(request, thread) except MergeError as e: return Response({'detail': e.msg}, status=400) first_post, merged_posts = posts[0], posts[1:] for post in merged_posts: post.merge(first_post) post.delete() first_post.save() thread.synchronize() thread.save() thread.category.synchronize() thread.category.save() first_post.thread = thread first_post.category = thread.category add_acl(request.user, first_post) return Response(PostSerializer(first_post, context={'user': request.user}).data)
def _test_thread_read(self): """thread read flag is set for user, then its set as unread by reply""" self.reply_thread(self.thread) add_acl(self.user, self.categories) threadstracker.make_read_aware(self.user, self.thread) self.assertFalse(self.thread.is_read) threadstracker.read_thread(self.user, self.thread, self.post) threadstracker.make_read_aware(self.user, self.thread) self.assertTrue(self.thread.is_read) categoriestracker.make_read_aware(self.user, self.categories) self.assertTrue(self.category.is_read) self.thread.last_post_on = timezone.now() self.thread.save() self.category.synchronize() self.category.save() self.reply_thread() threadstracker.make_read_aware(self.user, self.thread) self.assertFalse(self.thread.is_read) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read) posts = [post for post in self.thread.post_set.order_by('id')] threadstracker.make_posts_read_aware(self.user, self.thread, posts) for post in posts[:-1]: self.assertTrue(post.is_read) self.assertFalse(posts[-1].is_read)
def clean_threads_for_merge(request): threads_ids = clean_ids_list( request.data.get('threads', []), _("One or more thread ids received were invalid."), ) if len(threads_ids) < 2: raise MergeError(_("You have to select at least two threads to merge.")) elif len(threads_ids) > MERGE_LIMIT: message = ungettext( "No more than %(limit)s thread can be merged at single time.", "No more than %(limit)s threads can be merged at single time.", MERGE_LIMIT, ) raise MergeError(message % {'limit': MERGE_LIMIT}) threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT_NAME) threads_queryset = Thread.objects.filter( id__in=threads_ids, category__tree_id=threads_tree_id, ).select_related('category').order_by('-id') threads = [] for thread in threads_queryset: add_acl(request.user, thread) if can_see_thread(request.user, thread): threads.append(thread) if len(threads) != len(threads_ids): raise MergeError(_("One or more threads to merge could not be found.")) return threads
def override_acl(self, new_acl): new_acl.update({'can_browse': True}) forums_acl = self.user.acl forums_acl['visible_forums'].append(self.forum.pk) forums_acl['forums'][self.forum.pk] = new_acl override_acl(self.user, forums_acl) add_acl(self.user, self.forum)
def test_sync_record_for_empty_forum(self): """sync_record sets read flag on empty forum""" add_acl(self.user, self.forums) forumstracker.sync_record(self.user, self.forum) self.user.forumread_set.get(forum=self.forum) forumstracker.make_read_aware(self.user, self.forums) self.assertTrue(self.forum.is_read)
def check_thread_permissions(self, request, thread): add_acl(request.user, thread.forum) add_acl(request.user, thread) self.fetch_thread_participants(request.user, thread) allow_see_private_thread(request.user, thread) allow_use_private_threads(request.user)
def test_sync_record_for_empty_category(self): """sync_record sets read flag on empty category""" add_acl(self.user, self.categories) categoriestracker.sync_record(self.user, self.category) self.user.categoryread_set.get(category=self.category) categoriestracker.make_read_aware(self.user, self.categories) self.assertTrue(self.category.is_read)
def __init__(self, request, thread, pk, select_for_update=False): model = self.get_post(request, thread, pk, select_for_update) add_acl(request.user, model) self._model = model self._thread = model.thread self._category = model.category
def retrieve(self, request, pk=None): profile = self.get_user(pk) add_acl(request.user, profile) profile.status = get_user_status(request.user, profile) serializer = UserProfileSerializer(profile, context={'user': request.user}) return Response(serializer.data)
def merge_threads(request, validated_data, threads, poll): new_thread = Thread( category=validated_data['category'], started_on=threads[0].started_on, last_post_on=threads[0].last_post_on ) new_thread.set_title(validated_data['title']) new_thread.save() if poll: poll.move(new_thread) categories = [] for thread in threads: categories.append(thread.category) new_thread.merge(thread) thread.delete() record_event(request, new_thread, 'merged', { 'merged_thread': thread.title, }, commit=False) new_thread.synchronize() new_thread.save() if validated_data.get('weight') == THREAD_WEIGHT_GLOBAL: moderation.pin_thread_globally(request, new_thread) elif validated_data.get('weight'): moderation.pin_thread_locally(request, new_thread) if validated_data.get('is_hidden', False): moderation.hide_thread(request, new_thread) if validated_data.get('is_closed', False): moderation.close_thread(request, new_thread) if new_thread.category not in categories: categories.append(new_thread.category) for category in categories: category.synchronize() category.save() # set extra attrs on thread for UI new_thread.is_read = False new_thread.subscription = None # add top category to thread if validated_data.get('top_category'): categories = list(Category.objects.all_categories().filter( id__in=request.user.acl['visible_categories'] )) add_categories_to_items(validated_data['top_category'], categories, [new_thread]) else: new_thread.top_category = None add_acl(request.user, new_thread) return new_thread
def validate_is_hidden(self, is_hidden): try: add_acl(self.context, self.category) except AttributeError: return is_hidden # don't validate hidden further if category failed if is_hidden and not self.category.acl.get('can_hide_threads'): raise ValidationError(_("You don't have permission to hide threads in this category.")) return is_hidden
def retrieve(self, request, pk=None): qs = self.get_queryset() profile = get_object_or_404(self.get_queryset(), id=pk) add_acl(request.user, profile) serializer = UserProfileSerializer( profile, context={'user': request.user}) return Response(serializer.data)
def thread_start_editor(request): if request.user.is_anonymous: raise PermissionDenied(_("You need to be signed in to start threads.")) # list of categories that allow or contain subcategories that allow new threads available = [] categories = [] queryset = Category.objects.filter( pk__in=request.user.acl_cache['browseable_categories'], tree_id=trees_map.get_tree_id_for_root(THREADS_ROOT_NAME)).order_by( '-lft') for category in queryset: add_acl(request.user, category) post = False if can_start_thread(request.user, category): post = { 'close': bool(category.acl['can_close_threads']), 'hide': bool(category.acl['can_hide_threads']), 'pin': category.acl['can_pin_threads'], } available.append(category.pk) available.append(category.parent_id) elif category.pk in available: available.append(category.parent_id) categories.append({ 'id': category.pk, 'name': category.name, 'level': category.level - 1, 'post': post, }) # list only categories that allow new threads, or contains subcategory that allows one cleaned_categories = [] for category in reversed(categories): if category['id'] in available: cleaned_categories.append(category) if not cleaned_categories: raise PermissionDenied( _("No categories that allow new threads are available to you at the moment." )) return Response(cleaned_categories)
def update(self, request, thread_pk, pk): thread = self.get_thread_for_update(request, thread_pk) instance = self.get_poll(thread, pk) allow_edit_poll(request.user, instance) serializer = EditPollSerializer(instance, data=request.data) if serializer.is_valid(): serializer.save() add_acl(request.user, instance) instance.make_choices_votes_aware(request.user) return Response(PollSerializer(instance).data) else: return Response(serializer.errors, status=400)
def decorator(request, *args, **kwargs): queryset = kwargs['user'].warnings warning_id = kwargs.pop('warning_id') kwargs['warning'] = get_object_or_404(queryset, id=warning_id) add_acl(request.user, kwargs['warning']) required_permission(request.user, kwargs['warning']) response = f(request, *args, **kwargs) if response: return response else: return_path = moderation_return_path(request, kwargs['user']) return redirect(return_path)
def validate_weight(self, weight): try: add_acl(self.context, self.category) except AttributeError: return weight # don't validate weight further if category failed if weight > self.category.acl.get('can_pin_threads', 0): if weight == 2: raise ValidationError( _("You don't have permission to pin threads globally in this category.") ) else: raise ValidationError( _("You don't have permission to pin threads in this category.") ) return weight
def retrieve(self, request, pk=None): profile = self.get_user(request, pk) add_acl(request.user, profile) profile.status = get_user_status(request.user, profile) serializer = UserProfileSerializer(profile, context={'user': request.user}) profile_json = serializer.data if not profile.is_active: profile_json['is_active'] = False if profile.is_deleting_account: profile_json['is_deleting_account'] = True return Response(profile_json)
def __init__(self, request, pk, slug=None, read_aware=False, subscription_aware=False, select_for_update=False): model = self.get_thread(request, pk, slug, select_for_update) model.path = self.get_thread_path(model.category) add_acl(request.user, model.category) add_acl(request.user, model) if read_aware: make_read_aware(request.user, model) if subscription_aware: make_subscription_aware(request.user, model) self._model = model self._category = model.category self._path = model.path
def __init__(self, request, profile, page=0): root_category = ThreadsRootCategory(request) threads_categories = [root_category.unwrap()] + root_category.subcategories threads_queryset = self.get_threads_queryset( request, threads_categories, profile) posts_queryset = self.get_posts_queryset( request.user, profile, threads_queryset ).filter( is_event=False, is_hidden=False, is_unapproved=False ).order_by('-pk') list_page = paginate( posts_queryset, page, settings.MISAGO_POSTS_PER_PAGE, settings.MISAGO_POSTS_TAIL) paginator = pagination_dict(list_page, include_page_range=False) posts = list(list_page.object_list) posters = [] threads = [] for post in posts: threads.append(post.thread) if post.poster: posters.append(post.poster) add_categories_to_items( root_category.unwrap(), threads_categories, posts + threads) add_acl(request.user, threads) add_acl(request.user, posts) threadstracker.make_threads_read_aware(request.user, threads) for post in posts: threadstracker.make_posts_read_aware(request.user, post.thread, [post]) add_likes_to_posts(request.user, posts) make_users_status_aware(request.user, posters) self._user = request.user self.posts = posts self.paginator = paginator
def decorator(request, *args, **kwargs): User = get_user_model() relations = ('rank', 'online_tracker', 'ban_cache') queryset = User.objects.select_related(*relations) profile = get_object_or_404(queryset, pk=kwargs.pop('pk')) if not profile.is_active and not request.user.is_staff: raise Http404() validate_slug(profile, kwargs.pop('slug')) kwargs['profile'] = profile add_acl(request.user, profile) return f(request, *args, **kwargs)
def test_sync_record_for_category_deleted_threads(self): """unread category reverts to read after its emptied""" self.post_thread(self.user.joined_on + timedelta(days=1)) self.post_thread(self.user.joined_on + timedelta(days=1)) self.post_thread(self.user.joined_on + timedelta(days=1)) add_acl(self.user, self.categories) categoriestracker.sync_record(self.user, self.category) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read) self.category.thread_set.all().delete() self.category.synchronize() self.category.save() categoriestracker.make_read_aware(self.user, self.categories) self.assertTrue(self.category.is_read)
def update(self, request, thread_pk, pk=None): thread = self.get_thread(request, thread_pk) instance = self.get_poll(thread, pk) allow_edit_poll(request.user, instance) serializer = EditPollSerializer(instance, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() add_acl(request.user, instance) instance.make_choices_votes_aware(request.user) create_audit_trail(request, instance) return Response(PollSerializer(instance).data)
def test_sync_record_for_forum_with_deleted_threads(self): """unread forum reverts to read after its emptied""" self.post_thread(self.user.joined_on + timedelta(days=1)) self.post_thread(self.user.joined_on + timedelta(days=1)) self.post_thread(self.user.joined_on + timedelta(days=1)) add_acl(self.user, self.forums) forumstracker.sync_record(self.user, self.forum) forumstracker.make_read_aware(self.user, self.forums) self.assertFalse(self.forum.is_read) self.forum.thread_set.all().delete() self.forum.synchronize() self.forum.save() forumstracker.make_read_aware(self.user, self.forums) self.assertTrue(self.forum.is_read)
def __init__(self, request, category, list_type, page): self.allow_see_list(request, category, list_type) category_model = category.unwrap() base_queryset = self.get_base_queryset(request, category.categories, list_type) base_queryset = base_queryset.select_related('starter', 'last_poster') threads_categories = [category_model] + category.subcategories threads_queryset = self.get_remaining_threads_queryset( base_queryset, category_model, threads_categories) list_page = paginate(threads_queryset, page, settings.MISAGO_THREADS_PER_PAGE, settings.MISAGO_THREADS_TAIL) paginator = pagination_dict(list_page) if list_page.number > 1: threads = list(list_page.object_list) else: pinned_threads = list( self.get_pinned_threads(base_queryset, category_model, threads_categories)) threads = list(pinned_threads) + list(list_page.object_list) add_categories_to_items(category_model, category.categories, threads) add_acl(request.user, threads) make_subscription_aware(request.user, threads) if list_type in ('new', 'unread'): # we already know all threads on list are unread for thread in threads: thread.is_read = False thread.is_new = True else: threadstracker.make_read_aware(request.user, threads) self.filter_threads(request, threads) # set state on object for easy access from hooks self.category = category self.threads = threads self.list_type = list_type self.paginator = paginator
def merge_threads(user, validated_data, threads): new_thread = Thread( category=validated_data['category'], weight=validated_data.get('weight', 0), is_closed=validated_data.get('is_closed', False), started_on=threads[0].started_on, last_post_on=threads[0].last_post_on, ) new_thread.set_title(validated_data['title']) new_thread.save() categories = [] for thread in threads: categories.append(thread.category) new_thread.merge(thread) thread.delete() new_thread.synchronize() new_thread.save() if new_thread.category not in categories: categories.append(new_thread.category) for category in categories: category.synchronize() category.save() # set extra attrs on thread for UI new_thread.is_read = False new_thread.subscription = None # add top category to thread if validated_data.get('top_category'): categories = list(Category.objects.all_categories().filter( id__in=user.acl['visible_categories'])) add_categories_to_threads(validated_data['top_category'], categories, [new_thread]) else: new_thread.top_category = None new_thread.save() add_acl(user, new_thread) return new_thread
def test_merge_with_top_category(self): """api performs merge with top category""" posts_ids = [p.id for p in Post.objects.all()] self.override_acl({ 'can_merge_threads': True, 'can_close_threads': False, 'can_edit_threads': False, 'can_reply_threads': False, }) thread = testutils.post_thread(category=self.category) response = self.client.post(self.api_link, json.dumps({ 'top_category': self.root.id, 'threads': [self.thread.id, thread.id], 'title': 'Merged thread!', 'category': self.category.id, }), content_type="application/json") self.assertEqual(response.status_code, 200) # is response json with new thread? response_json = json.loads(response.content) new_thread = Thread.objects.get(pk=response_json['id']) new_thread.is_read = False new_thread.subscription = None new_thread.top_category = self.category add_acl(self.user, new_thread.category) add_acl(self.user, new_thread) self.assertEqual(response_json, ThreadListSerializer(new_thread).data) # did posts move to new thread? for post in Post.objects.filter(id__in=posts_ids): self.assertEqual(post.thread_id, new_thread.id) # are old threads gone? self.assertEqual([t.pk for t in Thread.objects.all()], [new_thread.pk])
def posts_merge_endpoint(request, thread): if not thread.acl['can_merge_posts']: raise PermissionDenied(_("You can't merge posts in this thread.")) serializer = MergePostsSerializer( data=request.data, context={ 'thread': thread, 'user': request.user, }, ) serializer.is_valid(raise_exception=True) posts = serializer.validated_data['posts'] first_post, merged_posts = posts[0], posts[1:] for post in merged_posts: post.merge(first_post) post.delete() if first_post.pk == thread.first_post_id: first_post.set_search_document(thread.title) else: first_post.set_search_document() first_post.save() first_post.update_search_vector() first_post.save(update_fields=['search_vector']) first_post.postread_set.all().delete() thread.synchronize() thread.save() thread.category.synchronize() thread.category.save() first_post.thread = thread first_post.category = thread.category add_acl(request.user, first_post) return Response(PostSerializer(first_post, context={'user': request.user}).data)
def exclude_all_invisible_threads(queryset, user): forums_in = [] conditions = None for forum in Forum.objects.all_forums(): add_acl(user, forum) condition_forum = Q(forum=forum) condition_author = Q(starter_id=user.id) # can see all threads? if forum.acl['can_see_all_threads']: can_mod = forum.acl['can_review_moderated_content'] can_hide = forum.acl['can_hide_threads'] if not can_mod or not can_hide: if not can_mod and not can_hide: condition = Q(is_moderated=False) & Q(is_hidden=False) elif not can_mod: condition = Q(is_moderated=False) elif not can_hide: condition = Q(is_hidden=False) visibility_condition = condition_author | condition visibility_condition = condition_forum & visibility_condition else: # user can see everything so don't bother with rest of routine forums_in.append(forum.pk) continue else: # show all threads in forum made by user visibility_condition = condition_forum & condition_author if conditions: conditions = conditions | visibility_condition else: conditions = visibility_condition if conditions and forums_in: return queryset.filter(Q(forum_id__in=forums_in) | conditions) elif conditions: return queryset.filter(conditions) elif forums_in: return queryset.filter(forum_id__in=forums_in) else: return Thread.objects.none()
def test_sync_record_for_forum_with_many_threads(self): """sync_record sets unread flag on forum with many threads""" self.post_thread(self.user.joined_on + timedelta(days=1)) self.post_thread(self.user.joined_on - timedelta(days=1)) self.post_thread(self.user.joined_on + timedelta(days=1)) self.post_thread(self.user.joined_on - timedelta(days=1)) add_acl(self.user, self.forums) forumstracker.sync_record(self.user, self.forum) self.user.forumread_set.get(forum=self.forum) forumstracker.make_read_aware(self.user, self.forums) self.assertFalse(self.forum.is_read) self.post_thread(self.user.joined_on + timedelta(days=1)) forumstracker.sync_record(self.user, self.forum) forumstracker.make_read_aware(self.user, self.forums) self.assertFalse(self.forum.is_read)
def test_sync_record_for_forum_with_new_thread(self): """ sync_record sets read flag on forum with old thread, then keeps flag to unread when new reply is posted """ self.post_thread(self.user.joined_on + timedelta(days=1)) add_acl(self.user, self.forums) forumstracker.sync_record(self.user, self.forum) self.user.forumread_set.get(forum=self.forum) forumstracker.make_read_aware(self.user, self.forums) self.assertFalse(self.forum.is_read) self.post_thread(self.user.joined_on + timedelta(days=1)) forumstracker.sync_record(self.user, self.forum) forumstracker.make_read_aware(self.user, self.forums) self.assertFalse(self.forum.is_read)
def test_sync_record_for_category_new_thread(self): """ sync_record sets read flag on category with old thread, then keeps flag to unread when new reply is posted """ self.post_thread(self.user.joined_on + timedelta(days=1)) add_acl(self.user, self.categories) categoriestracker.sync_record(self.user, self.category) self.user.categoryread_set.get(category=self.category) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read) self.post_thread(self.user.joined_on + timedelta(days=1)) categoriestracker.sync_record(self.user, self.category) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read)
def test_sync_record_for_forum_with_old_thread_and_reply(self): """ sync_record sets read flag on forum with old thread, then changes flag to unread when new reply is posted """ self.post_thread(self.user.reads_cutoff - timedelta(days=1)) add_acl(self.user, self.forums) forumstracker.sync_record(self.user, self.forum) self.user.forumread_set.get(forum=self.forum) forumstracker.make_read_aware(self.user, self.forums) self.assertTrue(self.forum.is_read) thread = self.post_thread(self.user.reads_cutoff + timedelta(days=1)) forumstracker.sync_record(self.user, self.forum) forumstracker.make_read_aware(self.user, self.forums) self.assertFalse(self.forum.is_read)
def test_sync_record_for_category_many_threads(self): """sync_record sets unread flag on category with many threads""" self.post_thread(self.user.joined_on + timedelta(days=1)) self.post_thread(self.user.joined_on - timedelta(days=1)) self.post_thread(self.user.joined_on + timedelta(days=1)) self.post_thread(self.user.joined_on - timedelta(days=1)) add_acl(self.user, self.categories) categoriestracker.sync_record(self.user, self.category) self.user.categoryread_set.get(category=self.category) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read) self.post_thread(self.user.joined_on + timedelta(days=1)) categoriestracker.sync_record(self.user, self.category) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read)
def revert_post_endpoint(request, post): edit = get_edit_by_pk(post, request.GET.get('edit')) datetime = timezone.now() post_edits = post.edits post.edits_record.create( category=post.category, thread=post.thread, edited_on=datetime, editor=request.user, editor_name=request.user.username, editor_slug=request.user.slug, editor_ip=request.user_ip, edited_from=post.original, edited_to=edit.edited_from, ) parsing_result = common_flavour(request, post.poster, edit.edited_from) post.original = parsing_result['original_text'] post.parsed = parsing_result['parsed_text'] update_post_checksum(post) post.updated_on = datetime post.edits = F('edits') + 1 post.last_editor = request.user post.last_editor_name = request.user.username post.last_editor_slug = request.user.slug post.save() post.is_read = True post.is_new = False post.edits = post_edits + 1 add_acl(request.user, post) if post.poster: make_users_status_aware(request.user, [post.poster]) return Response(PostSerializer(post, context={'user': request.user}).data)
def get_posts(self, user, forum, thread, kwargs): queryset = self.get_posts_queryset(user, forum, thread) page = paginate(queryset, kwargs.get('page', 0), 10, 3) posts = [] for post in page.object_list: add_acl(user, post) if post.poster: poster_state = get_user_state(post.poster, user.acl) post.poster.online_state = poster_state posts.append(post) if page.next_page_first_item: add_events_to_posts(user, thread, posts, page.next_page_first_item.posted_on) else: add_events_to_posts(user, thread, posts) return page, posts
def patch_move(request, thread, value): allow_move_thread(request.user, thread) category_pk = get_int_or_404(value) new_category = get_object_or_404( Category.objects.all_categories().select_related('parent'), pk=category_pk ) add_acl(request.user, new_category) allow_see_category(request.user, new_category) allow_browse_category(request.user, new_category) allow_start_thread(request.user, new_category) if new_category == thread.category: raise ValidationError(_("You can't move thread to the category it's already in.")) moderation.move_thread(request, thread, new_category) return {'category': CategorySerializer(new_category).data}
def patch_move(request, thread, value): if thread.acl.get('can_move'): category_pk = get_int_or_404(value) new_category = get_object_or_404( Category.objects.all_categories().select_related('parent'), pk=category_pk ) add_acl(request.user, new_category) allow_see_category(request.user, new_category) allow_browse_category(request.user, new_category) allow_start_thread(request.user, new_category) moderation.move_thread(request.user, thread, new_category) return {'category': CategorySerializer(new_category).data} else: raise PermissionDenied( _("You don't have permission to move this thread."))
def test_edit(self): """endpoint returns valid configuration for editor""" for i in range(3): self.override_acl({ 'max_attachment_size': 1000, }) with open(TEST_DOCUMENT_PATH, 'rb') as upload: response = self.client.post(reverse('misago:api:attachment-list'), data={ 'upload': upload }) self.assertEqual(response.status_code, 200) attachments = list(Attachment.objects.order_by('id')) attachments[0].uploader = None attachments[0].save() for attachment in attachments[:2]: attachment.post = self.post attachment.save() self.override_acl({ 'can_edit_posts': 1, }) response = self.client.get(self.api_link) for attachment in attachments: add_acl(self.user, attachment) self.assertEqual(response.status_code, 200) self.assertEqual(json.loads(smart_str(response.content)), { 'id': self.post.pk, 'api': self.post.get_api_url(), 'post': self.post.original, 'can_protect': False, 'is_protected': self.post.is_protected, 'poster': self.post.poster_name, 'attachments': [ AttachmentSerializer(attachments[1], context={'user': self.user}).data, AttachmentSerializer(attachments[0], context={'user': self.user}).data, ] })
def setUp(self): User = get_user_model() self.user = User.objects.create_user("Bob", "*****@*****.**", "Pass.123") datetime = timezone.now() self.category = Category.objects.all_categories()[:1][0] self.thread = Thread(category=self.category, started_on=datetime, starter_name='Tester', starter_slug='tester', last_post_on=datetime, last_poster_name='Tester', last_poster_slug='tester') self.thread.set_title("Test thread") self.thread.save() add_acl(self.user, self.category) add_acl(self.user, self.thread)
def poll_vote_create(request, thread, poll): poll.make_choices_votes_aware(request.user) allow_vote_poll(request.user, poll) try: clean_votes = validate_votes(poll, request.data) except ValidationError as e: return Response({'detail': six.text_type(e)}, status=400) remove_user_votes(request.user, poll, clean_votes) set_new_votes(request, poll, clean_votes) add_acl(request.user, poll) serialized_poll = PollSerializer(poll).data poll.choices = list(map(presave_clean_choice, deepcopy(poll.choices))) poll.save() return Response(serialized_poll)
def real_add_events_to_posts(user, thread, posts, delimeter=None): start_date = posts[0].posted_on events_queryset = thread.event_set.filter(occured_on__gte=start_date) if delimeter: events_queryset = events_queryset.filter(occured_on__lt=delimeter) events_queryset = events_queryset.order_by('id') acl = user.acl['categories'].get(thread.category_id, {}) if not acl.get('can_hide_events'): events_queryset = events_queryset.filter(is_hidden=False) events = [e for e in events_queryset[:50]] add_acl(user, events) for i, post in enumerate(posts[:-1]): post.events = [] while events and events[0].occured_on < posts[i + 1].posted_on: post.events.append(events.pop(0)) posts[-1].events = events
def setUp(self): User = get_user_model() self.user = User.objects.create_user("Bob", "*****@*****.**", "Pass.123") datetime = timezone.now() self.forum = Forum.objects.filter(role="forum")[:1][0] self.thread = Thread(forum=self.forum, started_on=datetime, starter_name='Tester', starter_slug='tester', last_post_on=datetime, last_poster_name='Tester', last_poster_slug='tester') self.thread.set_title("Test thread") self.thread.save() add_acl(self.user, self.forum) add_acl(self.user, self.thread)