Access url for interventions
* adds access_token as new attribute * adds generating of access_token * adds new form * adds two new routes for sharing an intervention * adds translation * adds render_submit to BaseModalForm which triggers rendering the modal footer
This commit is contained in:
@@ -15,7 +15,7 @@ from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from intervention.models import Intervention
|
||||
from konova.forms import BaseForm
|
||||
from konova.forms import BaseForm, BaseModalForm
|
||||
from konova.models import Document
|
||||
from konova.settings import DEFAULT_LAT, DEFAULT_LON, DEFAULT_ZOOM
|
||||
from organisation.models import Organisation
|
||||
@@ -220,6 +220,42 @@ class OpenInterventionForm(EditInterventionForm):
|
||||
self.disable_form_field(field)
|
||||
|
||||
|
||||
class ShareInterventionForm(BaseModalForm):
|
||||
url = forms.CharField(
|
||||
label=_("Share link"),
|
||||
label_suffix="",
|
||||
disabled=True,
|
||||
required=False,
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
"style": "width:100%",
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.form_title = _("Share")
|
||||
self.form_caption = _("Send this link to users who you want to have writing access on the data.")
|
||||
self.template = "modal/modal_form.html"
|
||||
self.render_submit = False
|
||||
|
||||
# Make sure an access_token is set
|
||||
if self.instance.access_token is None:
|
||||
self.instance.generate_access_token()
|
||||
|
||||
self.share_link = self.request.build_absolute_uri(
|
||||
reverse("intervention:share", args=(self.instance.id, self.instance.access_token,))
|
||||
)
|
||||
self.initialize_form_field(
|
||||
"url",
|
||||
self.share_link
|
||||
)
|
||||
|
||||
def save(self):
|
||||
i = 0
|
||||
|
||||
|
||||
class DummyFilterInput(forms.HiddenInput):
|
||||
""" A dummy input widget
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ from django.utils.timezone import now
|
||||
|
||||
from intervention.settings import INTERVENTION_IDENTIFIER_LENGTH, INTERVENTION_IDENTIFIER_TEMPLATE
|
||||
from konova.models import BaseObject, Geometry, UuidModel
|
||||
from konova.utils import generators
|
||||
from konova.utils.generators import generate_random_string
|
||||
from organisation.models import Organisation
|
||||
from user.models import UserActionLogEntry
|
||||
@@ -92,6 +93,12 @@ class Intervention(BaseObject):
|
||||
|
||||
# Users having access on this object
|
||||
users = models.ManyToManyField(User)
|
||||
access_token = models.CharField(
|
||||
max_length=255,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Used for sharing access",
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return "{} ({})".format(self.identifier, self.title)
|
||||
@@ -139,6 +146,40 @@ class Intervention(BaseObject):
|
||||
_str = "{}{}{}".format(curr_month, curr_year, rand_str)
|
||||
return INTERVENTION_IDENTIFIER_TEMPLATE.format(_str)
|
||||
|
||||
def generate_access_token(self, make_unique: bool = False, rec_depth: int = 5):
|
||||
""" Creates a new access token for the intervention
|
||||
|
||||
Tokens are not used for identification of a table row. The share logic checks the intervention id as well
|
||||
as the given token. Therefore two different interventions can hold the same access_token without problems.
|
||||
For (possible) future changes to the share logic, the make_unique parameter may be used for checking whether
|
||||
the access_token is already used in any intervention. If so, tokens will be generated as long as a free token
|
||||
can be found.
|
||||
|
||||
Args:
|
||||
make_unique (bool): Perform check on uniqueness over all intervention entries
|
||||
rec_depth (int): How many tries for generating a free random token (only if make_unique)
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# Make sure we won't end up in an infinite loop of trying to generate access_tokens
|
||||
rec_depth = rec_depth - 1
|
||||
if rec_depth < 0 and make_unique:
|
||||
raise RuntimeError(
|
||||
"Access token generating for {} does not seem to find a free random token! Aborted!".format(self.id)
|
||||
)
|
||||
|
||||
# Create random token
|
||||
token = generators.generate_random_string(15)
|
||||
token_used_in = Intervention.objects.filter(access_token=token)
|
||||
# Make sure the token is not used anywhere as access_token, yet.
|
||||
# Make use of QuerySet lazy method for checking if it exists or not.
|
||||
if token_used_in and make_unique:
|
||||
self.generate_access_token(make_unique, rec_depth)
|
||||
else:
|
||||
self.access_token = token
|
||||
self.save()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.identifier is None or len(self.identifier) == 0:
|
||||
# Create new identifier
|
||||
|
||||
@@ -24,11 +24,9 @@
|
||||
</button>
|
||||
</a>
|
||||
{% if has_access %}
|
||||
<a href="{% url 'home' %}" class="mr-2">
|
||||
<button class="btn btn-default" title="{% trans 'Share' %}">
|
||||
{% fa5_icon 'share-alt' %}
|
||||
</button>
|
||||
</a>
|
||||
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'intervention:share-create' intervention.id %}">
|
||||
{% fa5_icon 'share-alt' %}
|
||||
</button>
|
||||
<a href="{% url 'home' %}" class="mr-2">
|
||||
<button class="btn btn-default" title="{% trans 'Run check' %}">
|
||||
{% fa5_icon 'star' %}
|
||||
|
||||
@@ -7,7 +7,8 @@ Created on: 30.11.20
|
||||
"""
|
||||
from django.urls import path
|
||||
|
||||
from intervention.views import index_view, new_view, open_view, edit_view, remove_view, new_document_view
|
||||
from intervention.views import index_view, new_view, open_view, edit_view, remove_view, new_document_view, share_view, \
|
||||
create_share_view
|
||||
|
||||
app_name = "intervention"
|
||||
urlpatterns = [
|
||||
@@ -17,4 +18,6 @@ urlpatterns = [
|
||||
path('<id>', open_view, name='open'),
|
||||
path('<id>/edit', edit_view, name='edit'),
|
||||
path('<id>/remove', remove_view, name='remove'),
|
||||
path('<id>/share/<token>', share_view, name='share'),
|
||||
path('<id>/share', create_share_view, name='share-create'),
|
||||
]
|
||||
@@ -4,7 +4,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from django.http import HttpRequest
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
|
||||
from intervention.forms import NewInterventionForm, EditInterventionForm
|
||||
from intervention.forms import NewInterventionForm, EditInterventionForm, ShareInterventionForm
|
||||
from intervention.models import Intervention
|
||||
from intervention.tables import InterventionTable
|
||||
from konova.contexts import BaseContext
|
||||
@@ -198,3 +198,68 @@ def remove_view(request: HttpRequest, id: str):
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, template, context)
|
||||
|
||||
|
||||
@login_required
|
||||
def share_view(request: HttpRequest, id: str, token: str):
|
||||
""" Performs sharing of an intervention
|
||||
|
||||
If token given in url is not valid, the user will be redirected to the dashboard
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
id (str): Intervention's id
|
||||
token (str): Access token for intervention
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
user = request.user
|
||||
intervention = get_object_or_404(Intervention, id=id)
|
||||
# Check tokens
|
||||
if intervention.access_token == token:
|
||||
# Send different messages in case user has already been added to list of sharing users
|
||||
if intervention.has_access(user):
|
||||
messages.info(
|
||||
request,
|
||||
_("{} has already been shared with you").format(intervention.identifier)
|
||||
)
|
||||
else:
|
||||
messages.success(
|
||||
request,
|
||||
_("{} has been shared with you").format(intervention.identifier)
|
||||
)
|
||||
intervention.users.add(user)
|
||||
return redirect("intervention:open", id=id)
|
||||
else:
|
||||
messages.error(
|
||||
request,
|
||||
_("Share link invalid"),
|
||||
extra_tags="danger",
|
||||
)
|
||||
return redirect("home")
|
||||
|
||||
|
||||
@login_required
|
||||
def create_share_view(request: HttpRequest, id: str):
|
||||
""" Renders sharing form for an intervention
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
id (str): Intervention's id
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
user = request.user
|
||||
intervention = get_object_or_404(Intervention, id=id)
|
||||
form = ShareInterventionForm(request.POST or None, instance=intervention, request=request)
|
||||
if request.method != "GET":
|
||||
raise NotImplementedError
|
||||
context = {
|
||||
"form": form,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, form.template, context)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user