Repository: kartoza/prj.app

Issue:
Unable to add changelog to project even with write permisions
# Problem
I have been granted access (manager) for a project but I get a 404 when I try to edit the project and also there is no option to add the changelog.

404 when editing the project
![error](https://user-images.githubusercontent.com/2510900/35043968-3ddc677a-fb97-11e7-86e9-637ed3fd84db.png)

No add option for changelog
![no_versions](https://user-images.githubusercontent.com/2510900/35043981-47ed87b2-fb97-11e7-86ba-52dec321e323.png)

# Solution
Make it happy
  


Discussion:

Resolving patch:
diff --git a/django_project/changes/views/category.py b/django_project/changes/views/category.py
--- a/django_project/changes/views/category.py
+++ b/django_project/changes/views/category.py
@@ -18,8 +18,9 @@
     RedirectView)
 from django.http import HttpResponseRedirect, Http404
 from django.db import IntegrityError
+from django.db.models import Q
 from django.core.exceptions import ValidationError
-from braces.views import LoginRequiredMixin, StaffuserRequiredMixin
+from braces.views import LoginRequiredMixin
 from ..models import Category, Version
 from ..forms import CategoryForm
 
@@ -127,7 +128,7 @@ def get_queryset(self):
         return qs
 
 
-class CategoryListView(CategoryMixin, StaffuserRequiredMixin, ListView):
+class CategoryListView(LoginRequiredMixin, CategoryMixin, ListView):
     """List view for Category."""
     context_object_name = 'categories'
     template_name = 'category/list.html'
@@ -183,7 +184,7 @@ def get_queryset(self, queryset=None):
             return queryset
 
 
-class CategoryOrderView(StaffuserRequiredMixin, CategoryMixin, ListView):
+class CategoryOrderView(LoginRequiredMixin, CategoryMixin, ListView):
     """List view to order category"""
     context_object_name = 'categories'
     template_name = 'category/order.html'
@@ -228,7 +229,10 @@ def get_queryset(self, queryset=None):
                         'approved yet. Try logging in as a staff member if '
                         'you wish to view it.')
                 queryset = Category.approved_objects.filter(
-                    project=project).order_by('sort_number')
+                    Q(project=project) &
+                    (Q(project__owner=self.request.user) |
+                     Q(project__changelog_manager=self.request.user))
+                ).order_by('sort_number')
                 return queryset
             else:
                 raise Http404(
@@ -506,11 +510,16 @@ def get_queryset(self):
         projects which user created (staff gets all projects)
         :rtype: QuerySet
         """
+        project_slug = self.kwargs.get('project_slug', None)
+        project = Project.objects.get(slug=project_slug)
         qs = Category.approved_objects
         if self.request.user.is_staff:
             return qs
         else:
-            return qs.filter(creator=self.request.user)
+            return qs.filter(
+                Q(project=project) &
+                (Q(project__owner=self.request.user) |
+                 Q(project__changelog_manager=self.request.user)))
 
     def get_success_url(self):
         """Define the redirect URL
@@ -535,7 +544,7 @@ def form_valid(self, form):
 
 
 class PendingCategoryListView(
-        StaffuserRequiredMixin, CategoryMixin, ListView):
+        LoginRequiredMixin, CategoryMixin, ListView):
     """List view for pending Category."""
     context_object_name = 'categories'
     template_name = 'category/list.html'
@@ -590,7 +599,9 @@ def get_queryset(self):
                         'approved yet. Try logging in as a staff member if '
                         'you wish to view it.')
                 self.queryset = Category.unapproved_objects.filter(
-                    project=self.project)
+                    Q(project=self.project) &
+                    (Q(project__owner=self.request.user) |
+                     Q(project__changelog_manager=self.request.user)))
                 return self.queryset
             else:
                 raise Http404(
@@ -599,7 +610,7 @@ def get_queryset(self):
         return self.queryset
 
 
-class ApproveCategoryView(CategoryMixin, StaffuserRequiredMixin, RedirectView):
+class ApproveCategoryView(LoginRequiredMixin, CategoryMixin, RedirectView):
     """Redirect view for approving Category."""
     permanent = False
     query_string = True
diff --git a/django_project/changes/views/sponsor.py b/django_project/changes/views/sponsor.py
--- a/django_project/changes/views/sponsor.py
+++ b/django_project/changes/views/sponsor.py
@@ -573,7 +573,7 @@ def get_queryset(self):
         return self.queryset
 
 
-class ApproveSponsorView(SponsorMixin, LoginRequiredMixin, RedirectView):
+class ApproveSponsorView(LoginRequiredMixin, SponsorMixin, RedirectView):
     """Redirect view for approving Sponsor."""
     permanent = False
     query_string = True
@@ -668,7 +668,7 @@ def generate_sponsor_cloud(request, **kwargs):
             'the_project': project})
 
 
-class GenerateSponsorPDFView(SponsorMixin, LoginRequiredMixin, TemplateView):
+class GenerateSponsorPDFView(LoginRequiredMixin, SponsorMixin, TemplateView):
     """Template View for invoice generation"""
     context_object_name = 'sponsors'
     template_name = 'sponsor/invoice.html'
diff --git a/django_project/changes/views/version.py b/django_project/changes/views/version.py
--- a/django_project/changes/views/version.py
+++ b/django_project/changes/views/version.py
@@ -371,13 +371,19 @@ def get_queryset(self):
         :rtype: QuerySet
         :raises: Http404
         """
+        project_slug = self.kwargs.get('project_slug', None)
+        project = Project.objects.get(slug=project_slug)
         if not self.request.user.is_authenticated():
             raise Http404
-        qs = Version.objects.filter(project=self.project)
+        qs = Version.objects.filter(project=project)
         if self.request.user.is_staff:
             return qs
         else:
-            return qs.filter(author=self.request.user)
+            return qs.filter(
+                Q(project=project) &
+                (Q(author=self.request.user) |
+                 Q(project__owner=self.request.user) |
+                 Q(project__changelog_manager=self.request.user)))
 
 
 # noinspection PyAttributeOutsideInit
@@ -438,7 +444,7 @@ def form_valid(self, form):
 
 
 # noinspection PyAttributeOutsideInit
-class VersionUpdateView(StaffuserRequiredMixin, VersionMixin, UpdateView):
+class VersionUpdateView(LoginRequiredMixin, VersionMixin, UpdateView):
     """Update view for Version."""
     context_object_name = 'version'
     template_name = 'version/update.html'
@@ -464,10 +470,17 @@ def get_queryset(self):
         :returns: A queryset which is filtered to only show approved Versions.
         :rtype: QuerySet
         """
+        project_slug = self.kwargs.get('project_slug', None)
+        project = Project.objects.get(slug=project_slug)
+        versions_qs = Version.objects.all()
         if self.request.user.is_staff:
-            versions_qs = Version.objects.all()
+            versions_qs = versions_qs
         else:
-            versions_qs = Version.approved_objects.all()
+            versions_qs = versions_qs.filter(
+                Q(project=project) &
+                (Q(author=self.request.user) |
+                 Q(project__owner=self.request.user) |
+                 Q(project__changelog_manager=self.request.user)))
         return versions_qs
 
     def get_success_url(self):


Test patch: