diff --git a/src/phonebook/models.py b/src/phonebook/models.py index 73d8aa83c..d9dc08726 100644 --- a/src/phonebook/models.py +++ b/src/phonebook/models.py @@ -95,6 +95,9 @@ def save(self, *args, **kwargs) -> None: self.check_unique_ipei() super().save(*args, **kwargs) + def __str__(self) -> str: + return f"{self.number or self.letters} ({self.user.profile.get_name})" + def check_unique_ipei(self) -> None: """Check IPEI is unique.""" if ( diff --git a/src/teams/migrations/0065_team_public_dect_number_team_public_phone_number.py b/src/teams/migrations/0065_team_public_dect_number_team_public_phone_number.py new file mode 100644 index 000000000..9264c7e73 --- /dev/null +++ b/src/teams/migrations/0065_team_public_dect_number_team_public_phone_number.py @@ -0,0 +1,37 @@ +# Generated by Django 5.2.9 on 2026-01-23 11:48 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("phonebook", "0005_alter_dectregistration_ipei"), + ("teams", "0064_alter_team_facilitator_group_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="team", + name="public_dect_number", + field=models.ForeignKey( + blank=True, + help_text="The public DECT for this team.", + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="public_team_numbers", + to="phonebook.dectregistration", + ), + ), + migrations.AddField( + model_name="team", + name="public_phone_number", + field=models.CharField( + blank=True, + help_text="The public phonenumber for this team.", + max_length=14, + null=True, + ), + ), + ] diff --git a/src/teams/models.py b/src/teams/models.py index a838e11f1..15c458e74 100644 --- a/src/teams/models.py +++ b/src/teams/models.py @@ -5,15 +5,15 @@ from typing import TYPE_CHECKING from django.conf import settings +from django.core.validators import MaxValueValidator +from django.core.validators import MinValueValidator from django.contrib.auth.models import Group -from django.contrib.auth.models import Permission -from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.fields import DateTimeRangeField from django.db import models from django.urls import reverse_lazy from django_prometheus.models import ExportModelOperationsMixin -from camps.models import Permission as CampPermission +from phonebook.models import DectRegistration from utils.models import CampRelatedModel from utils.models import CreatedUpdatedModel from utils.models import UUIDModel @@ -211,6 +211,22 @@ class Team(ExportModelOperationsMixin("team"), CampRelatedModel): public_signal_channel_link = models.URLField(null=True, blank=True, default="") private_signal_channel_link = models.URLField(null=True, blank=True, default="") + public_phone_number = models.CharField( + max_length=14, # Allow for "+00 1234567890" + blank=True, + null=True, + help_text="The public phonenumber for this team.", + ) + + public_dect_number = models.ForeignKey( + DectRegistration, + on_delete=models.PROTECT, + blank=True, + null=True, + related_name="public_team_numbers", + help_text="The public DECT for this team.", + ) + shifts_enabled = models.BooleanField( default=False, help_text="Does this team have shifts? This enables defining shifts for this team.", diff --git a/src/teams/templates/team_base.html b/src/teams/templates/team_base.html index 557eac6d0..9cf084716 100644 --- a/src/teams/templates/team_base.html +++ b/src/teams/templates/team_base.html @@ -22,6 +22,14 @@

{{ team.name }} Team

+ {% if is_team_infopager %} + + {% endif %} + - - - {% if request.user in team.members.all %} + {% if request.user in team.leads.all %} {% endif %} - {% if is_team_infopager %} + {% if request.user in team.leads.all %} {% endif %} - {% if request.user in team.approved_members %} + {% if request.user in team.members.all %} {% endif %} - {% if request.user in team.leads.all %} + + + {% if request.user in team.approved_members %} {% endif %} @@ -78,10 +86,6 @@

{{ team.name }} Team

{% if request.user in team.members.all %}

Your membership status: {% membershipstatus user team %}

- {% if request.user in team.leads.all %} - Manage Team - {% endif %} - {% else %} {% if team.needs_members %} This team is looking for members! Join Team diff --git a/src/teams/templates/team_general.html b/src/teams/templates/team_general.html index 1a08b4efd..2c382d5fc 100644 --- a/src/teams/templates/team_general.html +++ b/src/teams/templates/team_general.html @@ -50,6 +50,20 @@
Signal

The {{ team.name }} Team does not have a public Signal Group.

{% endif %} +
Phone
+ {% if team.public_phone_number %} +

The {{ team.name }} Team public phone number {{ team.public_phone_number|urlize }}

+ {% else %} +

The {{ team.name }} Team does not have a public phone number.

+ {% endif %} + +
DECT
+ {% if team.public_dect_number %} +

The {{ team.name }} Team public DECT number is {{ team.public_dect_number.number }}

+ {% else %} +

The {{ team.name }} Team does not have a public DECT number.

+ {% endif %} + {% if request.user in team.approved_members.all and team.private_signal_channel_link %}

The {{ team.name }} Team private Signal group is {{ team.private_signal_channel_link|urlize }}

{% endif %} diff --git a/src/teams/templates/team_guide.html b/src/teams/templates/team_guide.html index a5640816b..2fdf891a0 100644 --- a/src/teams/templates/team_guide.html +++ b/src/teams/templates/team_guide.html @@ -9,19 +9,21 @@
-
- - Print - - {% if request.user in team.leads.all %} - Edit - {% endif %} +
+

+ Guide / Howto for {{ team.name }} +

+
+ + Print + + {% if request.user in team.leads.all %} + Edit + {% endif %} +
-

- Guide / Howto for {{ team.name }} -

diff --git a/src/teams/templates/team_list.html b/src/teams/templates/team_list.html index d16e8f8d8..5eda9bccd 100644 --- a/src/teams/templates/team_list.html +++ b/src/teams/templates/team_list.html @@ -99,7 +99,7 @@

Your teams

 Details {% if request.user in team.leads.all %} -  Manage +  Settings {% endif %} {% if request.user in team.members.all %}  Leave diff --git a/src/teams/templates/team_manage.html b/src/teams/templates/team_settings.html similarity index 81% rename from src/teams/templates/team_manage.html rename to src/teams/templates/team_settings.html index e4d1d0f3a..b163ba768 100644 --- a/src/teams/templates/team_manage.html +++ b/src/teams/templates/team_settings.html @@ -6,12 +6,12 @@ {% endblock extra_head %} {% block title %} - Manage Team: {{ team.name }} | {{ block.super }} + Settings | {{ block.super }} {% endblock %} {% block team_content %}
-

Manage {{ team.name }} Team

+

Settings

@@ -19,7 +19,7 @@ {% bootstrap_form form %} - + Cancel 
diff --git a/src/teams/tests/test_base_views.py b/src/teams/tests/test_base_views.py index 04fa88ad3..89847a0f2 100644 --- a/src/teams/tests/test_base_views.py +++ b/src/teams/tests/test_base_views.py @@ -55,11 +55,11 @@ def test_team_list_view(self) -> None: response = self.client.get(path=url) assert response.status_code == 200 - def test_team_manage_view(self) -> None: - """Test the team manage view.""" + def test_team_settings_view(self) -> None: + """Test the team settings view.""" self.client.force_login(self.users[4]) url = reverse( - "teams:manage", + "teams:settings", kwargs={ "team_slug": self.teams["noc"].slug, "camp_slug": self.camp.slug, diff --git a/src/teams/urls.py b/src/teams/urls.py index 0d6f50a84..36645da53 100644 --- a/src/teams/urls.py +++ b/src/teams/urls.py @@ -7,7 +7,7 @@ from teams.views.base import FixIrcAclView from teams.views.base import TeamGeneralView from teams.views.base import TeamListView -from teams.views.base import TeamManageView +from teams.views.base import TeamSettingsView from teams.views.guide import TeamGuidePrintView from teams.views.guide import TeamGuideView from teams.views.info import InfoCategoriesListView @@ -46,7 +46,7 @@ path("", TeamGeneralView.as_view(), name="general"), path("join/", TeamJoinView.as_view(), name="join"), path("leave/", TeamLeaveView.as_view(), name="leave"), - path("manage/", TeamManageView.as_view(), name="manage"), + path("settings/", TeamSettingsView.as_view(), name="settings"), path("guide/", TeamGuideView.as_view(), name="guide"), path("guide/print/", TeamGuidePrintView.as_view(), name="guide_print"), path("fix_irc_acl/", FixIrcAclView.as_view(), name="fix_irc_acl"), diff --git a/src/teams/views/base.py b/src/teams/views/base.py index 3d2b3d375..c62f84988 100644 --- a/src/teams/views/base.py +++ b/src/teams/views/base.py @@ -15,6 +15,7 @@ from django.views.generic.edit import UpdateView from camps.mixins import CampViewMixin +from phonebook.models import DectRegistration from teams.models import Team from teams.models import TeamMember from utils.mixins import IsTeamPermContextMixin @@ -76,11 +77,11 @@ def get_context_data(self, **kwargs) -> dict: return context -class TeamManageView(CampViewMixin, EnsureTeamLeadMixin, IsTeamPermContextMixin, UpdateView): +class TeamSettingsView(CampViewMixin, EnsureTeamLeadMixin, IsTeamPermContextMixin, UpdateView): """View for mananaging team members.""" model = Team - template_name = "team_manage.html" + template_name = "team_settings.html" fields = ( "description", "needs_members", @@ -92,22 +93,35 @@ class TeamManageView(CampViewMixin, EnsureTeamLeadMixin, IsTeamPermContextMixin, "private_irc_channel_managed", "public_signal_channel_link", "private_signal_channel_link", + "public_phone_number", + "public_dect_number", "guide", ) slug_url_kwarg = "team_slug" + active_menu = "settings" def get_form(self, *args, **kwargs) -> Form: """Method for updating form widgets.""" form = super().get_form(*args, **kwargs) form.fields["guide"].widget = MarkdownWidget() + + dect_filter = DectRegistration.objects.filter( + camp=self.camp, + user__in=self.object.members.all() + ) + form.fields["public_dect_number"].queryset = dect_filter.filter( + publish_in_phonebook=True + ) + return form def get_success_url(self) -> str: """Method for returning the success url.""" - return reverse_lazy( - "teams:general", - kwargs={"camp_slug": self.camp.slug, "team_slug": self.get_object().slug}, - ) + kwargs = { + "camp_slug": self.camp.slug, + "team_slug": self.get_object().slug + } + return reverse_lazy("teams:general", kwargs=kwargs) def form_valid(self, form: Form) -> HttpResponseRedirect: """Method for sending success message if form is valid."""