Repository: kartoza/prj.app

Issue:
error clicking on edit widget
I logged in via Google as gavin@kartoza.com (before having a superuser account) and could see all content, with edit functionality. 

Clicked on version edit widget and got error page - infinite loop redirect


Discussion:
@sonlinux please hide the buttons on version edit (and entry edit) if they user does not have the needed permissions. E.g. http://staging.changelog.qgis.org/en/qgis/version/list/ and on the entries here :http://staging.changelog.qgis.org/en/qgis/version/3.0.0/
Maybe, while you will have look at hiding button @sonlinux, maybe you can implement a flag like this one is the view: https://github.com/kartoza/projecta/blob/develop/django_project/lesson/views/worksheet.py#L69

then in the template, you can just use this variable. It allows us later to easily comeback and add/remove new permissions in only one place.
Alright @Gustry ill just that.
Thanks

Resolving patch:
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
@@ -77,6 +77,12 @@ def get_context_data(self, **kwargs):
         :rtype: dict
         """
         context = super(VersionListView, self).get_context_data(**kwargs)
+
+        # lets set the the flags to a default
+        # for running checks in templates
+        context['user_can_edit'] = False
+        context['user_can_delete'] = False
+
         context['num_versions'] = self.get_queryset().count()
         context['unapproved'] = False
         context['rst_download'] = False
@@ -85,6 +91,26 @@ def get_context_data(self, **kwargs):
         if project_slug:
             context['the_project'] = Project.objects.get(slug=project_slug)
             context['project'] = context['the_project']
+
+        # lets check for specific user permissions here.
+        if self.request.user.is_staff:
+            context['user_can_edit'] = True
+            context['user_can_delete'] = True
+
+        if self.request.user.is_superuser:
+            context['user_can_edit'] = True
+            context['user_can_delete'] = True
+
+        version_managers = (
+            context['project'].changelog_managers.all())
+        if self.request.user in version_managers:
+            context['user_can_edit'] = True
+            context['user_can_delete'] = True
+
+        if self.request.user == context['project'].owner:
+            context['user_can_edit'] = True
+            context['user_can_delete'] = True
+
         return context
 
     def get_queryset(self):
@@ -149,6 +175,7 @@ def get_context_data(self, **kwargs):
         context['project_slug'] = project_slug
         if project_slug:
             context['project'] = Project.objects.get(slug=project_slug)
+
         return context
 
     def get_queryset(self):


Test patch:
diff --git a/django_project/changes/tests/test_views.py b/django_project/changes/tests/test_views.py
--- a/django_project/changes/tests/test_views.py
+++ b/django_project/changes/tests/test_views.py
@@ -752,8 +752,348 @@ def test_VersionDelete_no_login(self):
         self.assertEqual(response.status_code, 302)
 
 
+class TestVersionViewsWithAnonymousUserForCRUD(TestCase):
+    """
+    Check if anonymous user can perform CRUD operations on version entries
+    just in case they have the URL to the views.
+    """
+
+    @override_settings(VALID_DOMAIN=['testserver', ])
+    def setUp(self):
+        """
+        Setup before each test.
+        We force the locale to en otherwise it will use
+        the locale of the host running the tests and we
+        will get unpredictable results / 404s.
+        """
+
+        self.client = Client()
+        self.client.post(
+            '/set_language/', data={'language': 'en'})
+        logging.disable(logging.CRITICAL)
+        self.project = ProjectF.create(name='testproject')
+        self.version = VersionF.create(
+            project=self.project,
+            name='1.0.1')
+        self.category = CategoryF.create(
+            project=self.project,
+            name='testcategory')
+
+        self.user = None
+
+    @override_settings(VALID_DOMAIN=['testserver', ])
+    def tearDown(self):
+        """
+        Teardown after each test.
+        """
+        self.project.delete()
+        self.version.delete()
+        self.category.delete()
+
+    @override_settings(VALID_DOMAIN=['testserver', ])
+    def test_VersionListView_with_anonymous_user(self):
+        """
+        Test if anonymous user can view version entry list.
+        """
+        response = self.client.get(reverse('version-list', kwargs={
+            'project_slug': self.project.slug
+        }))
+
+        expected_templates = [
+            'version/list.html', u'changes/version_list.html'
+        ]
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.template_name, expected_templates)
+        self.assertEqual(response.context_data['object_list'][0],
+                         self.version)
+
+    @override_settings(VALID_DOMAIN=['testserver', ])
+    def test_VersionCreateView_with_anonymous_user(self):
+        """
+        Test if anonymous user can create a version entry.
+        """
+        response = self.client.get(reverse('version-create', kwargs={
+            'project_slug': self.project.slug
+        }))
+        self.assertEqual(response.status_code, 302)
+
+    @override_settings(VALID_DOMAIN=['testserver'])
+    def test_VersionUpdateView_with_anonymous_user(self):
+        """
+        Test if anonymous user can update a version entry.
+        """
+        response = self.client.get(reverse('version-create', kwargs={
+            'project_slug': self.project.slug
+        }))
+        self.assertEqual(response.status_code, 302)
+
+    @override_settings(VALID_DOMAIN=['testserver'])
+    def test_VersionDeleteView_with_anonymous_user(self):
+        """
+        Test if anonymous user can delete a version entry.
+        """
+        response = self.client.get(reverse('version-delete', kwargs={
+            'slug': self.version.slug,
+            'project_slug': self.version.project.slug
+        }))
+        self.assertEqual(response.status_code, 302)
+
+
+class TestVersionViewsWithNormalUserForCRUD(TestCase):
+    """
+    Check if normal(a regular logged in user) user can perform CRUD.
+    """
+
+    @override_settings(VALID_DOMAIN=['testserver', ])
+    def setUp(self):
+        """
+        Setup before each test.
+        We force the locale to en otherwise it will use
+        the locale of the host running the tests and we
+        will get unpredictable results / 404s.
+        """
+
+        self.client = Client()
+        self.client.post(
+            '/set_language/', data={'language': 'en'})
+        logging.disable(logging.CRITICAL)
+        self.project = ProjectF.create(name='testproject')
+        self.version = VersionF.create(
+            project=self.project,
+            name='1.0.1')
+        self.category = CategoryF.create(
+            project=self.project,
+            name='testcategory')
+
+        # Here we create a normal User without staff permissions.
+        self.user = UserF.create(**{
+            'username': 'sonlinux',
+            'password': 'password',
+            'is_staff': False
+        })
+
+        # Something changed in the way factoryboy works with django 1.8
+        # I think - we need to explicitly set the users password
+        # because the core.model_factories.UserF._prepare method
+        # which sets the password is never called. Next two lines are
+        # a work around for that - sett #581
+        self.user.set_password('password')
+        self.user.save()
+
+    @override_settings(VALID_DOMAIN=['testserver', ])
+    def tearDown(self):
+        """
+        Teardown after each test.
+        """
+        self.project.delete()
+        self.version.delete()
+        self.category.delete()
+        self.user.delete()
+
+    @override_settings(VALID_DOMAIN=['testserver', ])
+    def test_VersionListView_with_normal_user(self):
+        """
+        Test if a normal user can view a list of version entries.
+        """
+        self.client.login(username='sonlinux', password='password')
+        response = self.client.get(reverse('version-list', kwargs={
+            'project_slug': self.project.slug
+        }))
+
+        expected_templates = [
+            'version/list.html', u'changes/version_list.html'
+        ]
+
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.template_name, expected_templates)
+        self.assertEqual(response.context_data['object_list'][0],
+                         self.version)
+
+    @override_settings(VALID_DOMAIN=['testserver', ])
+    def test_VersionCreateView_with_normal_user(self):
+        """
+        Test if a normal user can create a list of version entries.
+        """
+        self.client.login(username='sonlinux', password='password')
+        response = self.client.get(reverse('version-create', kwargs={
+            'project_slug': self.project.slug
+        }))
+
+        self.assertEqual(response.status_code, 200)
+
+    @override_settings(VALID_DOMAIN=['testserver', ])
+    def test_VersionUpdateView_with_normal_user(self):
+        """
+        Test if normal user can update a version entry.
+        """
+        self.client.login(username='sonlinux', password='password')
+
+        # lets ensure the user updating owns the project.
+        response = self.client.get(reverse('category-delete', kwargs={
+            'slug': self.category.slug,
+            'project_slug': self.category.project.slug
+        }))
+
+        self.assertEqual(response.status_code, 200)
+        expected_templates = [
+            'category/delete.html'
+        ]
+
+        self.assertEqual(response.template_name, expected_templates)
+
+    @override_settings(VALID_DOMAIN=['testserver', ])
+    def test_VersionDeleteView_with_normal_user(self):
+        """
+        Test if a normal user can delete a version entry.
+        """
+        self.client.login(username='sonlinux', password='password')
+
+        response = self.client.get(reverse('category-delete', kwargs={
+            'slug': self.category.slug,
+            'project_slug': self.category.project.slug
+        }))
+
+        self.assertEqual(response.status_code, 200)
+        expected_templates = [
+            'category/delete.html'
+        ]
+
+        self.assertEqual(response.template_name, expected_templates)
+
+
+class TestVersionViewsWithStaffUserForCRUD(TestCase):
+    """
+    Test if staff user can perform CRUD operations on a version entry.
+    """
+
+    @override_settings(VALID_DOMAIN=['testserver', ])
+    def setUp(self):
+        """
+        Setup before each test.
+        We force the locale to en otherwise it will use
+        the locale of the host running the tests and we
+        will get unpredictable results / 404s.
+        """
+
+        self.client = Client()
+        self.client.post(
+            '/set_language/', data={'language': 'en'})
+        logging.disable(logging.CRITICAL)
+        self.project = ProjectF.create(name='testproject')
+        self.version = VersionF.create(
+            project=self.project,
+            name='1.0.1')
+        self.category = CategoryF.create(
+            project=self.project,
+            name='testcategory')
+
+        self.user = UserF.create(**{
+            'username': 'sonlinux',
+            'password': 'password',
+            'is_staff': True
+        })
+
+        # Something changed in the way factoryboy works with django 1.8
+        # I think - we need to explicitly set the users password
+        # because the core.model_factories.UserF._prepare method
+        # which sets the password is never called. Next two lines are
+        # a work around for that - sett #581
+        self.user.set_password('password')
+        self.user.save()
+
+    @override_settings(VALID_DOMAIN=['testserver', ])
+    def tearDown(self):
+        """
+        Teardown after each test.
+        """
+        self.project.delete()
+        self.version.delete()
+        self.category.delete()
+        self.user.delete()
+
+    @override_settings(VALID_DOMAIN=['testserver', ])
+    def test_VersionListView_with_staff_user(self):
+        """
+        Test if staff user can view a list of version entries.
+        """
+        response = self.client.get(reverse('version-list', kwargs={
+            'project_slug': self.project.slug
+        }))
+        self.assertEqual(response.status_code, 200)
+
+        expected_template = [
+            'version/list.html', u'changes/version_list.html'
+        ]
+
+        self.assertEqual(response.template_name, expected_template)
+
+    @override_settings(VALID_DOMAIN=['testserver', ])
+    def test_VersionCreateView_with_staff_user(self):
+        """
+        Test if staff user can create a version entry.
+        """
+        self.client.login(username='sonlinux', password='password')
+
+        post_data = {
+            'project': self.project.id,
+            'name': u'2.0.3',
+            'description': u'Test create with staff user',
+            'author': self.user.id
+        }
+
+        response = self.client.get(reverse('version-create', kwargs={
+            'project_slug': self.project.slug
+        }), post_data)
+
+        self.assertEqual(response.status_code, 200)
+
+        expected_template = [
+            'version/create.html'
+        ]
+        self.assertEqual(response.template_name, expected_template)
+
+    @override_settings(VALID_DOMAIN=['testserver', ])
+    def test_VersionUpdateView_with_staff_user(self):
+        """
+        Test if staff user can update a version entry.
+        """
+        self.client.login(username='sonlinux', password='password')
+
+        response = self.client.get(reverse('version-update', kwargs={
+            'project_slug': self.version.project.slug,
+            'slug': self.version.slug
+        }))
+        self.assertEqual(response.status_code, 200)
+
+        expected_templates = [
+            'version/update.html'
+        ]
+        self.assertEqual(response.template_name, expected_templates)
+
+    @override_settings(VALID_DOMAIN=['testserver', ])
+    def test_VersionDeleteView_with_staff_user(self):
+        """
+        Test if staff user can delete a version entry.
+        """
+        self.client.login(username='sonlinux', password='password')
+
+        response = self.client.get(reverse('category-delete', kwargs={
+            'slug': self.category.slug,
+            'project_slug': self.category.project.slug
+        }))
+
+        self.assertEqual(response.status_code, 200)
+        expected_templates = [
+            'category/delete.html'
+        ]
+
+        self.assertEqual(response.template_name, expected_templates)
+
+
 class TestSponsorshipLevelViews(TestCase):
-    """Tests that SponsorshipLevel views work."""
+    """
+    Tests that SponsorshipLevel views work.
+    """
 
     @override_settings(VALID_DOMAIN=['testserver', ])
     def setUp(self):