-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add anonymous calendar links with user tokens #36
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# Generated by Django 4.0.9 on 2024-07-06 17:48 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('accounts', '0001_initial'), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name='user', | ||
name='ical_token', | ||
field=models.CharField(max_length=64, null=True, unique=True, verbose_name='iCalendar Token'), | ||
), | ||
migrations.AddIndex( | ||
model_name='user', | ||
index=models.Index(fields=['ical_token'], name='user_ical_token_idx'), | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
from django.urls import reverse | ||
from django.utils.translation import gettext_lazy as _ | ||
from phonenumber_field.modelfields import PhoneNumberField | ||
from shiftings import local_settings | ||
|
||
if TYPE_CHECKING: | ||
from shiftings.events.models import Event | ||
|
@@ -31,10 +32,14 @@ def display(self) -> str: | |
class User(BaseUser): | ||
display_name = models.CharField(max_length=150, verbose_name=_('Display Name'), null=True, blank=True) | ||
phone_number = PhoneNumberField(verbose_name=_('Telephone Number'), blank=True, null=True) | ||
ical_token = models.CharField(max_length=64, verbose_name=_('iCalendar Token'), null=True, unique=True) | ||
|
||
class Meta: | ||
default_permissions = () | ||
ordering = ['username'] | ||
indexes = [ | ||
models.Index(fields=['ical_token'], name='user_ical_token_idx') | ||
] | ||
|
||
def __str__(self): | ||
return self.display | ||
|
@@ -65,5 +70,17 @@ def shift_count(self) -> int: | |
total += Shift.objects.filter(participants__user=claimed_user).count() | ||
return total | ||
|
||
@property | ||
def all_shifts_url(self): | ||
git if self.ical_token is None: | ||
return None | ||
return local_settings.SITE + '/user/calendar?token=' + self.ical_token | ||
|
||
@property | ||
def my_shifts_url(self): | ||
if self.ical_token is None: | ||
return None | ||
return local_settings.SITE + '/user/participation_calendar?token=' + self.ical_token | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. s.o. |
||
|
||
def get_absolute_url(self): | ||
return reverse('user_profile') |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,19 @@ <h4 class="text-danger">Your data cannot be restored.</h4> | |
</form> | ||
</div> | ||
{% endsimpledisplaymodal %} | ||
{% simpledisplaymodal 'resetCalendarToken' _('Reset Calendar URL') %} | ||
<div> | ||
{% trans "Do you want to reset your personal calendar access token? All previous tokens will stop working." %} | ||
</div> | ||
<div class="mt-3"> | ||
<form action="{% url "user_ical_token_reset" %}" method="post"> | ||
{% csrf_token %} | ||
<button type="submit" class="btn btn-info float-end" title="{% trans "Reset iCalendar Link Token" %}"> | ||
{% trans "Reset Token" %} <i class="fa-solid fa-repeat"></i> | ||
</button> | ||
</form> | ||
</div> | ||
{% endsimpledisplaymodal %} | ||
{% endblock %} | ||
{% block left %} | ||
<div class="sticky-top"> | ||
|
@@ -115,6 +128,31 @@ <h5>{% trans "Organizations" %}</h5> | |
</div> | ||
</div> | ||
</div> | ||
<div class="card bg-dark mt-2"> | ||
<div class="card-header center-items justify-content-between"> | ||
<h4>{% trans "Calendar URL" %}</h4> | ||
<button class="btn btn-info" title="{% trans "Reset iCalendar Link Token" %}" data-bs-toggle="modal" | ||
data-bs-target="#resetCalendarToken"> | ||
{% trans "Reset Token" %} <i class="fa-solid fa-repeat"></i> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ist das ein Reset? |
||
</button> | ||
</div> | ||
<div class="card-body pt-0"> | ||
{% if user.ical_token is None %} | ||
<div class="text-bg-dark text-center text-secondary p-2">{% trans "(Re-)Generate your personal Token in order to use persistent iCalendar links." %}</div> | ||
{% else %} | ||
<span class="input-group mb-2"> | ||
<label for="user_all_shifts_url" class="input-group-text">{% trans "Overview" %}</label> | ||
<input id="user_all_shifts_url" class="form-control" value="{{ user.all_shifts_url }}" /> | ||
{% include 'widgets/copy_input.html' with input_selector='#user_all_shifts_url' %} | ||
</span> | ||
<span class="input-group mb-2"> | ||
<label for="user_my_shifts_url" class="input-group-text">{% trans "My Shifts" %}</label> | ||
<input id="user_my_shifts_url" class="form-control" value="{{ user.my_shifts_url }}" /> | ||
{% include 'widgets/copy_input.html' with input_selector='#user_all_shifts_url' %} | ||
</span> | ||
{% endif %} | ||
</div> | ||
</div> | ||
{% endblock %} | ||
|
||
{% block right %} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,7 @@ | |
from shiftings.accounts.views.auth import UserLoginView, UserLogoutView, UserReLoginView | ||
from shiftings.accounts.views.password import PasswordResetConfirmView, PasswordResetView | ||
from shiftings.accounts.views.user import (ConfirmEMailView, UserDeleteSelfView, UserEditView, UserProfileView, | ||
UserRegisterView) | ||
UserRegisterView, UserRegenerateCalendarTokenView) | ||
from shiftings.cal.feed.user import OwnShiftsFeed, UserFeed | ||
from shiftings.utils.converters import AlphaNumericConverter | ||
|
||
|
@@ -30,6 +30,7 @@ | |
name='password_reset_success'), | ||
path('calendar/', UserFeed(), name='user_calendar'), | ||
path('participation_calendar/', OwnShiftsFeed(), name='user_participation_calendar'), | ||
path('ical_token_reset/', UserRegenerateCalendarTokenView.as_view(), name='user_ical_token_reset') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ich fände rengenerate oder renew da besser da es ja kein reset ist |
||
] | ||
if settings.FEATURES.get('registration', False): | ||
urlpatterns.append(path('register/', UserRegisterView.as_view(), name='register')) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,9 +11,12 @@ | |
|
||
class UserFeed(ShiftFeed[User]): | ||
def get_object(self, request: HttpRequest, *args: Any, **kwargs: Any) -> Optional[User]: | ||
if not request.user.is_authenticated: | ||
raise Http403() | ||
return request.user | ||
if request.user.is_authenticated: | ||
return request.user | ||
|
||
# noinspection all | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sowas würde ich eher nicht verwenden meistens hat es einen Grund wenn die inspection meckert |
||
user = User.objects.get(ical_token=request.GET.get('token', '')) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Das reicht nicht ganz aus, vor allem für SSO-User müsste man das einmal im Monat checken ob der die Gruppen überhaupt noch sehen darf und dazu bräuchte man nen SSO Login. |
||
return user | ||
|
||
def file_name(self, obj: User) -> str: | ||
return f'{obj.display.lower().replace(" ", "_")}_shifts.ics' | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<button onclick=" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Format |
||
var x = document.querySelector('{{ input_selector }}'); | ||
x.select(); | ||
x.setSelectionRange(0, 99999); | ||
navigator.clipboard.writeText(x.value);" class="btn btn-secondary"> | ||
{% trans "Copy" %} <i class="fa-solid fa-clipboard"></i></button> | ||
</span> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was tut das?
Einfach Dinge aus local_settings importieren bringt nix das gibts bei dir aber das wars
Mal abgesehen davon das SITE vom Request abhängt
Ein fstring ist mMn ebenfalls wesentlich lesbarer:
f'{SITE}/user../{self.ical_token}'