Intitial teams support #147
@ -136,10 +136,12 @@ class ExtensionManager(models.Manager):
|
|||||||
return filter
|
return filter
|
||||||
|
|
||||||
def authored_by(self, user):
|
def authored_by(self, user):
|
||||||
return self.filter(self._authored_by_filter(user))
|
return self.filter(self._authored_by_filter(user)).distinct()
|
||||||
|
|
||||||
def listed_or_authored_by(self, user):
|
def listed_or_authored_by(self, user):
|
||||||
return self.filter(Q(status=self.model.STATUSES.APPROVED) | self._authored_by_filter(user))
|
return self.filter(
|
||||||
|
Q(status=self.model.STATUSES.APPROVED) | self._authored_by_filter(user)
|
||||||
|
).distinct()
|
||||||
|
|
||||||
|
|
||||||
class Extension(CreatedModifiedMixin, RatingMixin, TrackChangesMixin, models.Model):
|
class Extension(CreatedModifiedMixin, RatingMixin, TrackChangesMixin, models.Model):
|
||||||
|
@ -601,7 +601,7 @@ class UpdateTest(CheckFilePropertiesMixin, TestCase):
|
|||||||
{
|
{
|
||||||
**POST_DATA,
|
**POST_DATA,
|
||||||
'team': '-',
|
'team': '-',
|
||||||
'save_draft': '',
|
'save': '',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200, _get_all_form_errors(response))
|
self.assertEqual(response.status_code, 200, _get_all_form_errors(response))
|
||||||
@ -620,7 +620,7 @@ class UpdateTest(CheckFilePropertiesMixin, TestCase):
|
|||||||
{
|
{
|
||||||
**POST_DATA,
|
**POST_DATA,
|
||||||
'team': 'test-team2',
|
'team': 'test-team2',
|
||||||
'save_draft': '',
|
'save': '',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
# the field is ignored: no error expected and the team wasn't updated
|
# the field is ignored: no error expected and the team wasn't updated
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import Q
|
|
||||||
from django.shortcuts import get_object_or_404, redirect, reverse
|
from django.shortcuts import get_object_or_404, redirect, reverse
|
||||||
from django.views.generic import DetailView, ListView
|
from django.views.generic import DetailView, ListView
|
||||||
from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView
|
from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView
|
||||||
@ -100,23 +99,15 @@ class ManageListView(LoginRequiredMixin, ListView):
|
|||||||
template_name = 'extensions/manage/list.html'
|
template_name = 'extensions/manage/list.html'
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
filter = Q(maintainer__user_id=self.request.user.pk)
|
return Extension.objects.authored_by(self.request.user).prefetch_related(
|
||||||
user_teams = self.request.user.teams.all()
|
'authors',
|
||||||
if user_teams:
|
'preview_set',
|
||||||
filter = filter | Q(team__in=[t.pk for t in user_teams])
|
'preview_set__file',
|
||||||
return (
|
'ratings',
|
||||||
Extension.objects.filter(filter)
|
'team',
|
||||||
.prefetch_related(
|
'versions',
|
||||||
'authors',
|
'versions__file',
|
||||||
'preview_set',
|
'versions__tags',
|
||||||
'preview_set__file',
|
|
||||||
'ratings',
|
|
||||||
'team',
|
|
||||||
'versions',
|
|
||||||
'versions__file',
|
|
||||||
'versions__tags',
|
|
||||||
)
|
|
||||||
.distinct()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -372,10 +363,7 @@ class DraftExtensionView(
|
|||||||
"""Add all the additional forms to the context."""
|
"""Add all the additional forms to the context."""
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
if not extension_form:
|
if not extension_form:
|
||||||
extension_form = ExtensionUpdateForm(
|
extension_form = ExtensionUpdateForm(instance=self.extension, request=self.request)
|
||||||
instance=self.extension,
|
|
||||||
request=self.request,
|
|
||||||
)
|
|
||||||
context['extension_form'] = extension_form
|
context['extension_form'] = extension_form
|
||||||
context['edit_preview_formset'] = extension_form.edit_preview_formset
|
context['edit_preview_formset'] = extension_form.edit_preview_formset
|
||||||
context['add_preview_formset'] = extension_form.add_preview_formset
|
context['add_preview_formset'] = extension_form.add_preview_formset
|
||||||
@ -387,10 +375,7 @@ class DraftExtensionView(
|
|||||||
"""Handle bound forms and valid/invalid logic with the extra forms."""
|
"""Handle bound forms and valid/invalid logic with the extra forms."""
|
||||||
form = self.get_form()
|
form = self.get_form()
|
||||||
extension_form = ExtensionUpdateForm(
|
extension_form = ExtensionUpdateForm(
|
||||||
self.request.POST,
|
self.request.POST, self.request.FILES, instance=self.extension, request=self.request
|
||||||
self.request.FILES,
|
|
||||||
instance=self.extension,
|
|
||||||
request=self.request,
|
|
||||||
)
|
)
|
||||||
if form.is_valid() and extension_form.is_valid():
|
if form.is_valid() and extension_form.is_valid():
|
||||||
return self.form_valid(form, extension_form)
|
return self.form_valid(form, extension_form)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from django.contrib.auth.mixins import UserPassesTestMixin
|
from django.contrib.auth.mixins import UserPassesTestMixin
|
||||||
from django.db.models import Q
|
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from extensions.models import Extension
|
from extensions.models import Extension
|
||||||
@ -24,7 +23,7 @@ class ExtensionQuerysetMixin:
|
|||||||
if self.request.user.is_staff:
|
if self.request.user.is_staff:
|
||||||
return Extension.objects.all()
|
return Extension.objects.all()
|
||||||
if self.request.user.is_authenticated:
|
if self.request.user.is_authenticated:
|
||||||
return Extension.objects.listed_or_authored_by(self.request.user).distinct()
|
return Extension.objects.listed_or_authored_by(self.request.user)
|
||||||
return Extension.objects.listed
|
return Extension.objects.listed
|
||||||
|
|
||||||
|
|
||||||
@ -32,12 +31,8 @@ class MaintainedExtensionMixin:
|
|||||||
"""Fetch an extension by slug if current user is a maintainer."""
|
"""Fetch an extension by slug if current user is a maintainer."""
|
||||||
|
|
||||||
def dispatch(self, *args, **kwargs):
|
def dispatch(self, *args, **kwargs):
|
||||||
filter = Q(maintainer__user_id=self.request.user.pk)
|
|
||||||
user_teams = self.request.user.teams.all()
|
|
||||||
if user_teams:
|
|
||||||
filter = filter | Q(team__in=[t.pk for t in user_teams])
|
|
||||||
self.extension = get_object_or_404(
|
self.extension = get_object_or_404(
|
||||||
Extension.objects.filter(filter).distinct(),
|
Extension.objects.authored_by(self.request.user),
|
||||||
slug=self.kwargs['slug'],
|
slug=self.kwargs['slug'],
|
||||||
)
|
)
|
||||||
return super().dispatch(*args, **kwargs)
|
return super().dispatch(*args, **kwargs)
|
||||||
|
@ -18,10 +18,8 @@ class UploadFileView(LoginRequiredMixin, CreateView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
drafts = (
|
drafts = Extension.objects.authored_by(self.request.user).filter(
|
||||||
Extension.objects.authored_by(self.request.user)
|
status=Extension.STATUSES.DRAFT
|
||||||
.filter(status=Extension.STATUSES.DRAFT)
|
|
||||||
.distinct()
|
|
||||||
)
|
)
|
||||||
context['drafts'] = drafts
|
context['drafts'] = drafts
|
||||||
return context
|
return context
|
||||||
|
@ -52,13 +52,15 @@ class TeamsUsers(CreatedModifiedMixin, models.Model):
|
|||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def delete(self):
|
def delete(self):
|
||||||
# erase extension.team field if the user was the last maintainer from the team
|
# This runs when a user is leaving a team.
|
||||||
|
# If the user had authored an extension, other team members shouldn't have access to it,
|
||||||
|
# unless the extension has another maintainer who is still on that team.
|
||||||
for extension in self.user.extensions.filter(team=self.team).all():
|
for extension in self.user.extensions.filter(team=self.team).all():
|
||||||
# assuming small datasets, not optimizing db access
|
# assuming small datasets, not optimizing db access
|
||||||
authors = extension.authors.all()
|
authors = extension.authors.all()
|
||||||
has_other_authors_from_the_team = False
|
has_other_authors_from_the_team = False
|
||||||
for author in authors:
|
for author in authors:
|
||||||
if author == self.user:
|
if author.pk == self.user.pk:
|
||||||
continue
|
continue
|
||||||
if self.team in author.teams.all():
|
if self.team in author.teams.all():
|
||||||
has_other_authors_from_the_team = True
|
has_other_authors_from_the_team = True
|
||||||
|
@ -36,8 +36,8 @@ class LeaveTeamView(LoginRequiredMixin, DetailView):
|
|||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['may_leave'] = team_user.may_leave
|
context['may_leave'] = team_user.may_leave
|
||||||
context['will_lose_access_to'] = list(
|
context['will_lose_access_to'] = list(
|
||||||
Extension.objects.authored_by(self.request.user)
|
Extension.objects.authored_by(self.request.user).exclude(
|
||||||
.exclude(maintainer__user_id=self.request.user.pk)
|
maintainer__user_id=self.request.user.pk
|
||||||
.all()
|
)
|
||||||
)
|
)
|
||||||
return context
|
return context
|
||||||
|
Loading…
Reference in New Issue
Block a user