projects can be public, registered, private
This commit is contained in:
parent
c0e1dcdde0
commit
6ad5e46776
6 changed files with 68 additions and 60 deletions
File diff suppressed because one or more lines are too long
|
@ -8,9 +8,9 @@ from issue.models import *
|
||||||
|
|
||||||
|
|
||||||
AddProjectForm = modelform_factory(Project,
|
AddProjectForm = modelform_factory(Project,
|
||||||
fields=['display_name', 'name', 'description', 'public'])
|
fields=['display_name', 'name', 'description', 'access'])
|
||||||
EditProjectForm = modelform_factory(Project,
|
EditProjectForm = modelform_factory(Project,
|
||||||
fields=['display_name', 'description', 'public'])
|
fields=['display_name', 'description', 'access'])
|
||||||
LabelForm = modelform_factory(Label,
|
LabelForm = modelform_factory(Label,
|
||||||
fields=['name', 'color', 'inverted'])
|
fields=['name', 'color', 'inverted'])
|
||||||
TeamForm = modelform_factory(Team,
|
TeamForm = modelform_factory(Team,
|
||||||
|
|
|
@ -29,8 +29,9 @@ class ProjectMiddleware:
|
||||||
if request.user.is_authenticated() and request.user.is_staff:
|
if request.user.is_authenticated() and request.user.is_staff:
|
||||||
projects = Project.objects.all()
|
projects = Project.objects.all()
|
||||||
else:
|
else:
|
||||||
query = Q(public=True)
|
query = Q(access=Project.ACCESS_PUBLIC)
|
||||||
if request.user.is_authenticated():
|
if request.user.is_authenticated():
|
||||||
|
query |= Q(access=Project.ACCESS_REGISTERED)
|
||||||
# access granted through a team
|
# access granted through a team
|
||||||
teams = request.user.teams.values_list('name')
|
teams = request.user.teams.values_list('name')
|
||||||
query |= Q(permissions__grantee_type=PermModel.GRANTEE_TEAM,
|
query |= Q(permissions__grantee_type=PermModel.GRANTEE_TEAM,
|
||||||
|
|
24
issue/migrations/0002_auto_20140823_0532.py
Normal file
24
issue/migrations/0002_auto_20140823_0532.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('issue', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='project',
|
||||||
|
name='access',
|
||||||
|
field=models.IntegerField(choices=[(1, 'Public'), (2, 'Connected users'), (3, 'Private')], default=1),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='project',
|
||||||
|
name='public',
|
||||||
|
),
|
||||||
|
]
|
|
@ -29,6 +29,15 @@ class User(AbstractUser):
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Project(models.Model):
|
class Project(models.Model):
|
||||||
|
|
||||||
|
ACCESS_PUBLIC = 1
|
||||||
|
ACCESS_REGISTERED = 2
|
||||||
|
ACCESS_PRIVATE = 3
|
||||||
|
ACCESS_TYPE = (
|
||||||
|
(ACCESS_PUBLIC, 'Public'),
|
||||||
|
(ACCESS_REGISTERED, 'Registration required'),
|
||||||
|
(ACCESS_PRIVATE, 'Private'),
|
||||||
|
)
|
||||||
|
|
||||||
url_name_validator = RegexValidator(regex='^[a-z0-9_-]+$',
|
url_name_validator = RegexValidator(regex='^[a-z0-9_-]+$',
|
||||||
message="Please enter only lowercase characters, number, "
|
message="Please enter only lowercase characters, number, "
|
||||||
"underscores or hyphens.")
|
"underscores or hyphens.")
|
||||||
|
@ -43,9 +52,7 @@ class Project(models.Model):
|
||||||
description = models.TextField(blank=True, default="",
|
description = models.TextField(blank=True, default="",
|
||||||
verbose_name="Description")
|
verbose_name="Description")
|
||||||
|
|
||||||
public = models.BooleanField(default=True,
|
access = models.IntegerField(choices=ACCESS_TYPE, default=ACCESS_PUBLIC)
|
||||||
verbose_name="Do unregistered users have read access "
|
|
||||||
"to this project?")
|
|
||||||
|
|
||||||
subscribers = models.ManyToManyField(User, blank=True, null=True,
|
subscribers = models.ManyToManyField(User, blank=True, null=True,
|
||||||
related_name='subscribed_projects')
|
related_name='subscribed_projects')
|
||||||
|
|
|
@ -72,32 +72,6 @@ class TestPermissions(TestCase):
|
||||||
self.assertFalse(user.has_perm('modify_issue', project))
|
self.assertFalse(user.has_perm('modify_issue', project))
|
||||||
self.assertTrue(user.has_perm('delete_issue', project))
|
self.assertTrue(user.has_perm('delete_issue', project))
|
||||||
|
|
||||||
def test_unregistered_project_list(self):
|
|
||||||
response = self.client.get('/')
|
|
||||||
projects = response.context['projects']
|
|
||||||
project = Project.objects.get(name='project-1')
|
|
||||||
self.assertEqual(len(projects), 1)
|
|
||||||
self.assertEqual(projects[0], project)
|
|
||||||
|
|
||||||
def test_ungranted_project_list(self):
|
|
||||||
self.client.login(username='user1', password='user1')
|
|
||||||
response = self.client.get('/')
|
|
||||||
projects = response.context['projects']
|
|
||||||
project = Project.objects.get(name='project-1')
|
|
||||||
self.assertEqual(len(projects), 1)
|
|
||||||
self.assertEqual(projects[0], project)
|
|
||||||
self.client.logout()
|
|
||||||
|
|
||||||
def test_granted_project_list(self):
|
|
||||||
self.client.login(username='user2', password='user2')
|
|
||||||
response = self.client.get('/')
|
|
||||||
projects = response.context['projects']
|
|
||||||
project1 = Project.objects.get(name='project-1')
|
|
||||||
project2 = Project.objects.get(name='project-2')
|
|
||||||
self.assertEqual(len(projects), 2)
|
|
||||||
self.assertTrue(project1 in projects)
|
|
||||||
self.assertTrue(project2 in projects)
|
|
||||||
|
|
||||||
|
|
||||||
class TestNoProject(TestCase):
|
class TestNoProject(TestCase):
|
||||||
|
|
||||||
|
@ -180,7 +154,8 @@ class TestProjectsViews(TestCase):
|
||||||
lambda x: x)
|
lambda x: x)
|
||||||
|
|
||||||
def test_home_as_user1(self):
|
def test_home_as_user1(self):
|
||||||
expected = Project.objects.filter(name='project-1')
|
expected = Project.objects \
|
||||||
|
.filter(Q(name='project-1') | Q(name='project-3'))
|
||||||
self.client.login(username='user1', password='user1')
|
self.client.login(username='user1', password='user1')
|
||||||
url = reverse('list-project')
|
url = reverse('list-project')
|
||||||
self.assertEqual(url, '/')
|
self.assertEqual(url, '/')
|
||||||
|
@ -202,7 +177,8 @@ class TestProjectsViews(TestCase):
|
||||||
self.assertNotContains(response, 'New project')
|
self.assertNotContains(response, 'New project')
|
||||||
|
|
||||||
def test_home_as_user3(self):
|
def test_home_as_user3(self):
|
||||||
expected = Project.objects.filter(name='project-1')
|
expected = Project.objects \
|
||||||
|
.filter(Q(name='project-1') | Q(name='project-3'))
|
||||||
self.client.login(username='user3', password='user3')
|
self.client.login(username='user3', password='user3')
|
||||||
url = reverse('list-project')
|
url = reverse('list-project')
|
||||||
self.assertEqual(url, '/')
|
self.assertEqual(url, '/')
|
||||||
|
@ -225,41 +201,41 @@ class TestProjectsViews(TestCase):
|
||||||
|
|
||||||
def test_add_project_granted(self):
|
def test_add_project_granted(self):
|
||||||
self.client.login(username='user3', password='user3')
|
self.client.login(username='user3', password='user3')
|
||||||
expected_url = reverse('list-project-permission', args=['project-3'])
|
expected_url = reverse('list-project-permission', args=['project-4'])
|
||||||
url = reverse('add-project')
|
url = reverse('add-project')
|
||||||
response = self.client.post(url, {
|
response = self.client.post(url, {
|
||||||
'name': 'project-3',
|
'name': 'project-4',
|
||||||
'display_name': 'Project 3',
|
'display_name': 'Project 4',
|
||||||
'description': 'This is the third project.',
|
'description': 'This is the fourth project.',
|
||||||
|
'access': Project.ACCESS_PUBLIC,
|
||||||
})
|
})
|
||||||
self.assertRedirects(response, expected_url)
|
self.assertRedirects(response, expected_url)
|
||||||
self.assertQuerysetEqual(Project.objects.all(),
|
self.assertQuerysetEqual(Project.objects.all(), ['project-%s' % x
|
||||||
['project-1', 'project-2', 'project-3'],
|
for x in (1, 2, 3, 4)], lambda x: x.name, ordered=False)
|
||||||
lambda x: x.name, ordered=False)
|
|
||||||
|
|
||||||
def test_add_project_forbidden(self):
|
def test_add_project_forbidden(self):
|
||||||
self.client.login(username='user1', password='user1')
|
self.client.login(username='user1', password='user1')
|
||||||
url = reverse('add-project')
|
url = reverse('add-project')
|
||||||
response = self.client.post(url, {
|
response = self.client.post(url, {
|
||||||
'name': 'project-3',
|
'name': 'project-4',
|
||||||
'display_name': 'Project 3',
|
'display_name': 'Project 4',
|
||||||
'description': 'This is the third project.',
|
'description': 'This is the foorth project.',
|
||||||
})
|
})
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
self.assertQuerysetEqual(Project.objects.all(),
|
self.assertQuerysetEqual(Project.objects.all(), ['project-%s' % x
|
||||||
['project-1', 'project-2'], lambda x: x.name, ordered=False)
|
for x in (1, 2, 3)], lambda x: x.name, ordered=False)
|
||||||
|
|
||||||
def test_add_project_forbidden_ano(self):
|
def test_add_project_forbidden_ano(self):
|
||||||
expected_url = reverse('login') + '?next=' + reverse('add-project')
|
expected_url = reverse('login') + '?next=' + reverse('add-project')
|
||||||
url = reverse('add-project')
|
url = reverse('add-project')
|
||||||
response = self.client.post(url, {
|
response = self.client.post(url, {
|
||||||
'name': 'project-3',
|
'name': 'project-4',
|
||||||
'display_name': 'Project 3',
|
'display_name': 'Project 4',
|
||||||
'description': 'This is the third project.',
|
'description': 'This is the foorth project.',
|
||||||
})
|
})
|
||||||
self.assertRedirects(response, expected_url)
|
self.assertRedirects(response, expected_url)
|
||||||
self.assertQuerysetEqual(Project.objects.all(),
|
self.assertQuerysetEqual(Project.objects.all(), ['project-%s' % x
|
||||||
['project-1', 'project-2'], lambda x: x.name, ordered=False)
|
for x in (1, 2, 3)], lambda x: x.name, ordered=False)
|
||||||
|
|
||||||
def test_delete_project_get(self):
|
def test_delete_project_get(self):
|
||||||
self.client.login(username='user1', password='user1')
|
self.client.login(username='user1', password='user1')
|
||||||
|
@ -267,8 +243,8 @@ class TestProjectsViews(TestCase):
|
||||||
url = reverse('delete-project', args=['project-1'])
|
url = reverse('delete-project', args=['project-1'])
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertEqual(response.status_code, 405)
|
self.assertEqual(response.status_code, 405)
|
||||||
self.assertQuerysetEqual(Project.objects.all(),
|
self.assertQuerysetEqual(Project.objects.all(), ['project-%s' % x
|
||||||
['project-1', 'project-2'], lambda x: x.name, ordered=False)
|
for x in (1, 2, 3)], lambda x: x.name, ordered=False)
|
||||||
|
|
||||||
def test_delete_project_granted(self):
|
def test_delete_project_granted(self):
|
||||||
self.client.login(username='user1', password='user1')
|
self.client.login(username='user1', password='user1')
|
||||||
|
@ -276,16 +252,16 @@ class TestProjectsViews(TestCase):
|
||||||
url = reverse('delete-project', args=['project-1'])
|
url = reverse('delete-project', args=['project-1'])
|
||||||
response = self.client.post(url)
|
response = self.client.post(url)
|
||||||
self.assertRedirects(response, expected_url)
|
self.assertRedirects(response, expected_url)
|
||||||
self.assertQuerysetEqual(Project.objects.all(),
|
self.assertQuerysetEqual(Project.objects.all(), ['project-%s' % x
|
||||||
['project-2'], lambda x: x.name, ordered=False)
|
for x in (2, 3)], lambda x: x.name, ordered=False)
|
||||||
|
|
||||||
def test_delete_project_forbidden(self):
|
def test_delete_project_forbidden(self):
|
||||||
self.client.login(username='user2', password='user2')
|
self.client.login(username='user2', password='user2')
|
||||||
url = reverse('delete-project', args=['project-1'])
|
url = reverse('delete-project', args=['project-1'])
|
||||||
response = self.client.post(url)
|
response = self.client.post(url)
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
self.assertQuerysetEqual(Project.objects.all(),
|
self.assertQuerysetEqual(Project.objects.all(), ['project-%s' % x
|
||||||
['project-1', 'project-2'], lambda x: x.name, ordered=False)
|
for x in (1, 2, 3)], lambda x: x.name, ordered=False)
|
||||||
|
|
||||||
def test_delete_project_forbidden_ano(self):
|
def test_delete_project_forbidden_ano(self):
|
||||||
expected_url = reverse('login') + '?next=' \
|
expected_url = reverse('login') + '?next=' \
|
||||||
|
@ -293,8 +269,8 @@ class TestProjectsViews(TestCase):
|
||||||
url = reverse('delete-project', args=['project-1'])
|
url = reverse('delete-project', args=['project-1'])
|
||||||
response = self.client.post(url)
|
response = self.client.post(url)
|
||||||
self.assertRedirects(response, expected_url)
|
self.assertRedirects(response, expected_url)
|
||||||
self.assertQuerysetEqual(Project.objects.all(),
|
self.assertQuerysetEqual(Project.objects.all(), ['project-%s' % x
|
||||||
['project-1', 'project-2'], lambda x: x.name, ordered=False)
|
for x in (1, 2, 3)], lambda x: x.name, ordered=False)
|
||||||
|
|
||||||
|
|
||||||
class TestIssuesViews(TestCase):
|
class TestIssuesViews(TestCase):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue