#101 Team sharing form

* adds team sharing field to share form
* splits sharing logic into user based and teams based
* adds TeamAdmin for admin backend
* adds validity check on Team name -> only unused names shall be valid
pull/122/head
mpeltriaux 3 years ago
parent 071bdea099
commit e152dfd4d7

@ -109,8 +109,8 @@ class APIV1CreateTestCase(BaseAPIV1TestCase):
Returns:
"""
self.intervention.share_with(self.superuser)
self.eco_account.share_with(self.superuser)
self.intervention.share_with_user(self.superuser)
self.eco_account.share_with_user(self.superuser)
url = reverse("api:v1:deduction")
json_file_path = "api/tests/v1/create/deduction_create_post_body.json"

@ -57,7 +57,7 @@ class APIV1DeleteTestCase(BaseAPIV1TestCase):
"""
test_intervention = self.create_dummy_intervention()
test_intervention.share_with(self.superuser)
test_intervention.share_with_user(self.superuser)
url = reverse("api:v1:intervention", args=(str(test_intervention.id),))
self._test_delete_object(test_intervention, url)
@ -68,7 +68,7 @@ class APIV1DeleteTestCase(BaseAPIV1TestCase):
"""
test_comp = self.create_dummy_compensation()
test_comp.share_with(self.superuser)
test_comp.share_with_user(self.superuser)
url = reverse("api:v1:compensation", args=(str(test_comp.id),))
self._test_delete_object(test_comp, url)
@ -79,7 +79,7 @@ class APIV1DeleteTestCase(BaseAPIV1TestCase):
"""
test_acc = self.create_dummy_eco_account()
test_acc.share_with(self.superuser)
test_acc.share_with_user(self.superuser)
url = reverse("api:v1:ecoaccount", args=(str(test_acc.id),))
self._test_delete_object(test_acc, url)
@ -90,7 +90,7 @@ class APIV1DeleteTestCase(BaseAPIV1TestCase):
"""
test_ema = self.create_dummy_ema()
test_ema.share_with(self.superuser)
test_ema.share_with_user(self.superuser)
url = reverse("api:v1:ema", args=(str(test_ema.id),))
self._test_delete_object(test_ema, url)
@ -101,7 +101,7 @@ class APIV1DeleteTestCase(BaseAPIV1TestCase):
"""
test_deduction = self.create_dummy_deduction()
test_deduction.intervention.share_with(self.superuser)
test_deduction.intervention.share_with_user(self.superuser)
url = reverse("api:v1:deduction", args=(str(test_deduction.id),))
response = self._run_delete_request(url)

@ -64,7 +64,7 @@ class APIV1GetTestCase(BaseAPIV1TestCase):
Returns:
"""
self.intervention.share_with(self.superuser)
self.intervention.share_with_user(self.superuser)
url = reverse("api:v1:intervention", args=(str(self.intervention.id),))
geojson = self._test_get_object(self.intervention, url)
self._assert_geojson_format(geojson)
@ -91,7 +91,7 @@ class APIV1GetTestCase(BaseAPIV1TestCase):
Returns:
"""
self.intervention.share_with(self.superuser)
self.intervention.share_with_user(self.superuser)
self.compensation.intervention = self.intervention
self.compensation.save()
@ -119,7 +119,7 @@ class APIV1GetTestCase(BaseAPIV1TestCase):
Returns:
"""
self.eco_account.share_with(self.superuser)
self.eco_account.share_with_user(self.superuser)
url = reverse("api:v1:ecoaccount", args=(str(self.eco_account.id),))
geojson = self._test_get_object(self.eco_account, url)
@ -148,7 +148,7 @@ class APIV1GetTestCase(BaseAPIV1TestCase):
Returns:
"""
self.ema.share_with(self.superuser)
self.ema.share_with_user(self.superuser)
url = reverse("api:v1:ema", args=(str(self.ema.id),))
geojson = self._test_get_object(self.ema, url)
@ -172,7 +172,7 @@ class APIV1GetTestCase(BaseAPIV1TestCase):
Returns:
"""
self.deduction.intervention.share_with(self.superuser)
self.deduction.intervention.share_with_user(self.superuser)
url = reverse("api:v1:deduction", args=(str(self.deduction.id),))
_json = self._test_get_object(self.deduction, url)

@ -52,7 +52,7 @@ class APIV1UpdateTestCase(BaseAPIV1TestCase):
Returns:
"""
self.intervention.share_with(self.superuser)
self.intervention.share_with_user(self.superuser)
modified_on = self.intervention.modified
url = reverse("api:v1:intervention", args=(str(self.intervention.id),))
json_file_path = "api/tests/v1/update/intervention_update_put_body.json"
@ -79,7 +79,7 @@ class APIV1UpdateTestCase(BaseAPIV1TestCase):
"""
self.compensation.intervention = self.intervention
self.compensation.save()
self.intervention.share_with(self.superuser)
self.intervention.share_with_user(self.superuser)
modified_on = self.compensation.modified
url = reverse("api:v1:compensation", args=(str(self.compensation.id),))
@ -108,7 +108,7 @@ class APIV1UpdateTestCase(BaseAPIV1TestCase):
Returns:
"""
self.eco_account.share_with(self.superuser)
self.eco_account.share_with_user(self.superuser)
modified_on = self.eco_account.modified
url = reverse("api:v1:ecoaccount", args=(str(self.eco_account.id),))
@ -139,7 +139,7 @@ class APIV1UpdateTestCase(BaseAPIV1TestCase):
Returns:
"""
self.ema.share_with(self.superuser)
self.ema.share_with_user(self.superuser)
modified_on = self.ema.modified
url = reverse("api:v1:ema", args=(str(self.ema.id),))
@ -168,8 +168,8 @@ class APIV1UpdateTestCase(BaseAPIV1TestCase):
Returns:
"""
self.deduction.intervention.share_with(self.superuser)
self.deduction.account.share_with(self.superuser)
self.deduction.intervention.share_with_user(self.superuser)
self.deduction.account.share_with_user(self.superuser)
url = reverse("api:v1:deduction", args=(str(self.deduction.id),))
json_file_path = "api/tests/v1/update/deduction_update_put_body.json"

@ -292,7 +292,7 @@ class AbstractModelShareAPIView(AbstractAPIView):
id__in=obj.shared_users
)
new_users_objs = obj.shared_users.union(new_users_to_be_added)
obj.share_with_list(new_users_objs)
obj.share_with_user_list(new_users_objs)
return True

@ -400,7 +400,7 @@ class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMix
comment=comment,
legal=legal
)
acc.share_with(user)
acc.share_with_user(user)
# Add the log entry to the main objects log list
acc.log.add(action)

@ -103,7 +103,7 @@ class CompensationViewTestCase(BaseViewTestCase):
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
self.superuser.groups.set([])
self.intervention.share_with_list([self.superuser])
self.intervention.share_with_user_list([self.superuser])
# Since the user has no groups, it does not matter that data has been shared. There SHOULD not be any difference
# to a user without access, since the important permissions are missing
@ -143,7 +143,7 @@ class CompensationViewTestCase(BaseViewTestCase):
client.login(username=self.superuser.username, password=self.superuser_pw)
self.superuser.groups.set([])
# Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state
self.intervention.share_with_list([])
self.intervention.share_with_user_list([])
# Since the user has no groups, it does not matter that data is unshared. There SHOULD not be any difference
# to a user having shared access, since all important permissions are missing
@ -185,7 +185,7 @@ class CompensationViewTestCase(BaseViewTestCase):
group = self.groups.get(name=DEFAULT_GROUP)
self.superuser.groups.set([group])
# Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state
self.intervention.share_with_list([self.superuser])
self.intervention.share_with_user_list([self.superuser])
success_urls = [
self.index_url,
@ -221,7 +221,7 @@ class CompensationViewTestCase(BaseViewTestCase):
group = self.groups.get(name=DEFAULT_GROUP)
self.superuser.groups.set([group])
# Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state
self.intervention.share_with_list([])
self.intervention.share_with_user_list([])
success_urls = [
self.index_url,

@ -25,7 +25,7 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
super().setUp()
# Give the user shared access to the dummy intervention -> inherits the access to the compensation
self.intervention.share_with(self.superuser)
self.intervention.share_with_user(self.superuser)
# Make sure the intervention itself would be fine with valid data
self.intervention = self.fill_out_intervention(self.intervention)

@ -78,7 +78,7 @@ class EcoAccountViewTestCase(CompensationViewTestCase):
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
self.superuser.groups.set([])
self.eco_account.share_with_list([self.superuser])
self.eco_account.share_with_user_list([self.superuser])
# Since the user has no groups, it does not matter that data has been shared. There SHOULD not be any difference
# to a user without access, since the important permissions are missing
@ -119,7 +119,7 @@ class EcoAccountViewTestCase(CompensationViewTestCase):
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
self.superuser.groups.set([])
self.eco_account.share_with_list([])
self.eco_account.share_with_user_list([])
# Since the user has no groups, it does not matter that data is unshared. There SHOULD not be any difference
# to a user having shared access, since all important permissions are missing
@ -163,7 +163,7 @@ class EcoAccountViewTestCase(CompensationViewTestCase):
group = self.groups.get(name=DEFAULT_GROUP)
self.superuser.groups.set([group])
# Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state
self.eco_account.share_with_list([self.superuser])
self.eco_account.share_with_user_list([self.superuser])
success_urls = [
self.index_url,
@ -200,7 +200,7 @@ class EcoAccountViewTestCase(CompensationViewTestCase):
client.login(username=self.superuser.username, password=self.superuser_pw)
group = self.groups.get(name=DEFAULT_GROUP)
self.superuser.groups.set([group])
self.eco_account.share_with_list([])
self.eco_account.share_with_user_list([])
success_urls = [
self.index_url,

@ -27,7 +27,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
# Add user to conservation office group and give shared access to the account
self.superuser.groups.add(self.groups.get(name=DEFAULT_GROUP))
self.superuser.groups.add(self.groups.get(name=ETS_GROUP))
self.eco_account.share_with_list([self.superuser])
self.eco_account.share_with_user_list([self.superuser])
def test_new(self):
""" Test the creation of an EcoAccount
@ -73,7 +73,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
Returns:
"""
self.eco_account.share_with(self.superuser)
self.eco_account.share_with_user(self.superuser)
url = reverse("compensation:acc:edit", args=(self.eco_account.id,))
pre_edit_log_count = self.eco_account.log.count()
@ -129,7 +129,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
"""
# Add proper privilege for the user
self.eco_account.share_with(self.superuser)
self.eco_account.share_with_user(self.superuser)
pre_record_log_count = self.eco_account.log.count()
# Prepare url and form data
@ -178,7 +178,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
"""
# Give user shared access to the dummy intervention, which will be needed here
self.intervention.share_with(self.superuser)
self.intervention.share_with_user(self.superuser)
pre_deduction_acc_log_count = self.eco_account.log.count()
pre_deduction_int_log_count = self.intervention.log.count()
@ -231,7 +231,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
def test_edit_deduction(self):
test_surface = self.eco_account.get_available_rest()[0]
self.eco_account.set_recorded(self.superuser)
self.intervention.share_with(self.superuser)
self.intervention.share_with_user(self.superuser)
self.eco_account.refresh_from_db()
self.assertIn(self.superuser, self.intervention.is_shared_with(self.superuser))
@ -281,8 +281,8 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
"confirm": True,
}
intervention.share_with(self.superuser)
account.share_with(self.superuser)
intervention.share_with_user(self.superuser)
account.share_with_user(self.superuser)
pre_edit_intervention_log_count = intervention.log.count()
pre_edit_account_log_count = account.log.count()

@ -64,7 +64,7 @@ class PaymentViewTestCase(BaseViewTestCase):
client = Client()
client.login(username=self.superuser.username, password=self.superuser_pw)
self.superuser.groups.set([])
self.intervention.share_with_list([self.superuser])
self.intervention.share_with_user_list([self.superuser])
# Since the user has no groups, it does not matter that data has been shared. There SHOULD not be any difference
# to a user without access, since the important permissions are missing
@ -91,7 +91,7 @@ class PaymentViewTestCase(BaseViewTestCase):
client.login(username=self.superuser.username, password=self.superuser_pw)
self.superuser.groups.set([])
# Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state
self.intervention.share_with_list([])
self.intervention.share_with_user_list([])
# Since the user has no groups, it does not matter that data is unshared. There SHOULD not be any difference
# to a user having shared access, since all important permissions are missing
@ -120,7 +120,7 @@ class PaymentViewTestCase(BaseViewTestCase):
group = self.groups.get(name=DEFAULT_GROUP)
self.superuser.groups.set([group])
# Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state
self.intervention.share_with_list([self.superuser])
self.intervention.share_with_user_list([self.superuser])
success_urls = [
self.new_url,
@ -143,7 +143,7 @@ class PaymentViewTestCase(BaseViewTestCase):
group = self.groups.get(name=DEFAULT_GROUP)
self.superuser.groups.set([group])
# Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state
self.intervention.share_with_list([])
self.intervention.share_with_user_list([])
success_urls = [
]

@ -21,7 +21,7 @@ class PaymentWorkflowTestCase(BaseWorkflowTestCase):
def setUp(self) -> None:
super().setUp()
# Give the user shared access to the dummy intervention
self.intervention.share_with(self.superuser)
self.intervention.share_with_user(self.superuser)
self.payment = Payment.objects.get_or_create(
intervention=self.intervention,

@ -796,7 +796,7 @@ def share_view(request: HttpRequest, id: str, token: str):
request,
_("{} has been shared with you").format(obj.identifier)
)
obj.share_with(user)
obj.share_with_user(user)
return redirect("compensation:acc:detail", id=id)
else:
messages.error(

@ -80,7 +80,7 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin):
)
# Add the creating user to the list of shared users
acc.share_with(user)
acc.share_with_user(user)
# Add the log entry to the main objects log list
acc.log.add(action)

@ -110,7 +110,7 @@ class EmaViewTestCase(CompensationViewTestCase):
# Sharing does not have any effect in here, since the default group will prohibit further functionality access
# to this user
self.ema.share_with_list([self.superuser])
self.ema.share_with_user_list([self.superuser])
success_urls = [
self.index_url,
@ -160,7 +160,7 @@ class EmaViewTestCase(CompensationViewTestCase):
# Sharing does not have any effect in here, since the default group will prohibit further functionality access
# to this user
self.ema.share_with_list([])
self.ema.share_with_user_list([])
success_urls = [
self.index_url,
@ -203,7 +203,7 @@ class EmaViewTestCase(CompensationViewTestCase):
groups = self.groups.filter(Q(name=ETS_GROUP)|Q(name=DEFAULT_GROUP))
self.superuser.groups.set(groups)
# Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state
self.ema.share_with_list([self.superuser])
self.ema.share_with_user_list([self.superuser])
success_urls = [
self.index_url,
@ -243,7 +243,7 @@ class EmaViewTestCase(CompensationViewTestCase):
groups = self.groups.filter(Q(name=ETS_GROUP)|Q(name=DEFAULT_GROUP))
self.superuser.groups.set(groups)
# Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state
self.ema.share_with_list([])
self.ema.share_with_user_list([])
success_urls = [
self.index_url,

@ -621,7 +621,7 @@ def share_view(request: HttpRequest, id: str, token: str):
request,
_("{} has been shared with you").format(obj.identifier)
)
obj.share_with(user)
obj.share_with_user(user)
return redirect("ema:detail", id=id)
else:
messages.error(

@ -253,7 +253,7 @@ class NewInterventionForm(BaseForm):
intervention.log.add(action)
# Add the performing user as the first user having access to the data
intervention.share_with(user)
intervention.share_with_user(user)
return intervention

@ -115,7 +115,7 @@ class ShareModalForm(BaseModalForm):
def _add_teams_to_field(self):
form_data = {
"teams": []
"team_select": self.instance.teams.all()
}
self.load_initial_data(form_data)

@ -144,7 +144,7 @@ class InterventionViewTestCase(BaseViewTestCase):
# Add user to default group
default_group = Group.objects.get(name=DEFAULT_GROUP)
self.superuser.groups.set([default_group])
self.intervention.share_with_list([self.superuser])
self.intervention.share_with_user_list([self.superuser])
success_urls = [
self.index_url,
@ -190,7 +190,7 @@ class InterventionViewTestCase(BaseViewTestCase):
# Add user to default group
default_group = Group.objects.get(name=DEFAULT_GROUP)
self.superuser.groups.set([default_group])
self.intervention.share_with_list([])
self.intervention.share_with_user_list([])
success_urls = [
self.index_url,
@ -236,7 +236,7 @@ class InterventionViewTestCase(BaseViewTestCase):
# Add user to zb group
zb_group = self.groups.get(name=ZB_GROUP)
self.superuser.groups.set([zb_group])
self.intervention.share_with_list([self.superuser])
self.intervention.share_with_user_list([self.superuser])
success_urls = [
self.index_url,
@ -282,7 +282,7 @@ class InterventionViewTestCase(BaseViewTestCase):
# Add user to zb group
zb_group = self.groups.get(name=ZB_GROUP)
self.superuser.groups.set([zb_group])
self.intervention.share_with_list([])
self.intervention.share_with_user_list([])
success_urls = [
self.index_url,
@ -328,7 +328,7 @@ class InterventionViewTestCase(BaseViewTestCase):
# Add user to ets group
ets_group = Group.objects.get(name=ETS_GROUP)
self.superuser.groups.set([ets_group])
self.intervention.share_with_list([self.superuser])
self.intervention.share_with_user_list([self.superuser])
success_urls = [
self.index_url,
@ -374,7 +374,7 @@ class InterventionViewTestCase(BaseViewTestCase):
# Add user to default group
ets_group = Group.objects.get(name=ETS_GROUP)
self.superuser.groups.set([ets_group])
self.intervention.share_with_list([])
self.intervention.share_with_user_list([])
success_urls = [
self.index_url,

@ -30,7 +30,7 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
super().setUp()
# Recreate a new (bare minimum) intervention before each test
self.intervention = self.create_dummy_intervention()
self.intervention.share_with(self.superuser)
self.intervention.share_with_user(self.superuser)
def test_new(self):
"""
@ -365,7 +365,7 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
if self.eco_account.recorded is None:
rec_action = UserActionLogEntry.get_recorded_action(self.superuser)
self.eco_account.recorded = rec_action
self.eco_account.share_with_list([self.superuser])
self.eco_account.share_with_user_list([self.superuser])
self.eco_account.save()
num_all_deducs = EcoAccountDeduction.objects.count()

@ -432,7 +432,7 @@ def share_view(request: HttpRequest, id: str, token: str):
request,
_("{} has been shared with you").format(intervention.identifier)
)
intervention.share_with(user)
intervention.share_with_user(user)
return redirect("intervention:detail", id=id)
else:
messages.error(

@ -378,6 +378,7 @@ class CheckableObjectMixin(models.Model):
class ShareableObjectMixin(models.Model):
# Users having access on this object
users = models.ManyToManyField("user.User", help_text="Users having access (data shared with)")
teams = models.ManyToManyField("user.Team", help_text="Teams having access (data shared with)")
access_token = models.CharField(
max_length=255,
null=True,
@ -435,9 +436,36 @@ class ShareableObjectMixin(models.Model):
Returns:
"""
return self.users.filter(id=user.id)
directly_shared = self.users.filter(id=user.id).exists()
team_shared = self.teams.filter(
users__in=[user]
).exists()
is_shared = directly_shared or team_shared
return is_shared
def share_with(self, user):
def share_with_team(self, team):
""" Adds team to list of shared access teans
Args:
team (Team): The team to be added to the object
Returns:
"""
self.teams.add(team)
def share_with_team_list(self, team_list: list):
""" Sets the list of shared access teams
Args:
team_list (list): The teams to be added to the object
Returns:
"""
self.teams.set(team_list)
def share_with_user(self, user):
""" Adds user to list of shared access users
Args:
@ -449,7 +477,7 @@ class ShareableObjectMixin(models.Model):
if not self.is_shared_with(user):
self.users.add(user)
def share_with_list(self, user_list: list):
def share_with_user_list(self, user_list: list):
""" Sets the list of shared access users
Args:
@ -472,24 +500,32 @@ class ShareableObjectMixin(models.Model):
from user.models import User
form_data = form.cleaned_data
keep_accessing_users = form_data["users"]
# Fetch selected teams and find out which user IDs are in removed teams -> mails need to be sent
accessing_teams = form_data["team_select"]
removed_team_users = self.teams.all().exclude(
id__in=accessing_teams
).values_list("users__id", flat=True)
new_accessing_users = list(form_data["user_select"].values_list("id", flat=True))
keep_accessing_users = form_data["users"]
accessing_users = keep_accessing_users + new_accessing_users
users = User.objects.filter(
id__in=accessing_users
)
removed_users = self.users.all().exclude(
id__in=accessing_users
).values("id")
).values_list("id", flat=True)
removed_users = removed_users.union(removed_team_users)
# Send mails
for user in removed_users:
celery_send_mail_shared_access_removed.delay(self.identifier, self.title, user["id"])
for user in new_accessing_users:
celery_send_mail_shared_access_given.delay(self.identifier, self.title, user)
for user_id in removed_users:
celery_send_mail_shared_access_removed.delay(self.identifier, self.title, user_id)
for user_id in new_accessing_users:
celery_send_mail_shared_access_given.delay(self.identifier, self.title, user_id)
# Set new shared users
self.share_with_list(users)
self.share_with_user_list(users)
self.share_with_team_list(accessing_teams)
@property
def shared_users(self) -> QuerySet:

Binary file not shown.

@ -7,8 +7,8 @@
#: compensation/forms/modalForms.py:47 compensation/forms/modalForms.py:63
#: compensation/forms/modalForms.py:356 compensation/forms/modalForms.py:463
#: intervention/forms/forms.py:54 intervention/forms/forms.py:156
#: intervention/forms/forms.py:168 intervention/forms/modalForms.py:127
#: intervention/forms/modalForms.py:140 intervention/forms/modalForms.py:153
#: intervention/forms/forms.py:168 intervention/forms/modalForms.py:148
#: intervention/forms/modalForms.py:161 intervention/forms/modalForms.py:174
#: konova/filters/mixins.py:53 konova/filters/mixins.py:54
#: konova/filters/mixins.py:81 konova/filters/mixins.py:82
#: konova/filters/mixins.py:94 konova/filters/mixins.py:95
@ -26,7 +26,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-02-17 13:42+0100\n"
"POT-Creation-Date: 2022-02-18 09:35+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -64,7 +64,7 @@ msgstr "Verantwortliche Stelle"
#: compensation/forms/forms.py:165 intervention/forms/forms.py:64
#: intervention/forms/forms.py:81 intervention/forms/forms.py:97
#: intervention/forms/forms.py:113 intervention/forms/modalForms.py:49
#: user/forms.py:196
#: intervention/forms/modalForms.py:63 user/forms.py:196
msgid "Click for selection"
msgstr "Auswählen..."
@ -221,7 +221,7 @@ msgstr "Abbuchungen"
#: compensation/templates/compensation/detail/eco_account/includes/states-before.html:36
#: ema/templates/ema/detail/includes/states-after.html:36
#: ema/templates/ema/detail/includes/states-before.html:36
#: intervention/forms/modalForms.py:338
#: intervention/forms/modalForms.py:359
msgid "Surface"
msgstr "Fläche"
@ -284,8 +284,8 @@ msgid "Type"
msgstr "Typ"
#: analysis/templates/analysis/reports/includes/old_data/amount.html:24
#: compensation/tables.py:89 intervention/forms/modalForms.py:349
#: intervention/forms/modalForms.py:356 intervention/tables.py:88
#: compensation/tables.py:89 intervention/forms/modalForms.py:370
#: intervention/forms/modalForms.py:377 intervention/tables.py:88
#: intervention/templates/intervention/detail/view.html:19
#: konova/templates/konova/includes/quickstart/interventions.html:4
#: templates/navbars/navbar.html:22
@ -295,7 +295,7 @@ msgstr "Eingriff"
#: analysis/templates/analysis/reports/includes/old_data/amount.html:34
#: compensation/tables.py:266
#: compensation/templates/compensation/detail/eco_account/view.html:20
#: intervention/forms/modalForms.py:322 intervention/forms/modalForms.py:329
#: intervention/forms/modalForms.py:343 intervention/forms/modalForms.py:350
#: konova/templates/konova/includes/quickstart/ecoaccounts.html:4
#: templates/navbars/navbar.html:34
msgid "Eco-account"
@ -364,7 +364,7 @@ msgstr "Kompensation XY; Flur ABC"
#: ema/templates/ema/detail/includes/actions.html:34
#: ema/templates/ema/detail/includes/deadlines.html:34
#: ema/templates/ema/detail/includes/documents.html:34
#: intervention/forms/forms.py:180 intervention/forms/modalForms.py:152
#: intervention/forms/forms.py:180 intervention/forms/modalForms.py:173
#: intervention/templates/intervention/detail/includes/documents.html:34
#: intervention/templates/intervention/detail/includes/payments.html:34
#: intervention/templates/intervention/detail/includes/revocation.html:38
@ -484,7 +484,7 @@ msgid "Due on which date"
msgstr "Zahlung wird an diesem Datum erwartet"
#: compensation/forms/modalForms.py:64 compensation/forms/modalForms.py:357
#: intervention/forms/modalForms.py:154 konova/forms.py:395
#: intervention/forms/modalForms.py:175 konova/forms.py:395
msgid "Additional comment, maximum {} letters"
msgstr "Zusätzlicher Kommentar, maximal {} Zeichen"
@ -512,7 +512,7 @@ msgstr "Zusatzbezeichnung"
msgid "Select an additional biotope type"
msgstr "Zusatzbezeichnung wählen"
#: compensation/forms/modalForms.py:197 intervention/forms/modalForms.py:340
#: compensation/forms/modalForms.py:197 intervention/forms/modalForms.py:361
msgid "in m²"
msgstr ""
@ -540,7 +540,7 @@ msgstr "Fristart wählen"
#: compensation/templates/compensation/detail/compensation/includes/deadlines.html:31
#: compensation/templates/compensation/detail/eco_account/includes/deadlines.html:31
#: ema/templates/ema/detail/includes/deadlines.html:31
#: intervention/forms/modalForms.py:126
#: intervention/forms/modalForms.py:147
msgid "Date"
msgstr "Datum"
@ -752,7 +752,7 @@ msgstr "Menge"
#: intervention/templates/intervention/detail/includes/documents.html:39
#: intervention/templates/intervention/detail/includes/payments.html:39
#: intervention/templates/intervention/detail/includes/revocation.html:43
#: templates/log.html:10 user/templates/user/team/index.html:33
#: templates/log.html:10 user/templates/user/team/index.html:32
msgid "Action"
msgstr "Aktionen"
@ -1000,14 +1000,14 @@ msgstr "Zuletzt bearbeitet"
#: compensation/templates/compensation/detail/compensation/view.html:100
#: compensation/templates/compensation/detail/eco_account/view.html:83
#: ema/templates/ema/detail/view.html:76 intervention/forms/modalForms.py:56
#: ema/templates/ema/detail/view.html:76 intervention/forms/modalForms.py:70
#: intervention/templates/intervention/detail/view.html:116
msgid "Shared with"
msgstr "Freigegeben für"
#: compensation/templates/compensation/detail/eco_account/includes/controls.html:15
#: ema/templates/ema/detail/includes/controls.html:15
#: intervention/forms/modalForms.py:70
#: intervention/forms/modalForms.py:84
#: intervention/templates/intervention/detail/includes/controls.html:15
msgid "Share"
msgstr "Freigabe"
@ -1325,10 +1325,22 @@ msgid "Send this link to users who you want to have writing access on the data"
msgstr "Andere Nutzer erhalten über diesen Link Zugriff auf die Daten"
#: intervention/forms/modalForms.py:41
msgid "Add team to share with"
msgstr "Team hinzufügen"
#: intervention/forms/modalForms.py:43
msgid ""
"Multiple selection possible - You can only select teams which do not already "
"have access."
msgstr ""
"Mehrfachauswahl möglich - Sie können nur Teams wählen, für die der Eintrag "
"noch nicht freigegeben wurde."
#: intervention/forms/modalForms.py:55
msgid "Add user to share with"
msgstr "Nutzer direkt hinzufügen"
#: intervention/forms/modalForms.py:43
#: intervention/forms/modalForms.py:57
msgid ""
"Multiple selection possible - You can only select users which do not already "
"have access. Enter the full username."
@ -1336,46 +1348,46 @@ msgstr ""
"Mehrfachauswahl möglich - Sie können nur Nutzer wählen, für die der Eintrag "
"noch nicht freigegeben wurde. Geben Sie den ganzen Nutzernamen an."
#: intervention/forms/modalForms.py:59
#: intervention/forms/modalForms.py:73
msgid "Remove check to remove access for this user"
msgstr "Wählen Sie die Nutzer ab, die keinen Zugriff mehr haben sollen"
#: intervention/forms/modalForms.py:71
#: intervention/forms/modalForms.py:85
msgid "Share settings for {}"
msgstr "Freigabe Einstellungen für {}"
#: intervention/forms/modalForms.py:128
#: intervention/forms/modalForms.py:149
msgid "Date of revocation"
msgstr "Datum des Widerspruchs"
#: intervention/forms/modalForms.py:139
#: intervention/forms/modalForms.py:160
#: intervention/templates/intervention/detail/includes/revocation.html:35
msgid "Document"
msgstr "Dokument"
#: intervention/forms/modalForms.py:142
#: intervention/forms/modalForms.py:163
msgid "Must be smaller than 15 Mb"
msgstr "Muss kleiner als 15 Mb sein"
#: intervention/forms/modalForms.py:167
#: intervention/forms/modalForms.py:188
#: intervention/templates/intervention/detail/includes/revocation.html:18
msgid "Add revocation"
msgstr "Widerspruch hinzufügen"
#: intervention/forms/modalForms.py:224
#: intervention/forms/modalForms.py:245
msgid "Checked intervention data"
msgstr "Eingriffsdaten geprüft"
#: intervention/forms/modalForms.py:230
#: intervention/forms/modalForms.py:251
msgid "Checked compensations data and payments"
msgstr "Kompensationen und Zahlungen geprüft"
#: intervention/forms/modalForms.py:239
#: intervention/forms/modalForms.py:260
#: intervention/templates/intervention/detail/includes/controls.html:19
msgid "Run check"
msgstr "Prüfung vornehmen"
#: intervention/forms/modalForms.py:240 konova/forms.py:514
#: intervention/forms/modalForms.py:261 konova/forms.py:514
msgid ""
"I, {} {}, confirm that all necessary control steps have been performed by "
"myself."
@ -1383,23 +1395,23 @@ msgstr ""
"Ich, {} {}, bestätige, dass die notwendigen Kontrollschritte durchgeführt "
"wurden:"
#: intervention/forms/modalForms.py:324
#: intervention/forms/modalForms.py:345
msgid "Only recorded accounts can be selected for deductions"
msgstr "Nur verzeichnete Ökokonten können für Abbuchungen verwendet werden."
#: intervention/forms/modalForms.py:351
#: intervention/forms/modalForms.py:372
msgid "Only shared interventions can be selected"
msgstr "Nur freigegebene Eingriffe können gewählt werden"
#: intervention/forms/modalForms.py:364
#: intervention/forms/modalForms.py:385
msgid "New Deduction"
msgstr "Neue Abbuchung"
#: intervention/forms/modalForms.py:365
#: intervention/forms/modalForms.py:386
msgid "Enter the information for a new deduction from a chosen eco-account"
msgstr "Geben Sie die Informationen für eine neue Abbuchung ein."
#: intervention/forms/modalForms.py:408
#: intervention/forms/modalForms.py:429
msgid ""
"Eco-account {} is not recorded yet. You can only deduct from recorded "
"accounts."
@ -1407,7 +1419,7 @@ msgstr ""
"Ökokonto {} ist noch nicht verzeichnet. Abbuchungen können nur von "
"verzeichneten Ökokonten erfolgen."
#: intervention/forms/modalForms.py:418
#: intervention/forms/modalForms.py:439
msgid ""
"The account {} has not enough surface for a deduction of {} m². There are "
"only {} m² left"
@ -2273,7 +2285,7 @@ msgstr "* sind Pflichtfelder."
msgid "New entry"
msgstr "Neuer Eintrag"
#: templates/generic_index.html:41 user/templates/user/team/index.html:23
#: templates/generic_index.html:41 user/templates/user/team/index.html:22
msgid "New"
msgstr "Neu"
@ -2406,7 +2418,7 @@ msgstr "Neue Tokens müssen durch Administratoren freigeschaltet werden!"
msgid "Team name"
msgstr "Team Name"
#: user/forms.py:179 user/templates/user/team/index.html:31
#: user/forms.py:179 user/templates/user/team/index.html:30
msgid "Description"
msgstr "Beschreibung"
@ -2434,19 +2446,23 @@ msgstr ""
"Sie werden standardmäßig der Administrator dieses Teams. Sie müssen sich "
"selbst nicht zur Liste der Mitglieder hinzufügen."
#: user/forms.py:230
#: user/forms.py:218 user/forms.py:279
msgid "Name already taken. Try another."
msgstr "Name bereits vergeben. Probieren Sie einen anderen."
#: user/forms.py:249
msgid "Admin"
msgstr "Administrator"
#: user/forms.py:231
#: user/forms.py:250
msgid "Administrators manage team details and members"
msgstr "Administratoren verwalten die Teamdaten und Mitglieder"
#: user/forms.py:244
#: user/forms.py:263
msgid "Selected admin ({}) needs to be a member of this team."
msgstr "Gewählter Administrator ({}) muss ein Mitglied des Teams sein."
#: user/forms.py:256 user/templates/user/team/index.html:52
#: user/forms.py:291 user/templates/user/team/index.html:51
msgid "Edit team"
msgstr "Team bearbeiten"
@ -2466,7 +2482,7 @@ msgstr "Gelöscht"
msgid "Show contact data"
msgstr "Zeige Kontaktdaten"
#: user/templates/user/index.html:13 user/templates/user/team/index.html:30
#: user/templates/user/index.html:13 user/templates/user/team/index.html:29
msgid "Name"
msgstr ""
@ -2515,20 +2531,20 @@ msgstr ""
msgid "Manage teams"
msgstr ""
#: user/templates/user/index.html:69 user/templates/user/team/index.html:19
#: user/templates/user/index.html:69 user/templates/user/team/index.html:18
#: user/views.py:142
msgid "Teams"
msgstr ""
#: user/templates/user/team/index.html:21
#: user/templates/user/team/index.html:20
msgid "Add new team"
msgstr "Neues Team hinzufügen"
#: user/templates/user/team/index.html:32
#: user/templates/user/team/index.html:31
msgid "Members"
msgstr "Mitglieder"
#: user/templates/user/team/index.html:55
#: user/templates/user/team/index.html:54
msgid "Remove team"
msgstr "Team entfernen"

@ -1,6 +1,6 @@
from django.contrib import admin
from user.models import UserNotification, UserActionLogEntry, User
from user.models import UserNotification, UserActionLogEntry, User, Team
class UserNotificationAdmin(admin.ModelAdmin):
@ -64,7 +64,20 @@ class UserActionLogEntryAdmin(admin.ModelAdmin):
]
class TeamAdmin(admin.ModelAdmin):
list_display = [
"name",
"description",
"admin",
]
search_fields = [
"name",
"description",
]
admin.site.register(User, UserAdmin)
admin.site.register(Team, TeamAdmin)
# Outcommented for a cleaner admin backend on production
#admin.site.register(UserNotification, UserNotificationAdmin)

@ -206,6 +206,25 @@ class NewTeamModalForm(BaseModalForm):
self.action_url = reverse("user:team-new")
self.cancel_redirect = reverse("user:team-index")
def _is_name_valid(self):
name = self.cleaned_data.get("name", None)
teams_with_same_name = Team.objects.filter(
name=name
)
name_valid = not teams_with_same_name.exists()
if not name_valid:
self.add_error(
"name",
_("Name already taken. Try another.")
)
return name_valid
def is_valid(self):
super_valid = super().is_valid()
name_valid = self._is_name_valid()
return super_valid and name_valid
def save(self):
with transaction.atomic():
team = Team.objects.create(
@ -246,6 +265,22 @@ class EditTeamModalForm(NewTeamModalForm):
return _is_valid
def _is_name_valid(self):
name = self.cleaned_data.get("name", None)
teams_with_same_name = Team.objects.filter(
name=name
).exclude(
id=self.instance.id
)
name_valid = not teams_with_same_name.exists()
if not name_valid:
self.add_error(
"name",
_("Name already taken. Try another.")
)
return name_valid
def is_valid(self):
super_valid = super().is_valid()
admin_valid = self.__is_admin_valid()

Loading…
Cancel
Save