Compare commits

..

10 Commits

Author SHA1 Message Date
22cddb9902 Merge pull request '# Fix for #500' (#501) from 500_Geometry_conflicts_still_visible into master
Reviewed-on: #501
2025-11-19 13:16:30 +01:00
c986bd0b92 # Fix for #500
* fixes bug where de-facto deleted compensations (because of deleted intervention) would still show up as geometry conflicts on other entries
2025-11-19 13:16:09 +01:00
2c60d86177 Merge pull request '# Update netgis map client' (#498) from netgis_map_client into master
Reviewed-on: #498
2025-11-07 14:11:57 +01:00
b7792ececc # Update netgis map client
* updates netgis map client (bugfix for https://github.com/sebastianpauli/netgis-client/issues/43#issuecomment-3446898016)
2025-11-07 14:11:15 +01:00
210f3fcafa Merge pull request '# HOTFIX' (#495) from update_map_config into master
Reviewed-on: #495
2025-10-23 16:12:45 +02:00
e7d67560f2 # HOTFIX
* fixes bug on netgis map client where importing a geometry would result in an error message
* THIS IS JUST A WORKAROUND AND HAS TO BE REPLACED BY A PROPER FIX FROM THE DEVS ASAP
2025-10-23 16:12:05 +02:00
b5e991fb95 Merge pull request '# Map layer update' (#494) from update_map_config into master
Reviewed-on: #494
2025-10-22 16:02:02 +02:00
d3a555d406 # Map layer update
* updates rp_dop layer from deprecated to newest
2025-10-22 16:00:35 +02:00
9a374f50de Merge pull request '# CSS bugfix' (#492) from fix_css_map_menu_bar_overlap into master
Reviewed-on: #492
2025-10-21 15:59:37 +02:00
ce63dd30bc # CSS bugfix
* fixes a rendering bug where the menu bar of the map client overlapped modal forms
2025-10-21 15:57:25 +02:00
36 changed files with 1074 additions and 1126 deletions

0
codelist/views.py Normal file
View File

View File

@@ -10,24 +10,24 @@ from django.urls import path
from compensation.views.compensation.document import EditCompensationDocumentView, NewCompensationDocumentView, \
GetCompensationDocumentView, RemoveCompensationDocumentView
from compensation.views.compensation.resubmission import CompensationResubmissionView
from compensation.views.compensation.report import CompensationReportView
from compensation.views.compensation.report import report_view
from compensation.views.compensation.deadline import NewCompensationDeadlineView, EditCompensationDeadlineView, \
RemoveCompensationDeadlineView
from compensation.views.compensation.action import NewCompensationActionView, EditCompensationActionView, \
RemoveCompensationActionView
from compensation.views.compensation.state import NewCompensationStateView, EditCompensationStateView, \
RemoveCompensationStateView
from compensation.views.compensation.compensation import new_view, edit_view, \
remove_view, CompensationIndexView, CompensationIdentifierGeneratorView, CompensationDetailView
from compensation.views.compensation.compensation import index_view, new_view, new_id_view, detail_view, edit_view, \
remove_view
from compensation.views.compensation.log import CompensationLogView
urlpatterns = [
# Main compensation
path("", CompensationIndexView.as_view(), name="index"),
path('new/id', CompensationIdentifierGeneratorView.as_view(), name='new-id'),
path("", index_view, name="index"),
path('new/id', new_id_view, name='new-id'),
path('new/<intervention_id>', new_view, name='new'),
path('new', new_view, name='new'),
path('<id>', CompensationDetailView.as_view(), name='detail'),
path('<id>', detail_view, name='detail'),
path('<id>/log', CompensationLogView.as_view(), name='log'),
path('<id>/edit', edit_view, name='edit'),
path('<id>/remove', remove_view, name='remove'),
@@ -43,7 +43,7 @@ urlpatterns = [
path('<id>/deadline/new', NewCompensationDeadlineView.as_view(), name="new-deadline"),
path('<id>/deadline/<deadline_id>/edit', EditCompensationDeadlineView.as_view(), name='deadline-edit'),
path('<id>/deadline/<deadline_id>/remove', RemoveCompensationDeadlineView.as_view(), name='deadline-remove'),
path('<id>/report', CompensationReportView.as_view(), name='report'),
path('<id>/report', report_view, name='report'),
path('<id>/resub', CompensationResubmissionView.as_view(), name='resubmission-create'),
# Documents

View File

@@ -8,11 +8,11 @@ Created on: 24.08.21
from django.urls import path
from compensation.autocomplete.eco_account import EcoAccountAutocomplete
from compensation.views.eco_account.eco_account import new_view, edit_view, remove_view, \
EcoAccountIndexView, EcoAccountIdentifierGeneratorView, EcoAccountDetailView
from compensation.views.eco_account.eco_account import index_view, new_view, new_id_view, edit_view, remove_view, \
detail_view
from compensation.views.eco_account.log import EcoAccountLogView
from compensation.views.eco_account.record import EcoAccountRecordView
from compensation.views.eco_account.report import EcoAccountReportView
from compensation.views.eco_account.report import report_view
from compensation.views.eco_account.resubmission import EcoAccountResubmissionView
from compensation.views.eco_account.state import NewEcoAccountStateView, EditEcoAccountStateView, \
RemoveEcoAccountStateView
@@ -28,13 +28,13 @@ from compensation.views.eco_account.deduction import NewEcoAccountDeductionView,
app_name = "acc"
urlpatterns = [
path("", EcoAccountIndexView.as_view(), name="index"),
path("", index_view, name="index"),
path('new/', new_view, name='new'),
path('new/id', EcoAccountIdentifierGeneratorView.as_view(), name='new-id'),
path('<id>', EcoAccountDetailView.as_view(), name='detail'),
path('new/id', new_id_view, name='new-id'),
path('<id>', detail_view, name='detail'),
path('<id>/log', EcoAccountLogView.as_view(), name='log'),
path('<id>/record', EcoAccountRecordView.as_view(), name='record'),
path('<id>/report', EcoAccountReportView.as_view(), name='report'),
path('<id>/report', report_view, name='report'),
path('<id>/edit', edit_view, name='edit'),
path('<id>/remove', remove_view, name='remove'),
path('<id>/resub', EcoAccountResubmissionView.as_view(), name='resubmission-create'),

View File

@@ -7,8 +7,8 @@ Created on: 19.08.22
"""
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Sum
from django.http import HttpRequest, JsonResponse
from django.shortcuts import get_object_or_404, render, redirect
from django.urls import reverse
@@ -19,29 +19,46 @@ from compensation.models import Compensation
from compensation.tables.compensation import CompensationTable
from intervention.models import Intervention
from konova.contexts import BaseContext
from konova.decorators import shared_access_required, default_group_required, login_required_modal
from konova.decorators import shared_access_required, default_group_required, any_group_check, login_required_modal, \
uuid_required
from konova.forms import SimpleGeomForm
from konova.forms.modals import RemoveModalForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE, DATA_CHECKED_PREVIOUSLY_TEMPLATE, \
RECORDED_BLOCKS_EDIT, CHECK_STATE_RESET, FORM_INVALID, PARAMS_INVALID, IDENTIFIER_REPLACED, \
COMPENSATION_ADDED_TEMPLATE, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView
from konova.views.detail import BaseDetailView
COMPENSATION_ADDED_TEMPLATE, DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
class CompensationIndexView(LoginRequiredMixin, BaseIndexView):
_TAB_TITLE = _("Compensations - Overview")
_INDEX_TABLE_CLS = CompensationTable
@login_required
@any_group_check
def index_view(request: HttpRequest):
"""
Renders the index view for compensation
def _get_queryset(self):
qs = Compensation.objects.filter(
deleted=None, # only show those which are not deleted individually
intervention__deleted=None, # and don't show the ones whose intervention has been deleted
).order_by(
"-modified__timestamp"
)
return qs
Args:
request (HttpRequest): The incoming request
Returns:
A rendered view
"""
template = "generic_index.html"
compensations = Compensation.objects.filter(
deleted=None, # only show those which are not deleted individually
intervention__deleted=None, # and don't show the ones whose intervention has been deleted
).order_by(
"-modified__timestamp"
)
table = CompensationTable(
request=request,
queryset=compensations
)
context = {
"table": table,
TAB_TITLE_IDENTIFIER: _("Compensations - Overview"),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required
@@ -114,9 +131,23 @@ def new_view(request: HttpRequest, intervention_id: str = None):
return render(request, template, context)
class CompensationIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
_MODEL_CLS = Compensation
_REDIRECT_URL = "compensation:index"
@login_required
@default_group_required
def new_id_view(request: HttpRequest):
""" JSON endpoint
Provides fetching of free identifiers for e.g. AJAX calls
"""
tmp = Compensation()
identifier = tmp.generate_new_identifier()
while Compensation.objects.filter(identifier=identifier).exists():
identifier = tmp.generate_new_identifier()
return JsonResponse(
data={
"gen_data": identifier
}
)
@login_required
@@ -184,71 +215,82 @@ def edit_view(request: HttpRequest, id: str):
return render(request, template, context)
class CompensationDetailView(BaseDetailView):
_MODEL_CLS = Compensation
_TEMPLATE = "compensation/detail/compensation/view.html"
@login_required
@any_group_check
@uuid_required
def detail_view(request: HttpRequest, id: str):
""" Renders a detail view for a compensation
def _get_object(self, id: str):
""" Returns the compensation
Args:
request (HttpRequest): The incoming request
id (str): The compensation's id
Args:
id (str): The compensation's id
Returns:
Returns:
obj (Compensation): The compensation
"""
comp = get_object_or_404(
Compensation.objects.select_related(
"modified",
"created",
"geometry"
),
id=id,
deleted=None,
intervention__deleted=None,
"""
template = "compensation/detail/compensation/view.html"
comp = get_object_or_404(
Compensation.objects.select_related(
"modified",
"created",
"geometry"
),
id=id,
deleted=None,
intervention__deleted=None,
)
geom_form = SimpleGeomForm(instance=comp)
parcels = comp.get_underlying_parcels()
_user = request.user
is_data_shared = comp.intervention.is_shared_with(_user)
# Order states according to surface
before_states = comp.before_states.all().prefetch_related("biotope_type").order_by("-surface")
after_states = comp.after_states.all().prefetch_related("biotope_type").order_by("-surface")
actions = comp.actions.all().prefetch_related("action_type")
# Precalculate logical errors between before- and after-states
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
sum_before_states = comp.get_surface_before_states()
sum_after_states = comp.get_surface_after_states()
diff_states = abs(sum_before_states - sum_after_states)
request = comp.set_status_messages(request)
last_checked = comp.intervention.get_last_checked_action()
last_checked_tooltip = ""
if last_checked:
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(last_checked.get_timestamp_str_formatted(), last_checked.user)
requesting_user_is_only_shared_user = comp.is_only_shared_with(_user)
if requesting_user_is_only_shared_user:
messages.info(
request,
DO_NOT_FORGET_TO_SHARE
)
return comp
def _get_detail_context(self, obj: Compensation):
""" Generate object specific detail context for view
Args:
obj (): The record
Returns:
"""
# Order states according to surface
before_states = obj.before_states.all().prefetch_related("biotope_type").order_by("-surface")
after_states = obj.after_states.all().prefetch_related("biotope_type").order_by("-surface")
actions = obj.actions.all().prefetch_related("action_type")
# Precalculate logical errors between before- and after-states
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
sum_before_states = obj.get_surface_before_states()
sum_after_states = obj.get_surface_after_states()
diff_states = abs(sum_before_states - sum_after_states)
last_checked = obj.intervention.get_last_checked_action()
last_checked_tooltip = ""
if last_checked:
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(
last_checked.get_timestamp_str_formatted(),
last_checked.user
)
context = {
"last_checked": last_checked,
"last_checked_tooltip": last_checked_tooltip,
"actions": actions,
"before_states": before_states,
"after_states": after_states,
"sum_before_states": sum_before_states,
"sum_after_states": sum_after_states,
"diff_states": diff_states,
"has_finished_deadlines": obj.get_finished_deadlines().exists(),
}
return context
context = {
"obj": comp,
"last_checked": last_checked,
"last_checked_tooltip": last_checked_tooltip,
"geom_form": geom_form,
"parcels": parcels,
"is_entry_shared": is_data_shared,
"actions": actions,
"before_states": before_states,
"after_states": after_states,
"sum_before_states": sum_before_states,
"sum_after_states": sum_after_states,
"diff_states": diff_states,
"is_default_member": _user.in_group(DEFAULT_GROUP),
"is_zb_member": _user.in_group(ZB_GROUP),
"is_ets_member": _user.in_group(ETS_GROUP),
"LANIS_LINK": comp.get_LANIS_link(),
TAB_TITLE_IDENTIFIER: f"{comp.identifier} - {comp.title}",
"has_finished_deadlines": comp.get_finished_deadlines().exists(),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required_modal

View File

@@ -5,48 +5,77 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 19.08.22
"""
from django.http import HttpRequest
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from compensation.models import Compensation
from konova.sub_settings.django_settings import BASE_URL
from konova.utils.qrcode import QrCode
from konova.views.report import BaseReportView
from konova.contexts import BaseContext
from konova.decorators import uuid_required
from konova.forms import SimpleGeomForm
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.generators import generate_qr_code
@uuid_required
def report_view(request: HttpRequest, id: str):
""" Renders the public report view
class BaseCompensationReportView(BaseReportView):
def _get_compensation_report_context(self, obj):
# Order states by surface
before_states = obj.before_states.all().order_by("-surface").prefetch_related("biotope_type")
after_states = obj.after_states.all().order_by("-surface").prefetch_related("biotope_type")
actions = obj.actions.all().prefetch_related("action_type")
Args:
request (HttpRequest): The incoming request
id (str): The id of the intervention
return {
"before_states": before_states,
"after_states": after_states,
"actions": actions,
Returns:
"""
# Reuse the compensation report template since compensations are structurally identical
template = "compensation/report/compensation/report.html"
comp = get_object_or_404(Compensation, id=id)
tab_title = _("Report {}").format(comp.identifier)
# If intervention is not recorded (yet or currently) we need to render another template without any data
if not comp.is_ready_for_publish():
template = "report/unavailable.html"
context = {
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, template, context)
# Prepare data for map viewer
geom_form = SimpleGeomForm(
instance=comp
)
parcels = comp.get_underlying_parcels()
class CompensationReportView(BaseCompensationReportView):
_MODEL = Compensation
_TEMPLATE = "compensation/report/compensation/report.html"
qrcode_url = request.build_absolute_uri(reverse("compensation:report", args=(id,)))
qrcode_img = generate_qr_code(qrcode_url, 10)
qrcode_lanis_url = comp.get_LANIS_link()
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
def _get_report_context(self, obj):
report_url = BASE_URL + reverse("compensation:report", args=(obj.id,))
qrcode_report = QrCode(report_url, 10)
qrcode_lanis = QrCode(obj.get_LANIS_link(), 7)
# Order states by surface
before_states = comp.before_states.all().order_by("-surface").prefetch_related("biotope_type")
after_states = comp.after_states.all().order_by("-surface").prefetch_related("biotope_type")
actions = comp.actions.all().prefetch_related("action_type")
report_context = {
"qrcode": {
"img": qrcode_report.get_img(),
"url": qrcode_report.get_content(),
},
"qrcode_lanis": {
"img": qrcode_lanis.get_img(),
"url": qrcode_lanis.get_content(),
},
"is_entry_shared": False, # disables action buttons during rendering
"tables_scrollable": False,
}
report_context.update(self._get_compensation_report_context(obj))
return report_context
context = {
"obj": comp,
"qrcode": {
"img": qrcode_img,
"url": qrcode_url,
},
"qrcode_lanis": {
"img": qrcode_img_lanis,
"url": qrcode_lanis_url,
},
"is_entry_shared": False, # disables action buttons during rendering
"before_states": before_states,
"after_states": after_states,
"geom_form": geom_form,
"parcels": parcels,
"actions": actions,
"tables_scrollable": False,
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, template, context)

View File

@@ -5,28 +5,54 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 19.08.22
"""
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from django.http import Http404
from django.utils.decorators import method_decorator
from compensation.models import EcoAccount
from konova.decorators import default_group_required, login_required_modal
from konova.views.deduction import AbstractNewDeductionView, AbstractEditDeductionView, AbstractRemoveDeductionView
class NewEcoAccountDeductionView(LoginRequiredMixin, AbstractNewDeductionView):
_MODEL = EcoAccount
_REDIRECT_URL = "compensation:acc:detail"
class NewEcoAccountDeductionView(AbstractNewDeductionView):
model = EcoAccount
redirect_url = "compensation:acc:detail"
@method_decorator(login_required_modal)
@method_decorator(login_required)
@method_decorator(default_group_required)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def _custom_check(self, obj):
# New deductions can only be created if the eco account has been recorded
if not obj.recorded:
raise Http404()
class EditEcoAccountDeductionView(LoginRequiredMixin, AbstractEditDeductionView):
_MODEL = EcoAccount
_REDIRECT_URL = "compensation:acc:detail"
class EditEcoAccountDeductionView(AbstractEditDeductionView):
def _custom_check(self, obj):
pass
model = EcoAccount
redirect_url = "compensation:acc:detail"
@method_decorator(login_required_modal)
@method_decorator(login_required)
@method_decorator(default_group_required)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
class RemoveEcoAccountDeductionView(LoginRequiredMixin, AbstractRemoveDeductionView):
_MODEL = EcoAccount
_REDIRECT_URL = "compensation:acc:detail"
class RemoveEcoAccountDeductionView(AbstractRemoveDeductionView):
def _custom_check(self, obj):
pass
model = EcoAccount
redirect_url = "compensation:acc:detail"
@method_decorator(login_required_modal)
@method_decorator(login_required)
@method_decorator(default_group_required)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)

View File

@@ -7,8 +7,8 @@ Created on: 19.08.22
"""
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest
from django.db.models import Sum
from django.http import HttpRequest, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
@@ -17,27 +17,43 @@ from compensation.forms.eco_account import EditEcoAccountForm, NewEcoAccountForm
from compensation.models import EcoAccount
from compensation.tables.eco_account import EcoAccountTable
from konova.contexts import BaseContext
from konova.decorators import shared_access_required, default_group_required, login_required_modal
from konova.decorators import shared_access_required, default_group_required, any_group_check, login_required_modal, \
uuid_required
from konova.forms import SimpleGeomForm
from konova.settings import ETS_GROUP
from konova.settings import ETS_GROUP, DEFAULT_GROUP, ZB_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import CANCEL_ACC_RECORDED_OR_DEDUCTED, RECORDED_BLOCKS_EDIT, FORM_INVALID, \
IDENTIFIER_REPLACED, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView
from konova.views.detail import BaseDetailView
IDENTIFIER_REPLACED, DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
class EcoAccountIndexView(LoginRequiredMixin, BaseIndexView):
_INDEX_TABLE_CLS = EcoAccountTable
_TAB_TITLE = _("Eco-account - Overview")
@login_required
@any_group_check
def index_view(request: HttpRequest):
"""
Renders the index view for eco accounts
def _get_queryset(self):
qs = EcoAccount.objects.filter(
deleted=None,
).order_by(
"-modified__timestamp"
)
return qs
Args:
request (HttpRequest): The incoming request
Returns:
A rendered view
"""
template = "generic_index.html"
eco_accounts = EcoAccount.objects.filter(
deleted=None,
).order_by(
"-modified__timestamp"
)
table = EcoAccountTable(
request=request,
queryset=eco_accounts
)
context = {
"table": table,
TAB_TITLE_IDENTIFIER: _("Eco-account - Overview"),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required
@@ -96,9 +112,23 @@ def new_view(request: HttpRequest):
return render(request, template, context)
class EcoAccountIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
_MODEL_CLS = EcoAccount
_REDIRECT_URL = "compensation:acc:index"
@login_required
@default_group_required
def new_id_view(request: HttpRequest):
""" JSON endpoint
Provides fetching of free identifiers for e.g. AJAX calls
"""
tmp = EcoAccount()
identifier = tmp.generate_new_identifier()
while EcoAccount.objects.filter(identifier=identifier).exists():
identifier = tmp.generate_new_identifier()
return JsonResponse(
data={
"gen_data": identifier
}
)
@login_required
@@ -162,72 +192,86 @@ def edit_view(request: HttpRequest, id: str):
return render(request, template, context)
class EcoAccountDetailView(BaseDetailView):
_MODEL_CLS = EcoAccount
_TEMPLATE = "compensation/detail/eco_account/view.html"
@login_required
@any_group_check
@uuid_required
def detail_view(request: HttpRequest, id: str):
""" Renders a detail view for a compensation
def _get_object(self, id: str):
""" Fetch object for detail view
Args:
request (HttpRequest): The incoming request
id (str): The compensation's id
Args:
id (str): The record's id'
Returns:
Returns:
"""
template = "compensation/detail/eco_account/view.html"
acc = get_object_or_404(
EcoAccount.objects.prefetch_related(
"deadlines",
).select_related(
'geometry',
'responsible',
),
id=id,
deleted=None,
)
geom_form = SimpleGeomForm(instance=acc)
parcels = acc.get_underlying_parcels()
_user = request.user
is_data_shared = acc.is_shared_with(_user)
"""
acc = get_object_or_404(
EcoAccount.objects.prefetch_related(
"deadlines",
).select_related(
'geometry',
'responsible',
),
id=id,
deleted=None,
# Order states according to surface
before_states = acc.before_states.order_by("-surface")
after_states = acc.after_states.order_by("-surface")
# Precalculate logical errors between before- and after-states
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
sum_before_states = acc.get_surface_before_states()
sum_after_states = acc.get_surface_after_states()
diff_states = abs(sum_before_states - sum_after_states)
# Calculate rest of available surface for deductions
available_total = acc.deductable_rest
available_relative = acc.get_deductable_rest_relative()
# Prefetch related data to decrease the amount of db connections
deductions = acc.deductions.filter(
intervention__deleted=None,
)
actions = acc.actions.all()
request = acc.set_status_messages(request)
requesting_user_is_only_shared_user = acc.is_only_shared_with(_user)
if requesting_user_is_only_shared_user:
messages.info(
request,
DO_NOT_FORGET_TO_SHARE
)
return acc
def _get_detail_context(self, obj: EcoAccount):
""" Generate object specific detail context for view
Args:
obj (): The record
Returns:
"""
# Order states according to surface
before_states = obj.before_states.order_by("-surface")
after_states = obj.after_states.order_by("-surface")
# Precalculate logical errors between before- and after-states
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
sum_before_states = obj.get_surface_before_states()
sum_after_states = obj.get_surface_after_states()
diff_states = abs(sum_before_states - sum_after_states)
# Calculate rest of available surface for deductions
available_total = obj.deductable_rest
available_relative = obj.get_deductable_rest_relative()
# Prefetch related data to decrease the amount of db connections
deductions = obj.deductions.filter(
intervention__deleted=None,
)
actions = obj.actions.all()
context = {
"before_states": before_states,
"after_states": after_states,
"sum_before_states": sum_before_states,
"sum_after_states": sum_after_states,
"diff_states": diff_states,
"available": available_relative,
"available_total": available_total,
"deductions": deductions,
"actions": actions,
"has_finished_deadlines": obj.get_finished_deadlines().exists(),
}
return context
context = {
"obj": acc,
"geom_form": geom_form,
"parcels": parcels,
"is_entry_shared": is_data_shared,
"before_states": before_states,
"after_states": after_states,
"sum_before_states": sum_before_states,
"sum_after_states": sum_after_states,
"diff_states": diff_states,
"available": available_relative,
"available_total": available_total,
"is_default_member": _user.in_group(DEFAULT_GROUP),
"is_zb_member": _user.in_group(ZB_GROUP),
"is_ets_member": _user.in_group(ETS_GROUP),
"LANIS_LINK": acc.get_LANIS_link(),
"deductions": deductions,
"actions": actions,
TAB_TITLE_IDENTIFIER: f"{acc.identifier} - {acc.title}",
"has_finished_deadlines": acc.get_finished_deadlines().exists(),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required_modal

View File

@@ -5,41 +5,85 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 19.08.22
"""
from django.http import HttpRequest
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from compensation.models import EcoAccount
from compensation.views.compensation.report import BaseCompensationReportView
from konova.sub_settings.django_settings import BASE_URL
from konova.utils.qrcode import QrCode
from konova.contexts import BaseContext
from konova.decorators import uuid_required
from konova.forms import SimpleGeomForm
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.generators import generate_qr_code
class EcoAccountReportView(BaseCompensationReportView):
_MODEL = EcoAccount
_TEMPLATE = "compensation/report/eco_account/report.html"
@uuid_required
def report_view(request: HttpRequest, id: str):
""" Renders the public report view
def _get_report_context(self, obj):
report_url = BASE_URL + reverse("compensation:acc:report", args=(obj.id,))
qrcode_report = QrCode(report_url, 10)
qrcode_lanis = QrCode(obj.get_LANIS_link(), 7)
Args:
request (HttpRequest): The incoming request
id (str): The id of the intervention
# Reduce amount of db fetched data to the bare minimum we need in the template (deduction's intervention id and identifier)
deductions = obj.deductions.all() \
.distinct("intervention") \
.select_related("intervention") \
.values_list("intervention__id", "intervention__identifier", "intervention__title", named=True)
Returns:
report_context = {
"qrcode": {
"img": qrcode_report.get_img(),
"url": qrcode_report.get_content(),
},
"qrcode_lanis": {
"img": qrcode_lanis.get_img(),
"url": qrcode_lanis.get_content(),
},
"is_entry_shared": False, # disables action buttons during rendering
"deductions": deductions,
"tables_scrollable": False,
"""
# Reuse the compensation report template since EcoAccounts are structurally identical
template = "compensation/report/eco_account/report.html"
acc = get_object_or_404(EcoAccount, id=id)
tab_title = _("Report {}").format(acc.identifier)
# If intervention is not recorded (yet or currently) we need to render another template without any data
if not acc.is_ready_for_publish():
template = "report/unavailable.html"
context = {
TAB_TITLE_IDENTIFIER: tab_title,
}
report_context.update(self._get_compensation_report_context(obj))
return report_context
context = BaseContext(request, context).context
return render(request, template, context)
# Prepare data for map viewer
geom_form = SimpleGeomForm(
instance=acc
)
parcels = acc.get_underlying_parcels()
qrcode_url = request.build_absolute_uri(reverse("compensation:acc:report", args=(id,)))
qrcode_img = generate_qr_code(qrcode_url, 10)
qrcode_lanis_url = acc.get_LANIS_link()
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
# Order states by surface
before_states = acc.before_states.all().order_by("-surface").select_related("biotope_type__parent")
after_states = acc.after_states.all().order_by("-surface").select_related("biotope_type__parent")
actions = acc.actions.all().prefetch_related("action_type__parent")
# Reduce amount of db fetched data to the bare minimum we need in the template (deduction's intervention id and identifier)
deductions = acc.deductions.all()\
.distinct("intervention")\
.select_related("intervention")\
.values_list("intervention__id", "intervention__identifier", "intervention__title", named=True)
context = {
"obj": acc,
"qrcode": {
"img": qrcode_img,
"url": qrcode_url,
},
"qrcode_lanis": {
"img": qrcode_img_lanis,
"url": qrcode_lanis_url,
},
"is_entry_shared": False, # disables action buttons during rendering
"before_states": before_states,
"after_states": after_states,
"geom_form": geom_form,
"parcels": parcels,
"actions": actions,
"deductions": deductions,
"tables_scrollable": False,
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, template, context)

View File

@@ -10,26 +10,25 @@ from django.urls import path
from ema.views.action import NewEmaActionView, EditEmaActionView, RemoveEmaActionView
from ema.views.deadline import NewEmaDeadlineView, EditEmaDeadlineView, RemoveEmaDeadlineView
from ema.views.document import NewEmaDocumentView, EditEmaDocumentView, RemoveEmaDocumentView, GetEmaDocumentView
from ema.views.ema import new_view, edit_view, remove_view, EmaIndexView, \
EmaIdentifierGeneratorView, EmaDetailView
from ema.views.ema import index_view, new_view, new_id_view, detail_view, edit_view, remove_view
from ema.views.log import EmaLogView
from ema.views.record import EmaRecordView
from ema.views.report import EmaReportView
from ema.views.report import report_view
from ema.views.resubmission import EmaResubmissionView
from ema.views.share import EmaShareFormView, EmaShareByTokenView
from ema.views.state import NewEmaStateView, EditEmaStateView, RemoveEmaStateView
app_name = "ema"
urlpatterns = [
path("", EmaIndexView.as_view(), name="index"),
path("", index_view, name="index"),
path("new/", new_view, name="new"),
path("new/id", EmaIdentifierGeneratorView.as_view(), name="new-id"),
path("<id>", EmaDetailView.as_view(), name="detail"),
path("new/id", new_id_view, name="new-id"),
path("<id>", detail_view, name="detail"),
path('<id>/log', EmaLogView.as_view(), name='log'),
path('<id>/edit', edit_view, name='edit'),
path('<id>/remove', remove_view, name='remove'),
path('<id>/record', EmaRecordView.as_view(), name='record'),
path('<id>/report', EmaReportView.as_view(), name='report'),
path('<id>/report', report_view, name='report'),
path('<id>/resub', EmaResubmissionView.as_view(), name='resubmission-create'),
path('<id>/state/new', NewEmaStateView.as_view(), name='new-state'),

View File

@@ -7,8 +7,8 @@ Created on: 19.08.22
"""
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest
from django.db.models import Sum
from django.http import HttpRequest, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
@@ -17,27 +17,43 @@ from ema.forms import NewEmaForm, EditEmaForm
from ema.models import Ema
from ema.tables import EmaTable
from konova.contexts import BaseContext
from konova.decorators import shared_access_required, conservation_office_group_required, login_required_modal
from konova.decorators import shared_access_required, conservation_office_group_required, login_required_modal, \
uuid_required
from konova.forms import SimpleGeomForm
from konova.forms.modals import RemoveModalForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import RECORDED_BLOCKS_EDIT, IDENTIFIER_REPLACED, FORM_INVALID, \
GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView
from konova.views.detail import BaseDetailView
DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
class EmaIndexView(LoginRequiredMixin, BaseIndexView):
_TAB_TITLE = _("EMAs - Overview")
_INDEX_TABLE_CLS = EmaTable
@login_required
def index_view(request: HttpRequest):
""" Renders the index view for EMAs
def _get_queryset(self):
qs = Ema.objects.filter(
deleted=None,
).order_by(
"-modified__timestamp"
)
return qs
Args:
request (HttpRequest): The incoming request
Returns:
"""
template = "generic_index.html"
emas = Ema.objects.filter(
deleted=None,
).order_by(
"-modified__timestamp"
)
table = EmaTable(
request,
queryset=emas
)
context = {
"table": table,
TAB_TITLE_IDENTIFIER: _("EMAs - Overview"),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required
@@ -95,58 +111,83 @@ def new_view(request: HttpRequest):
return render(request, template, context)
class EmaIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
_MODEL_CLS = Ema
_REDIRECT_URL = "ema:index"
@login_required
@conservation_office_group_required
def new_id_view(request: HttpRequest):
""" JSON endpoint
def _user_has_permission(self, user):
return user.is_ets_user()
Provides fetching of free identifiers for e.g. AJAX calls
class EmaDetailView(BaseDetailView):
_MODEL_CLS = Ema
_TEMPLATE = "ema/detail/view.html"
def _get_object(self, id: str):
""" Fetch object for detail view
Args:
id (str): The record's id'
Returns:
"""
ema = get_object_or_404(Ema, id=id, deleted=None)
return ema
def _get_detail_context(self, obj: Ema):
""" Generate object specific detail context for view
Args:
obj (): The record
Returns:
"""
# Order states according to surface
before_states = obj.before_states.all().order_by("-surface")
after_states = obj.after_states.all().order_by("-surface")
# Precalculate logical errors between before- and after-states
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
sum_before_states = obj.get_surface_before_states()
sum_after_states = obj.get_surface_after_states()
diff_states = abs(sum_before_states - sum_after_states)
context = {
"before_states": before_states,
"after_states": after_states,
"sum_before_states": sum_before_states,
"sum_after_states": sum_after_states,
"diff_states": diff_states,
"has_finished_deadlines": obj.get_finished_deadlines().exists(),
"""
tmp = Ema()
identifier = tmp.generate_new_identifier()
while Ema.objects.filter(identifier=identifier).exists():
identifier = tmp.generate_new_identifier()
return JsonResponse(
data={
"gen_data": identifier
}
return context
)
@login_required
@uuid_required
def detail_view(request: HttpRequest, id: str):
""" Renders the detail view of an EMA
Args:
request (HttpRequest): The incoming request
id (str): The EMA id
Returns:
"""
template = "ema/detail/view.html"
ema = get_object_or_404(Ema, id=id, deleted=None)
geom_form = SimpleGeomForm(instance=ema)
parcels = ema.get_underlying_parcels()
_user = request.user
is_entry_shared = ema.is_shared_with(_user)
# Order states according to surface
before_states = ema.before_states.all().order_by("-surface")
after_states = ema.after_states.all().order_by("-surface")
# Precalculate logical errors between before- and after-states
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
sum_before_states = ema.get_surface_before_states()
sum_after_states = ema.get_surface_after_states()
diff_states = abs(sum_before_states - sum_after_states)
ema.set_status_messages(request)
requesting_user_is_only_shared_user = ema.is_only_shared_with(_user)
if requesting_user_is_only_shared_user:
messages.info(
request,
DO_NOT_FORGET_TO_SHARE
)
context = {
"obj": ema,
"geom_form": geom_form,
"parcels": parcels,
"is_entry_shared": is_entry_shared,
"before_states": before_states,
"after_states": after_states,
"sum_before_states": sum_before_states,
"sum_after_states": sum_after_states,
"diff_states": diff_states,
"is_default_member": _user.in_group(DEFAULT_GROUP),
"is_zb_member": _user.in_group(ZB_GROUP),
"is_ets_member": _user.in_group(ETS_GROUP),
"LANIS_LINK": ema.get_LANIS_link(),
TAB_TITLE_IDENTIFIER: f"{ema.identifier} - {ema.title}",
"has_finished_deadlines": ema.get_finished_deadlines().exists(),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required

View File

@@ -5,36 +5,77 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 19.08.22
"""
from django.http import HttpRequest
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from compensation.views.compensation.report import BaseCompensationReportView
from ema.models import Ema
from konova.sub_settings.django_settings import BASE_URL
from konova.utils.qrcode import QrCode
from konova.contexts import BaseContext
from konova.decorators import uuid_required
from konova.forms import SimpleGeomForm
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.generators import generate_qr_code
@uuid_required
def report_view(request:HttpRequest, id: str):
""" Renders the public report view
class EmaReportView(BaseCompensationReportView):
_TEMPLATE = "ema/report/report.html"
_MODEL = Ema
Args:
request (HttpRequest): The incoming request
id (str): The id of the intervention
def _get_report_context(self, obj):
report_url = BASE_URL + reverse("ema:report", args=(obj.id,))
qrcode_report = QrCode(report_url, 10)
qrcode_lanis = QrCode(obj.get_LANIS_link(), 7)
Returns:
generic_compensation_report_context = self._get_compensation_report_context(obj)
"""
# Reuse the compensation report template since EMAs are structurally identical
template = "ema/report/report.html"
ema = get_object_or_404(Ema, id=id)
report_context = {
"qrcode": {
"img": qrcode_report.get_img(),
"url": qrcode_report.get_content(),
},
"qrcode_lanis": {
"img": qrcode_lanis.get_img(),
"url": qrcode_lanis.get_content(),
},
"is_entry_shared": False, # disables action buttons during rendering
"tables_scrollable": False,
tab_title = _("Report {}").format(ema.identifier)
# If intervention is not recorded (yet or currently) we need to render another template without any data
if not ema.is_ready_for_publish():
template = "report/unavailable.html"
context = {
TAB_TITLE_IDENTIFIER: tab_title,
}
report_context.update(generic_compensation_report_context)
return report_context
context = BaseContext(request, context).context
return render(request, template, context)
# Prepare data for map viewer
geom_form = SimpleGeomForm(
instance=ema,
)
parcels = ema.get_underlying_parcels()
qrcode_url = request.build_absolute_uri(reverse("ema:report", args=(id,)))
qrcode_img = generate_qr_code(qrcode_url, 10)
qrcode_lanis_url = ema.get_LANIS_link()
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
# Order states by surface
before_states = ema.before_states.all().order_by("-surface").prefetch_related("biotope_type")
after_states = ema.after_states.all().order_by("-surface").prefetch_related("biotope_type")
actions = ema.actions.all().prefetch_related("action_type")
context = {
"obj": ema,
"qrcode": {
"img": qrcode_img,
"url": qrcode_url
},
"qrcode_lanis": {
"img": qrcode_img_lanis,
"url": qrcode_lanis_url
},
"is_entry_shared": False, # disables action buttons during rendering
"before_states": before_states,
"after_states": after_states,
"geom_form": geom_form,
"parcels": parcels,
"actions": actions,
"tables_scrollable": False,
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, template, context)

View File

@@ -14,11 +14,10 @@ from intervention.views.deduction import NewInterventionDeductionView, EditInter
RemoveInterventionDeductionView
from intervention.views.document import NewInterventionDocumentView, GetInterventionDocumentView, \
RemoveInterventionDocumentView, EditInterventionDocumentView
from intervention.views.intervention import new_view, edit_view, remove_view, \
InterventionIndexView, InterventionIdentifierGeneratorView, InterventionDetailView
from intervention.views.intervention import index_view, new_view, new_id_view, detail_view, edit_view, remove_view
from intervention.views.log import InterventionLogView
from intervention.views.record import InterventionRecordView
from intervention.views.report import InterventionReportView
from intervention.views.report import report_view
from intervention.views.resubmission import InterventionResubmissionView
from intervention.views.revocation import new_revocation_view, edit_revocation_view, remove_revocation_view, \
get_revocation_view
@@ -26,10 +25,10 @@ from intervention.views.share import InterventionShareFormView, InterventionShar
app_name = "intervention"
urlpatterns = [
path("", InterventionIndexView.as_view(), name="index"),
path("", index_view, name="index"),
path('new/', new_view, name='new'),
path('new/id', InterventionIdentifierGeneratorView.as_view(), name='new-id'),
path('<id>', InterventionDetailView.as_view(), name='detail'),
path('new/id', new_id_view, name='new-id'),
path('<id>', detail_view, name='detail'),
path('<id>/log', InterventionLogView.as_view(), name='log'),
path('<id>/edit', edit_view, name='edit'),
path('<id>/remove', remove_view, name='remove'),
@@ -37,7 +36,7 @@ urlpatterns = [
path('<id>/share', InterventionShareFormView.as_view(), name='share-form'),
path('<id>/check', check_view, name='check'),
path('<id>/record', InterventionRecordView.as_view(), name='record'),
path('<id>/report', InterventionReportView.as_view(), name='report'),
path('<id>/report', report_view, name='report'),
path('<id>/resub', InterventionResubmissionView.as_view(), name='resubmission-create'),
# Compensations

View File

@@ -5,22 +5,51 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 19.08.22
"""
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from intervention.models import Intervention
from konova.decorators import default_group_required, shared_access_required
from konova.views.deduction import AbstractNewDeductionView, AbstractEditDeductionView, AbstractRemoveDeductionView
class NewInterventionDeductionView(LoginRequiredMixin, AbstractNewDeductionView):
_MODEL = Intervention
_REDIRECT_URL = "intervention:detail"
class NewInterventionDeductionView(AbstractNewDeductionView):
def _custom_check(self, obj):
pass
model = Intervention
redirect_url = "intervention:detail"
@method_decorator(login_required)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
class EditInterventionDeductionView(LoginRequiredMixin, AbstractEditDeductionView):
_MODEL = Intervention
_REDIRECT_URL = "intervention:detail"
class EditInterventionDeductionView(AbstractEditDeductionView):
def _custom_check(self, obj):
pass
model = Intervention
redirect_url = "intervention:detail"
@method_decorator(login_required)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
class RemoveInterventionDeductionView(LoginRequiredMixin, AbstractRemoveDeductionView):
_MODEL = Intervention
_REDIRECT_URL = "intervention:detail"
class RemoveInterventionDeductionView(AbstractRemoveDeductionView):
def _custom_check(self, obj):
pass
model = Intervention
redirect_url = "intervention:detail"
@method_decorator(login_required)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)

View File

@@ -7,7 +7,6 @@ Created on: 19.08.22
"""
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import JsonResponse, HttpRequest
from django.shortcuts import get_object_or_404, render, redirect
from django.urls import reverse
@@ -26,23 +25,40 @@ from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import DATA_CHECKED_PREVIOUSLY_TEMPLATE, RECORDED_BLOCKS_EDIT, \
CHECK_STATE_RESET, FORM_INVALID, IDENTIFIER_REPLACED, DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED, \
GEOMETRIES_IGNORED_TEMPLATE
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView
from konova.views.detail import BaseDetailView
class InterventionIndexView(LoginRequiredMixin, BaseIndexView):
_INDEX_TABLE_CLS = InterventionTable
_TAB_TITLE = _("Interventions - Overview")
@login_required
@any_group_check
def index_view(request: HttpRequest):
"""
Renders the index view for Interventions
def _get_queryset(self):
qs = Intervention.objects.filter(
deleted=None,
).select_related(
"legal"
).order_by(
"-modified__timestamp"
)
return qs
Args:
request (HttpRequest): The incoming request
Returns:
A rendered view
"""
template = "generic_index.html"
# Filtering by user access is performed in table filter inside InterventionTableFilter class
interventions = Intervention.objects.filter(
deleted=None, # not deleted
).select_related(
"legal"
).order_by(
"-modified__timestamp"
)
table = InterventionTable(
request=request,
queryset=interventions
)
context = {
"table": table,
TAB_TITLE_IDENTIFIER: _("Interventions - Overview"),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required
@@ -101,64 +117,97 @@ def new_view(request: HttpRequest):
return render(request, template, context)
class InterventionIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
_MODEL_CLS = Intervention
_REDIRECT_URL = "intervention:index"
@login_required
@default_group_required
def new_id_view(request: HttpRequest):
""" JSON endpoint
Provides fetching of free identifiers for e.g. AJAX calls
class InterventionDetailView(BaseDetailView):
_MODEL_CLS = Intervention
_TEMPLATE = "intervention/detail/view.html"
def _get_object(self, id: str):
""" Returns the intervention
Args:
id (str): The intervention's id
Returns:
obj (Intervention): The intervention
"""
# Fetch data, filter out deleted related data
obj = get_object_or_404(
self._MODEL_CLS.objects.select_related(
"geometry",
"legal",
"responsible",
).prefetch_related(
"legal__revocations",
),
id=id,
deleted=None
)
return obj
def _get_detail_context(self, obj: Intervention):
""" Generate object specific detail context for view
Args:
obj (): The record
Returns:
"""
compensations = obj.compensations.filter(deleted=None)
last_checked = obj.get_last_checked_action()
last_checked_tooltip = ""
if last_checked:
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(
last_checked.get_timestamp_str_formatted(),
last_checked.user
)
has_payment_without_document = obj.payments.exists() and not obj.get_documents()[1].exists()
context = {
"last_checked": last_checked,
"last_checked_tooltip": last_checked_tooltip,
"compensations": compensations,
"has_payment_without_document": has_payment_without_document,
"""
tmp_intervention = Intervention()
identifier = tmp_intervention.generate_new_identifier()
while Intervention.objects.filter(identifier=identifier).exists():
identifier = tmp_intervention.generate_new_identifier()
return JsonResponse(
data={
"gen_data": identifier
}
return context
)
@login_required
@any_group_check
@uuid_required
def detail_view(request: HttpRequest, id: str):
""" Renders a detail view for viewing an intervention's data
Args:
request (HttpRequest): The incoming request
id (str): The intervention's id
Returns:
"""
template = "intervention/detail/view.html"
# Fetch data, filter out deleted related data
intervention = get_object_or_404(
Intervention.objects.select_related(
"geometry",
"legal",
"responsible",
).prefetch_related(
"legal__revocations",
),
id=id,
deleted=None
)
compensations = intervention.compensations.filter(
deleted=None,
)
_user = request.user
is_data_shared = intervention.is_shared_with(user=_user)
geom_form = SimpleGeomForm(
instance=intervention,
)
last_checked = intervention.get_last_checked_action()
last_checked_tooltip = ""
if last_checked:
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(
last_checked.get_timestamp_str_formatted(),
last_checked.user
)
has_payment_without_document = intervention.payments.exists() and not intervention.get_documents()[1].exists()
requesting_user_is_only_shared_user = intervention.is_only_shared_with(_user)
if requesting_user_is_only_shared_user:
messages.info(
request,
DO_NOT_FORGET_TO_SHARE
)
context = {
"obj": intervention,
"last_checked": last_checked,
"last_checked_tooltip": last_checked_tooltip,
"compensations": compensations,
"is_entry_shared": is_data_shared,
"geom_form": geom_form,
"is_default_member": _user.in_group(DEFAULT_GROUP),
"is_zb_member": _user.in_group(ZB_GROUP),
"is_ets_member": _user.in_group(ETS_GROUP),
"LANIS_LINK": intervention.get_LANIS_link(),
"has_payment_without_document": has_payment_without_document,
TAB_TITLE_IDENTIFIER: f"{intervention.identifier} - {intervention.title}",
}
request = intervention.set_status_messages(request)
context = BaseContext(request, context).context
return render(request, template, context)
@login_required

View File

@@ -5,41 +5,72 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 19.08.22
"""
from django.http import HttpRequest
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from intervention.models import Intervention
from konova.sub_settings.django_settings import BASE_URL
from konova.utils.qrcode import QrCode
from konova.views.report import BaseReportView
from konova.contexts import BaseContext
from konova.decorators import uuid_required
from konova.forms import SimpleGeomForm
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.generators import generate_qr_code
class InterventionReportView(BaseReportView):
_TEMPLATE = 'intervention/report/report.html'
_MODEL = Intervention
@uuid_required
def report_view(request: HttpRequest, id: str):
""" Renders the public report view
def _get_report_context(self, obj: Intervention):
""" Returns the specific context needed for an intervention report
Args:
request (HttpRequest): The incoming request
id (str): The id of the intervention
Args:
obj (Intervention): The object for the report
Returns:
Returns:
dict: The object specific context for rendering the report
"""
distinct_deductions = obj.deductions.all().distinct("account")
report_url = BASE_URL + reverse("intervention:report", args=(obj.id,))
qrcode_report = QrCode(report_url, 10)
qrcode_lanis = QrCode(obj.get_LANIS_link(), 7)
"""
template = "intervention/report/report.html"
intervention = get_object_or_404(Intervention, id=id)
return {
"deductions": distinct_deductions,
"qrcode": {
"img": qrcode_report.get_img(),
"url": qrcode_report.get_content(),
},
"qrcode_lanis": {
"img": qrcode_lanis.get_img(),
"url": qrcode_lanis.get_content(),
},
"tables_scrollable": False,
tab_title = _("Report {}").format(intervention.identifier)
# If intervention is not recorded (yet or currently) we need to render another template without any data
if not intervention.is_ready_for_publish():
template = "report/unavailable.html"
context = {
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, template, context)
# Prepare data for map viewer
geom_form = SimpleGeomForm(
instance=intervention
)
parcels = intervention.get_underlying_parcels()
distinct_deductions = intervention.deductions.all().distinct(
"account"
)
qrcode_url = request.build_absolute_uri(reverse("intervention:report", args=(id,)))
qrcode_img = generate_qr_code(qrcode_url, 10)
qrcode_lanis_url = intervention.get_LANIS_link()
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
context = {
"obj": intervention,
"deductions": distinct_deductions,
"qrcode": {
"img": qrcode_img,
"url": qrcode_url,
},
"qrcode_lanis": {
"img": qrcode_img_lanis,
"url": qrcode_lanis_url,
},
"geom_form": geom_form,
"parcels": parcels,
"tables_scrollable": False,
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, template, context)

View File

@@ -10,6 +10,7 @@ import json
from django.contrib.gis.db.models import MultiPolygonField
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.db import models, transaction
from django.db.models import Q
from django.utils import timezone
from django.contrib.gis.geos import MultiPolygon
@@ -109,17 +110,26 @@ class Geometry(BaseResource):
objs (list): The list of objects
"""
objs = []
sets = [
# Some related data sets can be processed rather easily
regular_sets = [
self.intervention_set,
self.compensation_set,
self.ema_set,
self.ecoaccount_set,
]
for _set in sets:
for _set in regular_sets:
set_objs = _set.filter(
deleted=None
)
objs += set_objs
# ... but we need a special treatment for compensations, since they can be deleted directly OR inherit their
# de-facto-deleted status from their deleted parent intervention
comp_objs = self.compensation_set.filter(
Q(deleted=None) & Q(intervention__deleted=None)
)
objs += comp_objs
return objs
def get_data_object(self):

View File

@@ -677,12 +677,12 @@ class GeoReferencedMixin(models.Model):
return request
instance_objs = []
conflicts = self.geometry.conflicts_geometries.all()
conflicts = self.geometry.conflicts_geometries.iterator()
for conflict in conflicts:
instance_objs += conflict.affected_geometry.get_data_objects()
conflicts = self.geometry.conflicted_by_geometries.all()
conflicts = self.geometry.conflicted_by_geometries.iterator()
for conflict in conflicts:
instance_objs += conflict.conflicting_geometry.get_data_objects()

View File

@@ -288,4 +288,8 @@ Overwrites netgis.css attributes
Overwrites gradient used on default css of netgis map client
*/
background: var(--rlp-red) !important;
}
.netgis-menu{
z-index: 1 !important;
}

View File

@@ -5,11 +5,6 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 17.09.21
"""
from uuid import UUID
from django.contrib import messages
from django.utils.translation import gettext_lazy as _
from django.http import HttpRequest, Http404
def format_german_float(num) -> str:
@@ -24,28 +19,3 @@ def format_german_float(num) -> str:
num (str): The number as german Gleitkommazahl
"""
return format(num, "0,.2f").replace(",", "X").replace(".", ",").replace("X", ".")
def check_user_is_in_any_group(request: HttpRequest):
"""
Checks for any group membership. Adds a message in case of having none.
"""
user = request.user
# Inform user about missing group privileges!
groups = user.groups.all()
if not groups:
messages.info(
request,
_("+++ Attention: You are not part of any group. You won't be able to create, edit or do anything. Please contact an administrator. +++")
)
return request
def check_id_is_valid_uuid(**kwargs: dict):
uuid = kwargs.get("uuid", None) or kwargs.get("id", None)
if uuid:
try:
# Check whether the id is a proper uuid or something that would break a db fetch
UUID(uuid)
except ValueError:
raise Http404

View File

@@ -7,6 +7,10 @@ Created on: 09.11.20
"""
import random
import string
import qrcode
import qrcode.image.svg
from io import BytesIO
def generate_token() -> str:
@@ -38,3 +42,23 @@ def generate_random_string(length: int, use_numbers: bool = False, use_letters_l
ret_val = "".join(random.choice(elements) for i in range(length))
return ret_val
def generate_qr_code(content: str, size: int = 20) -> str:
""" Generates a qr code from given content
Args:
content (str): The content for the qr code
size (int): The image size
Returns:
qrcode_svg (str): The qr code as svg
"""
qrcode_factory = qrcode.image.svg.SvgImage
qrcode_img = qrcode.make(
content,
image_factory=qrcode_factory,
box_size=size
)
stream = BytesIO()
qrcode_img.save(stream)
return stream.getvalue().decode()

View File

@@ -1,47 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 17.10.25
"""
from io import BytesIO
import qrcode
import qrcode.image.svg as svg
class QrCode:
""" A wrapping class for creating a qr code with content
"""
_content = None
_img = None
def __init__(self, content: str, size: int):
self._content = content
self._img = self._generate_qr_code(content, size)
def _generate_qr_code(self, content: str, size: int = 20) -> str:
""" Generates a qr code from given content
Args:
content (str): The content for the qr code
size (int): The image size
Returns:
qrcode_svg (str): The qr code as svg
"""
img_factory = svg.SvgImage
qrcode_img = qrcode.make(
content,
image_factory=img_factory,
box_size=size
)
stream = BytesIO()
qrcode_img.save(stream)
return stream.getvalue().decode()
def get_img(self):
return self._img
def get_content(self):
return self._content

View File

@@ -1,141 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 15.10.25
"""
from abc import abstractmethod
from django.contrib import messages
from django.http import HttpRequest, JsonResponse
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views import View
from konova.contexts import BaseContext
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.general import check_user_is_in_any_group
from konova.utils.message_templates import MISSING_GROUP_PERMISSION, DATA_UNSHARED
class BaseView(View):
_TEMPLATE: str = "CHANGE_ME"
_TAB_TITLE: str = "CHANGE_ME"
_REDIRECT_URL: str = "CHANGE_ME"
_REDIRECT_URL_ERROR: str = "home"
class Meta:
abstract = True
def dispatch(self, request, *args, **kwargs):
request = check_user_is_in_any_group(request)
if not self._user_has_permission(request.user):
messages.info(request, MISSING_GROUP_PERMISSION)
return redirect(reverse(self._REDIRECT_URL_ERROR))
if not self._user_has_shared_access(request.user, **kwargs):
messages.info(request, DATA_UNSHARED)
return redirect(reverse(self._REDIRECT_URL_ERROR))
return super().dispatch(request, *args, **kwargs)
def _user_has_permission(self, user):
""" Has to be implemented properly by inheriting classes
Args:
user ():
Returns:
"""
return False
def _user_has_shared_access(self, user, **kwargs):
""" Has to be implemented properly by inheriting classes
Args:
user ():
Returns:
"""
return False
class BaseModalFormView(BaseView):
_TEMPLATE = "modal/modal_form.html"
_TAB_TITLE = None
class Meta:
abstract = True
class BaseIndexView(BaseView):
""" Base class for index views
"""
_TEMPLATE = "generic_index.html"
_INDEX_TABLE_CLS = None
_REDIRECT_URL = "home"
class Meta:
abstract = True
def get(self, request: HttpRequest):
qs = self._get_queryset()
table = self._INDEX_TABLE_CLS(
request=request,
queryset=qs
)
context = {
"table": table,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@abstractmethod
def _get_queryset(self):
raise NotImplementedError
def _user_has_permission(self, user):
# No specific permissions needed for opening base index view
return True
def _user_has_shared_access(self, user, **kwargs):
# No specific constraints for shared access of index views
return True
class BaseIdentifierGeneratorView(BaseView):
_MODEL_CLS = None
_REDIRECT_URL: str = "home"
class Meta:
abstract = True
def get(self, request: HttpRequest):
tmp_obj = self._MODEL_CLS()
identifier = tmp_obj.generate_new_identifier()
while self._MODEL_CLS.objects.filter(identifier=identifier).exists():
identifier = tmp_obj.generate_new_identifier()
return JsonResponse(
data={
"gen_data": identifier
}
)
def _user_has_permission(self, user):
""" Should be overwritten in inheriting classes!
Args:
user ():
Returns:
"""
return user.is_default_user()
def _user_has_shared_access(self, user, **kwargs):
# No specific constraints for shared access
return True

View File

@@ -6,60 +6,30 @@ Created on: 22.08.22
"""
from django.core.exceptions import ObjectDoesNotExist
from django.http import Http404, HttpRequest
from django.http import Http404
from django.shortcuts import get_object_or_404
from django.urls import reverse
from django.views import View
from intervention.forms.modals.deduction import NewEcoAccountDeductionModalForm, EditEcoAccountDeductionModalForm, \
RemoveEcoAccountDeductionModalForm
from konova.utils.message_templates import DEDUCTION_ADDED, DEDUCTION_EDITED, DEDUCTION_REMOVED, DEDUCTION_UNKNOWN
from konova.views.base import BaseModalFormView
class AbstractDeductionView(BaseModalFormView):
_MODEL = None
_REDIRECT_URL = None
class AbstractDeductionView(View):
model = None
redirect_url = None
def _custom_check(self, obj):
"""
Can be used by inheriting classes to provide custom checks before further processing
"""
pass
def _user_has_permission(self, user) -> bool:
"""
Args:
user ():
Returns:
"""
return user.is_default_user()
def _user_has_shared_access(self, user, **kwargs) -> bool:
""" A user has shared access on
Args:
user (User): The performing user
kwargs (dict): Parameters
Returns:
bool: True if the user has access to the requested object, False otherwise
"""
ret_val: bool = False
try:
obj = self._MODEL.objects.get(
id=kwargs.get("id")
)
ret_val = obj.is_shared_with(user)
except ObjectDoesNotExist:
ret_val = False
return ret_val
raise NotImplementedError("Must be implemented in subclasses")
class AbstractNewDeductionView(AbstractDeductionView):
class Meta:
abstract = True
@@ -73,13 +43,13 @@ class AbstractNewDeductionView(AbstractDeductionView):
Returns:
"""
obj = get_object_or_404(self._MODEL, id=id)
obj = get_object_or_404(self.model, id=id)
self._custom_check(obj)
form = NewEcoAccountDeductionModalForm(request.POST or None, instance=obj, request=request)
return form.process_request(
request,
msg_success=DEDUCTION_ADDED,
redirect_url=reverse(self._REDIRECT_URL, args=(id,)) + "#related_data",
redirect_url=reverse(self.redirect_url, args=(id,)) + "#related_data",
)
def post(self, request, id: str):
@@ -87,6 +57,10 @@ class AbstractNewDeductionView(AbstractDeductionView):
class AbstractEditDeductionView(AbstractDeductionView):
def _custom_check(self, obj):
pass
class Meta:
abstract = True
@@ -101,7 +75,7 @@ class AbstractEditDeductionView(AbstractDeductionView):
Returns:
"""
obj = get_object_or_404(self._MODEL, id=id)
obj = get_object_or_404(self.model, id=id)
self._custom_check(obj)
try:
eco_deduction = obj.deductions.get(id=deduction_id)
@@ -113,7 +87,7 @@ class AbstractEditDeductionView(AbstractDeductionView):
return form.process_request(
request=request,
msg_success=DEDUCTION_EDITED,
redirect_url=reverse(self._REDIRECT_URL, args=(id,)) + "#related_data"
redirect_url=reverse(self.redirect_url, args=(id,)) + "#related_data"
)
def post(self, request, id: str, deduction_id: str):
@@ -121,6 +95,10 @@ class AbstractEditDeductionView(AbstractDeductionView):
class AbstractRemoveDeductionView(AbstractDeductionView):
def _custom_check(self, obj):
pass
class Meta:
abstract = True
@@ -135,7 +113,7 @@ class AbstractRemoveDeductionView(AbstractDeductionView):
Returns:
"""
obj = get_object_or_404(self._MODEL, id=id)
obj = get_object_or_404(self.model, id=id)
self._custom_check(obj)
try:
eco_deduction = obj.deductions.get(id=deduction_id)
@@ -146,7 +124,7 @@ class AbstractRemoveDeductionView(AbstractDeductionView):
return form.process_request(
request=request,
msg_success=DEDUCTION_REMOVED,
redirect_url=reverse(self._REDIRECT_URL, args=(id,)) + "#related_data"
redirect_url=reverse(self.redirect_url, args=(id,)) + "#related_data"
)
def post(self, request, id: str, deduction_id: str):

View File

@@ -1,107 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 17.10.25
"""
from abc import abstractmethod
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest
from django.shortcuts import render
from konova.contexts import BaseContext
from konova.forms import SimpleGeomForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.general import check_id_is_valid_uuid
from konova.utils.message_templates import DO_NOT_FORGET_TO_SHARE
from konova.views.base import BaseView
class BaseDetailView(LoginRequiredMixin, BaseView):
_MODEL_CLS = None
class Meta:
abstract = True
def dispatch(self, request, *args, **kwargs):
check_id_is_valid_uuid(**kwargs)
return super().dispatch(request, *args, **kwargs)
def _user_has_shared_access(self, user, **kwargs):
""" Check if user has shared access to this object
Args:
user ():
**kwargs ():
Returns:
"""
# Access to an entry's detail view is not restricted by the state of being-shared or not
return True
def _user_has_permission(self, user):
# Detail views have no restrictions
return True
def get(self, request: HttpRequest, id: str):
""" Get endpoint for detail view
Args:
request (HttpRequest): The incoming request
id (str): The record's id
Returns:
"""
obj = self._get_object(id)
geom_form = SimpleGeomForm(instance=obj)
user = request.user
requesting_user_is_only_shared_user = obj.is_only_shared_with(user)
if requesting_user_is_only_shared_user:
messages.info(request, DO_NOT_FORGET_TO_SHARE)
obj.set_status_messages(request)
detail_context = self._get_detail_context(obj)
context = BaseContext(request, detail_context).context
context.update(
{
"obj": obj,
"geom_form": geom_form,
"is_default_member": user.in_group(DEFAULT_GROUP),
"is_zb_member": user.in_group(ZB_GROUP),
"is_ets_member": user.in_group(ETS_GROUP),
"LANIS_LINK": obj.get_LANIS_link(),
"is_entry_shared": obj.is_shared_with(user=user),
TAB_TITLE_IDENTIFIER: f"{obj.identifier} - {obj.title}"
}
)
return render(request,self._TEMPLATE, context)
@abstractmethod
def _get_detail_context(self, obj):
""" Generate object specific detail context for view
Args:
obj (): The record
Returns:
"""
raise NotImplementedError
@abstractmethod
def _get_object(self, id: str):
""" Fetch object for detail view
Args:
id (str): The record's id'
Returns:
"""
raise NotImplementedError

View File

@@ -9,19 +9,21 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Q
from django.http import HttpRequest
from django.shortcuts import render
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django.views import View
from compensation.models import EcoAccount, Compensation
from intervention.models import Intervention
from konova.contexts import BaseContext
from konova.decorators import any_group_check
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.views.base import BaseView
from news.models import ServerMessage
class HomeView(LoginRequiredMixin, BaseView):
_TEMPLATE = "konova/home.html"
class HomeView(LoginRequiredMixin, View):
@method_decorator(any_group_check)
def get(self, request: HttpRequest):
"""
Renders the landing page
@@ -32,6 +34,7 @@ class HomeView(LoginRequiredMixin, BaseView):
Returns:
A redirect
"""
template = "konova/home.html"
user = request.user
user_teams = user.shared_teams
@@ -72,12 +75,5 @@ class HomeView(LoginRequiredMixin, BaseView):
TAB_TITLE_IDENTIFIER: _("Home"),
}
context = BaseContext(request, additional_context).context
return render(request, self._TEMPLATE, context)
return render(request, template, context)
def _user_has_permission(self, user):
# No specific permission needed for home view
return True
def _user_has_shared_access(self, user, **kwargs):
# No specific constraint needed for home view
return True

View File

@@ -10,8 +10,9 @@ from json import JSONDecodeError
import requests
import urllib3.util
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse, HttpRequest, HttpResponse
from django.utils.decorators import method_decorator
from django.utils.http import urlencode
from django.views import View
@@ -21,13 +22,17 @@ from konova.sub_settings.lanis_settings import MAP_PROXY_HOST_WHITELIST
from konova.sub_settings.proxy_settings import PROXIES, GEOPORTAL_RLP_USER, GEOPORTAL_RLP_PASSWORD
class BaseClientProxyView(LoginRequiredMixin, View):
class BaseClientProxyView(View):
""" Provides proxy functionality for NETGIS map client.
"""
class Meta:
abstract = True
@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def _check_with_whitelist(self, url):
parsed_url = urllib3.util.parse_url(url)
parsed_url_host = parsed_url.host
@@ -62,6 +67,7 @@ class BaseClientProxyView(LoginRequiredMixin, View):
class ClientProxyParcelSearch(BaseClientProxyView):
def get(self, request: HttpRequest):
url = request.META.get("QUERY_STRING")

View File

@@ -1,106 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 17.10.25
"""
from abc import abstractmethod
from uuid import UUID
from django.http import HttpRequest, Http404
from django.shortcuts import get_object_or_404, render
from django.utils.translation import gettext_lazy as _
from konova.contexts import BaseContext
from konova.forms import SimpleGeomForm
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.views.base import BaseView
class BaseReportView(BaseView):
_TEMPLATE = None
_TAB_TITLE = _("Report {}")
_MODEL = None
class Meta:
abstract = True
def dispatch(self, request, *args, **kwargs):
# If the given id is not a uuid we act as the result was not found
try:
UUID(kwargs.get('id'))
except ValueError:
raise Http404()
return super().dispatch(request, *args, **kwargs)
def _return_unpublishable_content_response(self, request: HttpRequest, tab_title: str):
""" Handles HttpResponse return in case the object is not ready for publish
Args:
request ():
tab_title ():
Returns:
"""
template = "report/unavailable.html"
context = {
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, template, context)
def get(self, request: HttpRequest, id: str):
""" Renders the public report view
Args:
request (HttpRequest): The incoming request
id (str): The id of the intervention
Returns:
"""
obj = get_object_or_404(self._MODEL, id=id)
tab_title = self._TAB_TITLE.format(obj.identifier)
# If object is not recorded we need to render another template without any data
if not obj.is_ready_for_publish():
return self._return_unpublishable_content_response(request, tab_title)
# First get specific report context for different types of objects due to inheritance
report_context = self._get_report_context(obj)
# Then generate and add default report context (the same for all models)
geom_form = SimpleGeomForm(instance=obj)
parcels = obj.get_underlying_parcels()
report_context.update(
{
TAB_TITLE_IDENTIFIER: tab_title,
"parcels": parcels,
"geom_form": geom_form,
"obj": obj
}
)
# Then generate the general context based on the report specific data
context = BaseContext(request, report_context).context
return render(request, self._TEMPLATE, context)
@abstractmethod
def _get_report_context(self, obj):
""" Returns the specific context needed for this report view
Args:
obj (RecordableObjectMixin): The object for the report
Returns:
dict: The object specific context for rendering the report
"""
raise NotImplementedError
def _user_has_permission(self, user):
# Reports do not need specific permissions to be callable
return True
def _user_has_shared_access(self, user, **kwargs):
# Reports do not need specific share states to be callable
return True

View File

@@ -58,7 +58,7 @@
[
{ "id": "webatlas_farbe", "folder": "bg", "type": "WMS", "order": -1, "title": "WebatlasRP farbig", "attribution": "LVermGeo", "url": "https://maps.service24.rlp.de/gisserver/services/RP/RP_WebAtlasRP/MapServer/WmsServer?", "name": "RP_WebAtlasRP", "active": true},
{ "id": "webatlas_grau", "folder": "bg", "type": "WMS", "order": -1, "title": "WebatlasRP grau", "attribution": "LVermGeo", "url": "https://maps.service24.rlp.de/gisserver/services/RP/RP_ETRS_Gt/MapServer/WmsServer?", "name": "0", "active": false },
{ "id": "luftbilder", "folder": "bg", "type": "WMS", "order": -1, "title": "Luftbilder", "attribution": "LVermGeo", "url": "https://geo4.service24.rlp.de/wms/dop_basis.fcgi?", "name": "rp_dop", "active": false },
{ "id": "luftbilder", "folder": "bg", "type": "WMS", "order": -1, "title": "Luftbilder", "attribution": "LVermGeo", "url": "https://geo4.service24.rlp.de/wms/rp_dop20.fcgi?", "name": "rp_dop20", "active": false },
{ "id": "basemap_farbe", "folder": "bg", "type": "WMS", "order": -1, "title": "BasemapDE farbig", "attribution": "BKG", "url": "https://sgx.geodatenzentrum.de/wms_basemapde?", "name": "de_basemapde_web_raster_farbe", "active": false },
{ "id": "basemap_grau", "folder": "bg", "type": "WMS", "order": -1, "title": "BasemapDE grau", "attribution": "BKG", "url": "https://sgx.geodatenzentrum.de/wms_basemapde?", "name": "de_basemapde_web_raster_grau", "active": false },
{ "id": "dtk_farbe", "folder": "bg", "type": "WMS", "order": -1, "title": "DTK5 farbig", "attribution": "LVermGeo", "url": "https://geo4.service24.rlp.de/wms/dtk5_rp.fcgi?", "name": "rp_dtk5", "active": false },
@@ -188,6 +188,7 @@
{
"title": "Ebene hinzufügen",
"preview": true,
"editable": true,
"wms_options": [ "https://sgx.geodatenzentrum.de/wms_topplus_open" ],
"wfs_options": [ "http://213.139.159.34:80/geoserver/uesg/wfs" ],
"wfs_proxy": "/client/proxy?",

File diff suppressed because one or more lines are too long

View File

@@ -13,7 +13,7 @@ netgis.Attribution.prototype.add=function(a){for(var b=0;b<this.items.length;b++
netgis.Attribution.prototype.onContextUpdate=function(a){this.initConfig(a.detail.context.config);this.update()};netgis.Attribution.prototype.onContextUpdate_01=function(a){config.attribution&&config.attribution.prefix&&this.items.push(config.attribution.prefix);this.layers=[];for(var b=0;b<a.layers.length;b++){var c=a.layers[b];c.attribution&&0<c.attribution.length&&(this.layers[c.id]=c.attribution)}for(b=0;b<a.layers.length;b++)if(c=a.layers[b],c.active)this.onLayerShow({id:c.id})};
netgis.Attribution.prototype.onLayerShow=function(a){if(a=this.layers[a.id]){for(var b=0;b<this.items.length;b++)if(this.items[b]===a)return;this.items.push(a);this.update()}};netgis.Attribution.prototype.onLayerHide=function(a){if(a=this.layers[a.id]){for(var b=0;b<this.items.length;b++)if(this.items[b]===a){this.items.splice(b,1);break}this.update()}};
netgis.Attribution.prototype.onEditLayerChange=function(a){a=a.detail.geojson.area;for(var b=0;b<this.items.length;b++)if(-1<this.items[b].search("Zeichnungsfl\u00e4che: ")){this.items.splice(b,1);break}this.appendix=a&&0<a?"<b>Zeichnungsfl\u00e4che: "+netgis.util.formatArea(a,!0)+"</b>":null;this.update()};netgis=netgis||{};netgis.Client=function(a,b){b||(b={});a=this.initLegacyConfig(b,a);this.container=this.initContainer(a);this.debug=!1;netgis.util.isString(b)?netgis.util.isJSON(b,!1)?(b=JSON.parse(b),this.init(this.container,b)):(this.showLoader(!0),netgis.util.request(b,this.onConfigResponse.bind(this))):this.init(this.container,b)};netgis.Client.Config={loading_text:"Geoportal Client wird geladen..."};netgis.Client.Output={id:"netgis-storage"};
netgis.Client.prototype.init=function(a,b){this.config=b;this.initParams(b);this.initConfig(b);this.initElements(a);this.initEvents();this.initModules(b);this.initOutput(b);a=new netgis.ContextMenu;a.attachTo(this.container);this.modules.contextmenu=a;this.popup=new netgis.Popup;this.popup.attachTo(this.container)};
netgis.Client.prototype.init=function(a,b){this.config=b;this.initParams(b);this.initConfig(b);this.initElements(a);this.initEvents();this.initModules(b);this.initOutput(b)};
netgis.Client.prototype.initLegacyConfig=function(a,b){var c=netgis.config;if(!c)return b;c.MAP_CONTAINER_ID&&(b=c.MAP_CONTAINER_ID);a.modules||(a.modules={menu:!0,map:!0,controls:!0,attribution:!0,legend:!0,layertree:!0,info:!0,searchplace:!0,geolocation:!0});a.map||(a.map={});!c.INITIAL_CENTER_X&&0!==c.INITIAL_CENTER_X||!c.INITIAL_CENTER_Y&&0!==c.INITIAL_CENTER_Y||(a.map.center=[c.INITIAL_CENTER_X,c.INITIAL_CENTER_Y]);a.map.scalebar=!0;c.INITIAL_SCALE&&(a.map.scale=c.INITIAL_SCALE);c.MAP_SCALES&&
(a.map.scales=c.MAP_SCALES);c.MAP_EXTENT&&(a.map.extent=c.MAP_EXTENT);c.MAX_HISTORY&&(a.map.max_view_history=c.MAX_HISTORY);a.attribution={prefix:"GeoPortal"};c.MAP_PROJECTIONS&&(a.projections=c.MAP_PROJECTIONS);c.MAP_PROJECTION&&(a.map.projection=c.MAP_PROJECTION);a.controls={buttons:[{id:"zoom_in",icon:"<i class='fas fa-plus'></i>",title:"Zoom +"},{id:"zoom_out",icon:"<i class='fas fa-minus'></i>",title:"Zoom -"},{id:"geolocation",icon:"<i class='fas fa-crosshairs'></i>",title:"Ger\u00e4testandort"},
{id:"zoom_home",icon:"<i class='fas fa-home'></i>",title:"Anfangsausdehung"},{id:"legend",icon:"<i class='fas fa-bars'></i>",title:"Legende"}]};a.folders||(a.folders=[{id:"bg",title:"Hintergrund",parent:null,radio:!0}]);a.layers||(a.layers=[]);if(c.URL_BACKGROUND_HYBRID){var d=c.URL_BACKGROUND_HYBRID;-1!==d.indexOf("{x}")||-1!==d.indexOf("{y}")&&-1!==d.indexOf("{-y}")||-1!==d.indexOf("{z}")||(d+="/{z}/{x}/{-y}.jpeg");d={id:"bg_hybrid",active:!0,folder:"bg",order:1,title:"Hybrid",type:"TMS",url:d,
@@ -38,10 +38,7 @@ this.loaderTimeout=null}.bind(this),600)):(this.loader.classList.remove("netgis-
netgis.Client.prototype.isMobile=function(){return netgis.util.isMobile(this.container)};netgis.Client.prototype.onConfigResponse=function(a){a=JSON.parse(a);this.init(this.container,a);this.showLoader(!1)};netgis.Client.prototype.requestContextWMC=function(a,b){if(-1<a.indexOf("{id}"))if(b)a=netgis.util.replace(a,"{id}",b);else{console.warn("No WMC id set in config for url",a);return}(new netgis.WMC(this.config)).requestContext(a,this.onContextResponseWMC.bind(this));this.showLoader(!0)};
netgis.Client.prototype.onContextResponseWMC=function(a){console.info("WMC Response:",a);for(var b=0;b<a.config.layers.length;b++)this.config.layers.push(a.config.layers[b]);a.config.map.bbox&&(this.config.map.bbox=a.config.map.bbox);netgis.util.invoke(this.container,netgis.Events.CLIENT_CONTEXT_RESPONSE,{context:a});this.showLoader(!1)};
netgis.Client.prototype.onContextResponseLayer=function(a){var b=JSON.parse(a);console.info("Layer Response:",b);a=new netgis.WMC;b=b.wms.srv[0];a=a.parseServiceLayer(b.id.toString(),b,null,b.layer[0],null);this.config.layers.push(a);netgis.util.invoke(this.container,netgis.Events.MAP_LAYER_CREATE,a)};netgis.Client.prototype.requestContextOWS=function(a){console.info("Request OWS:",a);netgis.util.request(a,this.onContextResponseOWS.bind(this))};
netgis.Client.prototype.onContextResponseOWS=function(a){a=JSON.parse(a);console.info("OWS Response:",a);a=netgis.OWS.read(a,this);console.info("OWS Config:",a)};netgis.Client.prototype.onIconbarIconClick=function(a){switch(a.detail.id){case "home":a=this.config.layers;for(var b=0;b<a.length;b++){var c=a[b],d=c.id;!0===c.active?(this.modules.map.addLayer(d,c),this.modules.layertree.tree.setItemChecked(d,!0)):(this.modules.map.removeLayer(d),this.modules.layertree.tree.setItemChecked(d,!1))}}};
netgis.Client.prototype.onIconbarItemClick=function(a){a=a.detail;for(var b=this.config.layers,c=0;c<b.length;c++){var d=b[c],e=d.id;"background"!==d.folder&&(e===a.id?(this.modules.map.addLayer(e,d),this.modules.layertree.tree.setItemChecked(e,!0)):(this.modules.map.removeLayer(e),this.modules.layertree.tree.setItemChecked(e,!1)))}};
netgis.Client.prototype.onSwitcherButtonClick=function(a){a=a.detail;for(var b=this.config.switcher.buttons,c=this.config.layers,d=0;d<b.length;d++){var e=b[d].id;if(e===a.id)for(var f=0;f<c.length;f++){var g=c[f];g.id===e&&(this.modules.map.addLayer(e,g),this.modules.layertree.tree.setItemChecked(e,!0))}else this.modules.map.removeLayer(e),this.modules.layertree.tree.setItemChecked(e,!1)}0===this.modules.switcher.getIndex(a.id)&&this.modules.switcher.shift(1,0)};
netgis.Client.prototype.onGeolocationToggle=function(a){this.modules.map.setGeolocMarkerVisible(a.detail.on)};netgis.Client.prototype.onGeolocationChange=function(a){a=a.detail;this.modules.map.zoomLonLat(a.lon,a.lat,this.config.geolocation.zoom);this.modules.map.setGeolocMarkerLonLat(a.lon,a.lat)};netgis.Client.prototype.onMapEditLayerChange=function(a){a=JSON.stringify(a.detail.geojson);this.output.value=a};
netgis.Client.prototype.onContextResponseOWS=function(a){a=JSON.parse(a);console.info("OWS Response:",a);a=netgis.OWS.read(a,this);console.info("OWS Config:",a)};netgis.Client.prototype.onMapEditLayerChange=function(a){a=JSON.stringify(a.detail.geojson);this.output.value=a};
netgis.Client.handleCommand=function(a,b){var c=b.split(":");b=c[0];switch(b.toUpperCase()){case netgis.Commands.PLUGIN:b=c[1];if(!b){console.error("missing second command parameter id",c);break}netgis.util.invoke(a,netgis.Events.PLUGIN_TOGGLE,{id:b});break;case netgis.Commands.LAYERTREE:netgis.util.isMobile()?netgis.util.invoke(a,netgis.Events.LAYERTREE_TOGGLE,{on:!0}):netgis.util.invoke(a,netgis.Events.LAYERTREE_TOGGLE,null);break;case netgis.Commands.SEARCHPLACE:netgis.util.isMobile()?netgis.util.invoke(a,
netgis.Events.SEARCHPLACE_TOGGLE,{on:!0}):netgis.util.invoke(a,netgis.Events.SEARCHPLACE_TOGGLE,null);break;case netgis.Commands.SEARCHPARCEL:netgis.util.isMobile()?netgis.util.invoke(a,netgis.Events.SEARCHPARCEL_TOGGLE,{on:!0}):netgis.util.invoke(a,netgis.Events.SEARCHPARCEL_TOGGLE,null);break;case netgis.Commands.TOOLBOX:netgis.util.isMobile()?netgis.util.invoke(a,netgis.Events.TOOLBOX_TOGGLE,{on:!0}):netgis.util.invoke(a,netgis.Events.TOOLBOX_TOGGLE,null);break;case netgis.Commands.LEGEND:netgis.util.isMobile()?
netgis.util.invoke(a,netgis.Events.LEGEND_TOGGLE,{on:!0}):netgis.util.invoke(a,netgis.Events.LEGEND_TOGGLE,null);break;case netgis.Commands.VIEW_PREV:netgis.util.invoke(a,netgis.Events.MAP_VIEW_PREV,null);break;case netgis.Commands.VIEW_NEXT:netgis.util.invoke(a,netgis.Events.MAP_VIEW_NEXT,null);break;case netgis.Commands.VIEW:netgis.util.invoke(a,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.VIEW});break;case netgis.Commands.ZOOM_BOX:netgis.util.invoke(a,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.ZOOM_BOX});
@@ -94,19 +91,19 @@ netgis.Export.prototype.onExportEnd=function(a){this.modal.hide()};netgis=netgis
netgis.Geolocation.prototype.setActive=function(a,b){var c=this.config.geolocation;a?navigator.geolocation?(this.watch=navigator.geolocation.watchPosition(this.onPositionChange.bind(this),this.onPositionError.bind(this),{timeout:c&&c.timeout?1E3*c.timeout:1E4,maximumAge:0,enableHighAccuracy:!0}),b||netgis.util.invoke(this.container,netgis.Events.GEOLOCATION_TOGGLE_ACTIVE,{on:!0})):this.error("Geolocation not supported by this device!"):(this.watch&&(navigator.geolocation.clearWatch(this.watch),this.watch=
null),b||netgis.util.invoke(this.container,netgis.Events.GEOLOCATION_TOGGLE_ACTIVE,{on:!1}));this.active=a};netgis.Geolocation.prototype.isActive=function(){return this.active};netgis.Geolocation.prototype.error=function(a){console.error(a);this.watch&&(navigator.geolocation.clearWatch(this.watch),this.watch=null);netgis.util.invoke(this.container,netgis.Events.GEOLOCATION_TOGGLE_ACTIVE,{on:!1})};netgis.Geolocation.prototype.onActiveChange=function(a){this.setActive(a.currentTarget.checked)};
netgis.Geolocation.prototype.onCenterChange=function(a){};netgis.Geolocation.prototype.onPositionChange=function(a){netgis.util.invoke(this.container,netgis.Events.GEOLOCATION_CHANGE,{lon:a.coords.longitude,lat:a.coords.latitude,center:this.center})};netgis.Geolocation.prototype.onPositionError=function(a){this.error("Geolocation: "+a.message+" ("+a.code+")")};netgis.Geolocation.prototype.onGeolocToggleActive=function(a){a.target!==this.container&&this.setActive(a.detail.on)};
netgis.Geolocation.prototype.onGeolocToggleCenter=function(a){a.target!==this.container&&(this.center=a.detail.on)};netgis=netgis||{};netgis.Import=function(a){this.config=a;this.initElements(a);this.initSections(a);this.initPreview()};netgis.Import.Config={title:"Import Layer",preview:!0,wms_options:[],wfs_options:[],wfs_proxy:"",geopackage_lib:"/libs/geopackage/4.2.3/",geoportal_tab:!0,geoportal_search_url:"",geoportal_autocomplete:!0};
netgis.Geolocation.prototype.onGeolocToggleCenter=function(a){a.target!==this.container&&(this.center=a.detail.on)};netgis=netgis||{};netgis.Import=function(a){this.config=a;this.initElements(a);this.initSections(a);this.initPreview()};netgis.Import.Config={title:"Import Layer",preview:!0,editable:!0,wms_options:[],wfs_options:[],wfs_proxy:"",geopackage_lib:"/libs/geopackage/4.2.3/",geoportal_tab:!0,geoportal_search_url:"",geoportal_autocomplete:!0};
netgis.Import.prototype.initElements=function(a){a=a["import"];this.modal=new netgis.Modal(a.title?a.title:"Import");this.modal.container.classList.add("netgis-import");var b="WMS WFS GeoJSON GML GeoPackage Spatialite Shapefile".split(" ");a.geoportal_tab&&b.unshift("Geoportal");this.tabs=new netgis.Tabs(b);this.tabs.container.style.position="absolute";this.tabs.container.style.left="0mm";this.tabs.container.style.right="0mm";this.tabs.container.style.top="12mm";this.tabs.container.style.bottom="0mm";
this.tabs.attachTo(this.modal.content)};
netgis.Import.prototype.initSections=function(a){this.sections={};var b=0;a["import"]&&!0===a["import"].geoportal_tab&&(this.sections.geoportal=this.tabs.getContentSection(b),b+=1,this.sections.geoportal.classList.add("netgis-geoportal"),this.geoportalSearch=new netgis.Search("Thema, Schlagwort..."),this.geoportalSearch.container.addEventListener(netgis.Events.SEARCH_CHANGE,this.onGeoportalSearchChange.bind(this)),this.geoportalSearch.container.addEventListener(netgis.Events.SEARCH_CLEAR,this.onGeoportalSearchClear.bind(this)),
this.geoportalSearch.attachTo(this.sections.geoportal),a=document.createElement("span"),a.innerHTML="Suche im Datenkatalog:",this.geoportalSearch.label.insertBefore(a,this.geoportalSearch.label.firstChild),this.geoportalLoader=document.createElement("div"),this.geoportalLoader.className="netgis-loader netgis-text-a netgis-hide",this.geoportalLoader.innerHTML="<i class='fas fa-cog'></i>",this.sections.geoportal.appendChild(this.geoportalLoader),this.geoportalResults=new netgis.Tree,this.geoportalResults.container.addEventListener(netgis.Events.TREE_ITEM_CHANGE,
this.onGeoportalTreeItemChange.bind(this)),this.geoportalResults.attachTo(this.sections.geoportal),this.geoportalSubmit=this.addButton(this.sections.geoportal,"<i class='netgis-icon fas fa-check'></i><span>Hinzuf\u00fcgen<span class='netgis-count'></span></span>",this.onGeoportalSubmit.bind(this)));this.sections.wms=this.tabs.getContentSection(b);b+=1;this.addInputText(this.sections.wms,"WMS-URL:",this.config["import"].wms_options);this.addButton(this.sections.wms,"<i class='netgis-icon fas fa-cloud-download-alt'></i><span>Dienst laden</span>",
this.onWMSLoadClick.bind(this));this.addInputText(this.sections.wms,"Bezeichnung:");this.addInputSelect(this.sections.wms,"Ebene:");this.addInputSelect(this.sections.wms,"Format:");this.addButton(this.sections.wms,"<i class='netgis-icon fas fa-check'></i><span>Hinzuf\u00fcgen</span>",this.onWMSAcceptClick.bind(this));this.showDetailsWMS(!1);this.sections.wfs=this.tabs.getContentSection(b);b+=1;this.addInputText(this.sections.wfs,"WFS-URL:",this.config["import"].wfs_options);this.addButton(this.sections.wfs,
"<i class='netgis-icon fas fa-cloud-download-alt'></i><span>Dienst laden</span>",this.onWFSLoadClick.bind(this));this.addInputText(this.sections.wfs,"Bezeichnung:");this.addInputSelect(this.sections.wfs,"Ebene:");this.addInputSelect(this.sections.wfs,"Format:");this.addButton(this.sections.wfs,"<i class='netgis-icon fas fa-check'></i><span>Hinzuf\u00fcgen</span>",this.onWFSAcceptClick.bind(this));this.showDetailsWFS(!1);this.sections.geojson=this.tabs.getContentSection(b);b+=1;this.addInputFile(this.sections.geojson,
"GeoJSON-Datei:",".geojson,.json");this.addText(this.sections.geojson,"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");this.addButton(this.sections.geojson,"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",this.onGeoJSONAcceptClick.bind(this));this.sections.gml=this.tabs.getContentSection(b);b+=1;this.addInputFile(this.sections.gml,"GML-Datei:",".gml,.xml");this.addText(this.sections.gml,
"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");this.addButton(this.sections.gml,"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",this.onGMLAcceptClick.bind(this));this.sections.geopackage=this.tabs.getContentSection(b);b+=1;this.addInputFile(this.sections.geopackage,"GeoPackage-Datei:",".gpkg");this.addText(this.sections.geopackage,"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");
this.addButton(this.sections.geopackage,"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",this.onGeoPackageAcceptClick.bind(this));this.sections.spatialite=this.tabs.getContentSection(b);b+=1;this.addInputFile(this.sections.spatialite,"Spatialite-Datei:",".sqlite");this.addText(this.sections.spatialite,"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");this.addButton(this.sections.spatialite,
"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",this.onSpatialiteAcceptClick.bind(this));this.sections.shapefile=this.tabs.getContentSection(b);this.addInputFile(this.sections.shapefile,"Shapefile-Zip-Datei:",".zip");this.addText(this.sections.shapefile,"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");this.addButton(this.sections.shapefile,"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",
this.onShapefileAcceptClick.bind(this))};
netgis.Import.prototype.initSections=function(a){this.sections={};var b=0;a["import"]&&!0===a["import"].geoportal_tab&&(this.sections.geoportal=this.tabs.getContentSection(b),b+=1,this.sections.geoportal.classList.add("netgis-geoportal"),this.geoportalSearch=new netgis.Search("Thema, Schlagwort..."),this.geoportalSearch.autocomplete=a["import"].geoportal_autocomplete,this.geoportalSearch.container.addEventListener(netgis.Events.SEARCH_CHANGE,this.onGeoportalSearchChange.bind(this)),this.geoportalSearch.container.addEventListener(netgis.Events.SEARCH_CLEAR,
this.onGeoportalSearchClear.bind(this)),this.geoportalSearch.attachTo(this.sections.geoportal),a=document.createElement("span"),a.innerHTML="Suche im Datenkatalog:",this.geoportalSearch.label.insertBefore(a,this.geoportalSearch.label.firstChild),a=document.createElement("button"),a.innerHTML="Suchen",a.className="netgis-color-a netgis-hover-c netgis-round netgis-shadow",a.setAttribute("type","button"),a.addEventListener("click",this.onGeoportalSearchButtonClick.bind(this)),this.geoportalSearch.label.appendChild(a),
this.geoportalLoader=document.createElement("div"),this.geoportalLoader.className="netgis-loader netgis-text-a netgis-hide",this.geoportalLoader.innerHTML="<i class='fas fa-cog'></i>",this.sections.geoportal.appendChild(this.geoportalLoader),this.geoportalResults=new netgis.Tree,this.geoportalResults.container.addEventListener(netgis.Events.TREE_ITEM_CHANGE,this.onGeoportalTreeItemChange.bind(this)),this.geoportalResults.attachTo(this.sections.geoportal),this.geoportalSubmit=this.addButton(this.sections.geoportal,
"<i class='netgis-icon fas fa-check'></i><span>Hinzuf\u00fcgen<span class='netgis-count'></span></span>",this.onGeoportalSubmit.bind(this)));this.sections.wms=this.tabs.getContentSection(b);b+=1;this.addInputText(this.sections.wms,"WMS-URL:",this.config["import"].wms_options);this.addButton(this.sections.wms,"<i class='netgis-icon fas fa-cloud-download-alt'></i><span>Dienst laden</span>",this.onWMSLoadClick.bind(this));this.addInputText(this.sections.wms,"Bezeichnung:");this.addInputSelect(this.sections.wms,
"Ebene:");this.addInputSelect(this.sections.wms,"Format:");this.addButton(this.sections.wms,"<i class='netgis-icon fas fa-check'></i><span>Hinzuf\u00fcgen</span>",this.onWMSAcceptClick.bind(this));this.showDetailsWMS(!1);this.sections.wfs=this.tabs.getContentSection(b);b+=1;this.addInputText(this.sections.wfs,"WFS-URL:",this.config["import"].wfs_options);this.addButton(this.sections.wfs,"<i class='netgis-icon fas fa-cloud-download-alt'></i><span>Dienst laden</span>",this.onWFSLoadClick.bind(this));
this.addInputText(this.sections.wfs,"Bezeichnung:");this.addInputSelect(this.sections.wfs,"Ebene:");this.addInputSelect(this.sections.wfs,"Format:");this.addButton(this.sections.wfs,"<i class='netgis-icon fas fa-check'></i><span>Hinzuf\u00fcgen</span>",this.onWFSAcceptClick.bind(this));this.showDetailsWFS(!1);this.sections.geojson=this.tabs.getContentSection(b);b+=1;this.addInputFile(this.sections.geojson,"GeoJSON-Datei:",".geojson,.json");this.addText(this.sections.geojson,"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");
this.addButton(this.sections.geojson,"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",this.onGeoJSONAcceptClick.bind(this));this.sections.gml=this.tabs.getContentSection(b);b+=1;this.addInputFile(this.sections.gml,"GML-Datei:",".gml,.xml");this.addText(this.sections.gml,"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");this.addButton(this.sections.gml,"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",
this.onGMLAcceptClick.bind(this));this.sections.geopackage=this.tabs.getContentSection(b);b+=1;this.addInputFile(this.sections.geopackage,"GeoPackage-Datei:",".gpkg");this.addText(this.sections.geopackage,"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");this.addButton(this.sections.geopackage,"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",this.onGeoPackageAcceptClick.bind(this));
this.sections.spatialite=this.tabs.getContentSection(b);b+=1;this.addInputFile(this.sections.spatialite,"Spatialite-Datei:",".sqlite");this.addText(this.sections.spatialite,"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");this.addButton(this.sections.spatialite,"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",this.onSpatialiteAcceptClick.bind(this));this.sections.shapefile=
this.tabs.getContentSection(b);this.addInputFile(this.sections.shapefile,"Shapefile-Zip-Datei:",".zip");this.addText(this.sections.shapefile,"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");this.addButton(this.sections.shapefile,"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",this.onShapefileAcceptClick.bind(this))};
netgis.Import.prototype.initPreview=function(){this.preview=new netgis.Modal("Vorschau");this.preview.attachTo(this.modal.content);this.previewMapContainer=document.createElement("div");this.previewMapContainer.className="netgis-preview-map";this.preview.content.appendChild(this.previewMapContainer);if(ol){var a=this.config.map;a={projection:a.projection,center:a.centerLonLat?ol.proj.fromLonLat(a.centerLonLat):a.center,zoom:a.zoom};this.previewMap=new ol.Map({target:this.previewMapContainer,view:new ol.View(a),
pixelRatio:1,moveTolerance:3,controls:[]});this.previewMap.getView().padding=[10,10,10,10];this.previewMap.addLayer(new ol.layer.Tile({source:new ol.source.OSM}))}this.previewTree=new netgis.Tree;this.previewTree.container.classList.add("netgis-preview-tree");this.previewTree.attachTo(this.preview.content);this.previewTree.container.addEventListener(netgis.Events.TREE_ITEM_CHANGE,this.onPreviewTreeItemChange.bind(this));this.previewSubmit=document.createElement("button");this.previewSubmit.setAttribute("type",
"button");this.previewSubmit.className="netgis-import-submit netgis-button netgis-center netgis-color-a netgis-hover-c netgis-shadow";this.previewSubmit.innerHTML="<i class='netgis-icon fas fa-check'></i><span>Hinzuf\u00fcgen</span>";this.previewSubmit.addEventListener("click",this.onPreviewSubmitClick.bind(this));this.preview.content.appendChild(this.previewSubmit)};
@@ -137,16 +134,17 @@ netgis.Import.prototype.onSpatialiteAcceptClick=function(a){if(a=this.sections.s
netgis.Import.prototype.onSpatialiteLoad=function(a){var b=a.target;a=b.result;var c="import_"+netgis.util.getTimeStamp(!0);b=b.title;a={id:c,folder:null,active:!0,order:this.getLayerOrder(),style:this.config.styles["import"],title:b,type:netgis.LayerTypes.SPATIALITE,data:a};this.submitImportLayer(a)};
netgis.Import.prototype.onShapefileAcceptClick=function(a){if(a=this.sections.shapefile.getElementsByTagName("input")[0].files[0]){var b=new FileReader;b.title=a.name;b.onload=this.onShapefileLoad.bind(this);b.readAsArrayBuffer(a)}else alert("No file selected!")};
netgis.Import.prototype.onShapefileLoad=function(a){var b=a.target;a=b.result;var c="import_"+netgis.util.getTimeStamp(!0);b=b.title;a={id:c,folder:null,active:!0,order:this.getLayerOrder(),style:this.config.styles["import"],title:b,type:netgis.LayerTypes.SHAPEFILE,data:a};this.submitImportLayer(a)};
netgis.Import.prototype.onImportPreviewFeatures=function(a){var b=a.detail,c=b.layer;this.previewTree.clear();a=this.previewMap.getLayers().getArray();for(var d=1;d<a.length;d++)this.previewMap.removeLayer(a[d]);if(ol){a=config.styles["import"];a=new ol.style.Style({fill:new ol.style.Fill({color:a.fill}),stroke:new ol.style.Stroke({color:a.stroke,width:a.width})});c.setStyle(a);this.previewMap.addLayer(c);var e=this.previewTree.addFolder(null,b.id,b.title);a=c.getSource().getFeatures();if(0===a.length){var f=
this;c.getSource().on("addfeature",function(a){f.featureLoadTimeout&&window.clearTimeout(f.featureLoadTimeout);f.featureLoadTimeout=window.setTimeout(function(){var a=c.getSource().getFeatures();f.updatePreviewFeatures(a,e,c,b);f.featureLoadTimeout=null},100)})}else this.updatePreviewFeatures(a,e,c,b)}else console.error("import preview only supported with OL map renderer",c)};
netgis.Import.prototype.onImportPreviewFeatures=function(a){var b=a.detail,c=b.layer;this.previewTree.clear();a=this.previewMap.getLayers().getArray();for(var d=1;d<a.length;d++)this.previewMap.removeLayer(a[d]);if(ol){a=this.config.styles["import"];a=new ol.style.Style({fill:new ol.style.Fill({color:a.fill}),stroke:new ol.style.Stroke({color:a.stroke,width:a.width})});c.setStyle(a);this.previewMap.addLayer(c);var e=this.previewTree.addFolder(null,b.id,b.title);a=c.getSource().getFeatures();if(0===
a.length){var f=this;c.getSource().on("addfeature",function(a){f.featureLoadTimeout&&window.clearTimeout(f.featureLoadTimeout);f.featureLoadTimeout=window.setTimeout(function(){var a=c.getSource().getFeatures();f.updatePreviewFeatures(a,e,c,b);f.featureLoadTimeout=null},100)})}else this.updatePreviewFeatures(a,e,c,b)}else console.error("import preview only supported with OL map renderer",c)};
netgis.Import.prototype.updatePreviewFeatures=function(a,b,c,d){for(var e=0;e<a.length;e++){var f=a[e],g=f.getId(),h=f.getProperties();g||(g=e+1,f.setId(g));var k=null,l;for(l in h)switch(l.toLowerCase()){case "name":k=h[l];break;case "title":k=h[l];break;case "id":k=h[l];break;case "gid":k=h[l];break;case "oid":k=h[l];break;case "objectid":k=h[l]}k||(k=g);h=f.getGeometry();if(h instanceof ol.geom.Polygon||h instanceof ol.geom.MultiPolygon)k+=" ("+netgis.util.formatArea(h.getArea())+")";else if(h instanceof
ol.geom.LineString)k+=" ("+netgis.util.formatArea(h.getLength())+")";else if(h instanceof ol.geom.MultiLineString){f=0;h=h.getLineStrings();for(var m=0;m<h.length;m++)f+=h[m].getLength();k+=" ("+netgis.util.formatArea(f)+")"}this.previewTree.addCheckbox(b,g,"Feature "+k,!0)}this.previewTree.setFolderOpen(d.id,!0);this.previewTree.updateFolderChecks();this.preview.show();this.previewMap.updateSize();this.previewMap.getView().fit(c.getSource().getExtent())};
netgis.Import.prototype.onPreviewSubmitClick=function(a){var b=this.previewTree.container.getElementsByClassName("netgis-folder")[0];a=b.getAttribute("data-id");b=b.getElementsByTagName("span")[0].innerText;for(var c=this.previewTree.container.getElementsByTagName("input"),d=this.previewMap.getLayers().getArray()[1].getSource(),e=[],f=1;f<c.length;f++){var g=c[f];g.checked&&(g=g.getAttribute("data-id"),g=d.getFeatureById(g),e.push(g))}c=(new ol.format.GeoJSON).writeFeaturesObject(e);d=this.previewMap.getView().getProjection().getCode();
c.crs={type:"name",properties:{name:"urn:ogc:def:crs:"+d.replace(":","::")}};a={id:a,folder:null,active:!0,order:this.getLayerOrder(),style:this.config.styles["import"],title:b,type:netgis.LayerTypes.GEOJSON,data:c};this.config.layers.push(a);netgis.util.invoke(this.preview.container,netgis.Events.IMPORT_LAYER_ACCEPT,a);this.preview.hide();this.modal.hide()};
c.crs={type:"name",properties:{name:"urn:ogc:def:crs:"+d.replace(":","::")}};a={id:a,folder:null,active:!0,editable:this.config["import"]&&!0===this.config["import"].editable,order:this.getLayerOrder(),style:this.config.styles["import"],title:b,type:netgis.LayerTypes.GEOJSON,data:c};this.config.layers.push(a);netgis.util.invoke(this.preview.container,netgis.Events.IMPORT_LAYER_ACCEPT,a);this.preview.hide();this.modal.hide()};
netgis.Import.prototype.onPreviewTreeItemChange=function(a){a=a.detail;var b=this.previewMap.getLayers().getArray()[1].getSource().getFeatureById(a.id);a.checked?b.setStyle(null):b.setStyle(new ol.style.Style({}))};netgis.Import.prototype.onGeoportalSearchKeyUp=function(a){switch(a.keyCode){case 13:break;case 27:break;default:this.onGeoportalSearchChange()}};
netgis.Import.prototype.onGeoportalSearchChange=function(a){a=a.detail.query;if(0<a.length){this.geoportalLoader.classList.remove("netgis-hide");a=netgis.util.replace(a," ",",");var b=this.config["import"].geoportal_search_url;b=netgis.util.replace(b,"{query}",window.encodeURIComponent(a));netgis.util.request(b,this.onGeoportalSearchResponse.bind(this));this.geoportalSearch.showClearButton(!0)}};netgis.Import.prototype.onGeoportalSearchClear=function(a){this.geoportalResults.clear()};
netgis.Import.prototype.onGeoportalSearchResponse=function(a){function b(a){if(a&&a.layer)for(var c=0;c<a.layer.length;c++)b(a.layer[c]);else a&&e.push(a);return e.length}a=JSON.parse(a);this.geoportalResults.clear();this.geoportalDataRaw=a=a.wms.srv;this.geoportalData=[];for(var c=0;c<a.length;c++){var d=a[c];var e=[];var f=b(d);f=this.geoportalResults.addFolder(null,c,d.title+" ("+f+")");f.setAttribute("title",d["abstract"]);for(var g=0;g<e.length;g++)this.geoportalResults.addCheckbox(f,g,e[g].title);
d.children=e;this.geoportalData.push(d)}this.geoportalLoader.classList.add("netgis-hide")};netgis.Import.prototype.onGeoportalTreeItemChange=function(a){a=this.geoportalResults.container.getElementsByClassName("netgis-item");for(var b=0,c=0;c<a.length;c++)a[c].getElementsByTagName("input")[0].checked&&(b+=1);this.geoportalSubmit.getElementsByClassName("netgis-count")[0].innerHTML=0===b?"":" ("+b+")"};
d.children=e;this.geoportalData.push(d)}this.geoportalLoader.classList.add("netgis-hide")};netgis.Import.prototype.onGeoportalSearchButtonClick=function(a){this.geoportalSearch.onInputTimeout()};
netgis.Import.prototype.onGeoportalTreeItemChange=function(a){a=this.geoportalResults.container.getElementsByClassName("netgis-item");for(var b=0,c=0;c<a.length;c++)a[c].getElementsByTagName("input")[0].checked&&(b+=1);this.geoportalSubmit.getElementsByClassName("netgis-count")[0].innerHTML=0===b?"":" ("+b+")"};
netgis.Import.prototype.onGeoportalSubmit=function(a){a=this.geoportalResults.container.getElementsByClassName("netgis-item");for(var b=0,c=0;c<a.length;c++){var d=a[c],e=d.getElementsByTagName("input")[0];if(e.checked){b+=1;e=e.getAttribute("data-id");d=d.parentNode.parentNode.parentNode.getAttribute("data-id");var f=this.geoportalData[d],g=f.children[e],h=f.title;f=f.getMapUrl;var k=g.name;g=g.title;d="geoportal_"+d;e=d+"_"+e;netgis.util.invoke(this.sections.geoportal,netgis.Events.IMPORT_GEOPORTAL_SUBMIT,
{folder:{id:d,title:h},layer:{id:e,url:f,name:k,title:g}})}}0<b&&this.modal.hide()};netgis=netgis||{};netgis.Info=function(a){this.config=a;this.queryLayers={};this.popup=new netgis.Popup;this.popup.setHeader("Abfrage");this.initConfig(a)};netgis.Info.Config={default_format:"text/plain",proxy:""};netgis.Info.prototype.initConfig=function(a){a=a.layers;for(var b=a.length-1;0<=b;b--){var c=a[b];!0===c.active&&this.isLayerQueryable(c)?this.queryLayers[c.id]=c:this.queryLayers[c.id]&&delete this.queryLayers[c.id]}};
netgis.Info.prototype.attachTo=function(a){this.popup.attachTo(a);a.addEventListener(netgis.Events.CLIENT_CONTEXT_RESPONSE,this.onClientContextResponse.bind(this));a.addEventListener(netgis.Events.CLIENT_SET_MODE,this.onClientSetMode.bind(this));a.addEventListener(netgis.Events.MAP_LAYER_TOGGLE,this.onMapLayerToggle.bind(this));a.addEventListener(netgis.Events.MAP_LAYER_CREATE,this.onMapLayerCreate.bind(this));a.addEventListener(netgis.Events.MAP_LAYER_DELETE,this.onMapLayerDelete.bind(this));a.addEventListener(netgis.Events.IMPORT_LAYER_ACCEPT,
@@ -154,24 +152,24 @@ this.onImportLayerAccept.bind(this));a.addEventListener(netgis.Events.IMPORT_GEO
netgis.Info.prototype.addSection=function(a,b,c){this.popup.addContent([!0===c?"<details open='open'>":"<details>","<summary class='netgis-button netgis-noselect netgis-clip-text netgis-color-d netgis-hover-text-a netgis-hover-d'>",a,"</summary><div class='netgis-border-d'>",b,"</div></details>"].join(""))};netgis.Info.prototype.onClientContextResponse=function(a){this.initConfig(a.detail.context.config)};netgis.Info.prototype.onClientSetMode=function(a){this.popup.hide()};
netgis.Info.prototype.onMapLayerToggle=function(a){var b=a.detail;a=b.id;if(b.on){b=this.config.layers;for(var c=null,d=0;d<b.length;d++)if(b[d].id===a){c=b[d];break}c&&this.isLayerQueryable(c)&&(this.queryLayers[a]=c)}else delete this.queryLayers[a]};netgis.Info.prototype.onMapLayerCreate=function(a){a=a.detail;this.isLayerQueryable(a)&&(this.queryLayers[a.id]=a)};netgis.Info.prototype.onMapLayerDelete=function(a){delete this.queryLayers[a.detail.id]};
netgis.Info.prototype.onImportLayerAccept=function(a){a=a.detail;this.isLayerQueryable(a)&&(this.queryLayers[a.id]=a)};netgis.Info.prototype.onImportGeoportalSubmit=function(a){};
netgis.Info.prototype.onMapClick=function(a){a=a.detail;if(a.mode===netgis.Modes.SEARCH_PARCEL)this.popup.clearContent(),this.popup.hide();else if(a.mode===netgis.Modes.VIEW){var b=this.config.info;this.popup.container!==a.overlay&&this.popup.attachTo(a.overlay);this.popup.clearContent();var c=0,d;for(d in this.queryLayers){var e=this.queryLayers[d];if(a.info&&a.info[d]){var f=a.info[d];b.proxy&&0<b.proxy.length&&(f=b.proxy+f);netgis.util.request(f,this.onLayerResponseWMS.bind(this),{title:e.title});
netgis.Info.prototype.onMapClick=function(a){a=a.detail;if(a.mode===netgis.Modes.SEARCH_PARCEL)this.popup.clearContent(),this.popup.hide();else if(a.mode===netgis.Modes.VIEW){var b=this.config.info;this.popup.container!==a.overlay&&this.popup.attachTo(a.overlay);this.popup.clearContent();var c=0,d;for(d in this.queryLayers){var e=this.queryLayers[d];if(a.info&&a.info[d]){var f=a.info[d];b&&b.proxy&&0<b.proxy.length&&(f=b.proxy+f);netgis.util.request(f,this.onLayerResponseWMS.bind(this),{title:e.title});
c+=1}else{if(!e.query_url||""===e.query_url)switch(e.type){case netgis.LayerTypes.WMS:case netgis.LayerTypes.WMST:f=e.url;var g=["SERVICE=WMS","VERSION=1.1.1","REQUEST=GetFeatureInfo","STYLES=","LAYERS="+window.encodeURIComponent(e.name),"QUERY_LAYERS="+window.encodeURIComponent(e.name),"BBOX="+a.view.bbox.join(","),"SRS="+a.view.projection,"WIDTH="+a.view.width,"HEIGHT="+a.view.height,"X="+Math.round(a.pixel[0]),"Y="+Math.round(a.pixel[1]),"INFO_FORMAT="+(b&&b.default_format?b.default_format:"text/plain")];
f=f+(-1===f.indexOf("?")?"?":"")+g.join("&");b.proxy&&0<b.proxy.length&&(f=b.proxy+f);netgis.util.request(f,this.onLayerResponseWMS.bind(this),{title:e.title});c+=1}(f=e.query_url)&&""!==f&&(f=netgis.util.replace(f,"{bbox}",a.view.bbox.join(",")),f=netgis.util.replace(f,"{proj}",a.view.projection),f=netgis.util.replace(f,"{width}",a.view.width),f=netgis.util.replace(f,"{height}",a.view.height),f=netgis.util.replace(f,"{x}",a.coords[0]),f=netgis.util.replace(f,"{y}",a.coords[1]),f=netgis.util.replace(f,
"{px}",a.pixel[0]),f=netgis.util.replace(f,"{py}",a.pixel[1]),f=netgis.util.replace(f,"{lon}",a.lon),f=netgis.util.replace(f,"{lat}",a.lat),b.proxy&&0<b.proxy.length&&(f=b.proxy+f),netgis.util.request(f,this.onLayerResponseWMS.bind(this),{title:e.title}),c+=1)}}0<c?(this.popup.showLoader(),this.popup.show()):this.popup.hide()}};
f=f+(-1===f.indexOf("?")?"?":"")+g.join("&");b&&b.proxy&&0<b.proxy.length&&(f=b.proxy+f);netgis.util.request(f,this.onLayerResponseWMS.bind(this),{title:e.title});c+=1}(f=e.query_url)&&""!==f&&(f=netgis.util.replace(f,"{bbox}",a.view.bbox.join(",")),f=netgis.util.replace(f,"{proj}",a.view.projection),f=netgis.util.replace(f,"{width}",a.view.width),f=netgis.util.replace(f,"{height}",a.view.height),f=netgis.util.replace(f,"{x}",a.coords[0]),f=netgis.util.replace(f,"{y}",a.coords[1]),f=netgis.util.replace(f,
"{px}",a.pixel[0]),f=netgis.util.replace(f,"{py}",a.pixel[1]),f=netgis.util.replace(f,"{lon}",a.lon),f=netgis.util.replace(f,"{lat}",a.lat),b&&b.proxy&&0<b.proxy.length&&(f=b.proxy+f),netgis.util.request(f,this.onLayerResponseWMS.bind(this),{title:e.title}),c+=1)}}0<c?(this.popup.showLoader(),this.popup.show()):this.popup.hide()}};
netgis.Info.prototype.onMapFeatureClick=function(a){var b=a.detail,c=b.properties;if(b.mode===netgis.Modes.SEARCH_PARCEL)this.popup.clearContent(),this.popup.hide();else{a=null;var d=[],e="geometry fill fill-opacity stroke stroke-opacity stroke-width styleUrl".split(" ");for(g in c)if(!(-1<e.indexOf(g))){var f=c[g];d.push([g,f]);a||("name"===g&&""!==f?a=f:"title"===g&&""!==f?a=f:"id"===g&&f&&(a=f))}!a&&b.id&&(a=b.id);a=a?'Feature "'+a+'"':"Feature";"geolocation"===b.id&&((a=this.config.geolocation.marker_title)&&
""!==a||(a="Geolocation"),d.push(["L\u00e4ngengrad (Lon.)",b.lon]),d.push(["Breitengrad (Lat.)",b.lat]));b=[];if(0<d.length){b.push("<table>");for(c=0;c<d.length;c++){f=d[c];var g=f[0];f=f[1];b.push("<tr class='netgis-hover-d'>");b.push("<th>"+g+"</th>");b.push("<td>"+f+"</td>");b.push("</tr>")}b.push("</table>")}else b.push("<i>Keine Eigenschaften vorhanden...</i>");b=b.join("");this.addSection(a,b,!1);!this.popup.isVisible()&&this.popup.show()}};
netgis.Info.prototype.onLayerResponseWMS=function(a,b,c){b=b.title;if(c=c.getResponseHeader("Content-Type"))switch(c.split(";")[0]){case "text/plain":a="<pre>"+a+"</pre>"}this.popup.hideLoader();this.addSection(b,a,!1)};netgis=netgis||{};netgis.LayerID={EDITABLE:"editable-layer",NON_EDITABLE:"non-editable-layer"};netgis=netgis||{};netgis.LayerTree=function(a){this.config=a;this.importFolder=null;this.initElements(a);this.initFolders();this.initConfig(a)};netgis.LayerTree.Config={open:!1,title:"Layers",buttons:[]};netgis.LayerTree.Folders=[];
netgis.Info.prototype.onLayerResponseWMS=function(a,b,c){b=b.title;if(c=c.getResponseHeader("Content-Type"))switch(c.split(";")[0]){case "text/plain":a="<pre>"+a+"</pre>"}this.popup.hideLoader();this.addSection(b,a,!1)};netgis=netgis||{};netgis.LayerID={EDITABLE:"editable-layer",NON_EDITABLE:"non-editable-layer"};netgis=netgis||{};netgis.LayerTree=function(a){this.config=a;this.importFolder=null;this.initElements(a);this.initFolders();this.initConfig(a)};netgis.LayerTree.Config={open:!1,title:"Layers",draggable:!1,buttons:[]};netgis.LayerTree.Folders=[];
netgis.LayerTree.prototype.initElements=function(a){a=a.layertree;this.panel=new netgis.Panel("Layers");this.tree=new netgis.Tree(a.draggable);this.tree.attachTo(this.panel.content);this.tree.container.addEventListener(netgis.Events.TREE_ITEM_CHANGE,this.onTreeItemChange.bind(this));this.tree.container.addEventListener(netgis.Events.TREE_ITEM_SLIDER_CHANGE,this.onTreeItemSliderChange.bind(this));this.tree.container.addEventListener(netgis.Events.TREE_ITEM_ORDER_CHANGE,this.onTreeItemOrderChange.bind(this));
!0===a.draggable&&(this.panel.content.addEventListener("dragover",this.onDragOver.bind(this)),this.panel.content.addEventListener("drop",this.onDragDrop.bind(this)))};
netgis.LayerTree.prototype.initFolders=function(){this.editFolder=this.tree.addFolder(null,"edit-folder","Zeichnung",!0,!0);this.tree.addCheckbox(this.editFolder,netgis.LayerID.EDITABLE,"Editierbar");this.tree.setItemChecked(netgis.LayerID.EDITABLE,!0);this.tree.addCheckbox(this.editFolder,netgis.LayerID.NON_EDITABLE,"Nicht-Editierbar");this.tree.setItemChecked(netgis.LayerID.NON_EDITABLE,!0);this.editFolder.classList.add("netgis-hide")};
netgis.LayerTree.prototype.initConfig=function(a,b){var c=a.layertree;c&&c.title&&this.panel.setTitle(c.title);var d=a.folders,e={};if(d){for(var f=0;f<d.length;f++){var g=d[f],h=this.tree.addFolder(null,g.id,g.title,b,!1,g.draggable);e[g.id]=h;!0===g.open&&this.tree.setFolderOpen(g.id,!0)}for(f=0;f<d.length;f++)g=d[f],h=g.id,g=g.parent,-1===g&&(g=null),""===g&&(g=null),g&&this.tree.setFolderParent(e[h],e[g])}if(b=a.layers)for(f=0;f<b.length;f++){var k=b[f];g=e[k.folder]?e[k.folder]:null;h=k.id?k.id:
f.toString();var l=!1;if(g)for(var m=0;m<d.length;m++){var n=d[m];n.id===k.folder&&(l=n.radio)}m=k.title;n='<i class="fas fa-mouse-pointer" title="Ebene ist abfragbar"></i>';a.layertree&&a.layertree.query_icon&&(n=a.layertree.query_icon);(!0===k.query||k.query_url&&""!==k.query_url)&&n&&""!==n&&(m+='<span class="netgis-right">'+n+"</span>");h=!0===l?this.tree.addRadioButton(g,h,m,k.active,this.createDefaultDetails(k,!0,!1)):this.tree.addCheckbox(g,h,m,k.active,!1,this.createDefaultDetails(k,!0,!0));
h.addEventListener("contextmenu",this.onTreeItemMenu.bind(this));!0===k.hidden&&h.classList.add("netgis-hide")}this.tree.updateFolderChecks();if(c&&c.buttons)for(a=a.layertree.buttons,f=0;f<a.length;f++)d=a[f],this.tree.addButton(null,d.id,d.title,this.onTreeButtonClick.bind(this));c&&!0===c.open&&this.panel.show()};
f.toString();var l=!1;if(g)for(var m=0;m<d.length;m++){var n=d[m];n.id===k.folder&&(l=n.radio)}m=k.title;n='<i class="fas fa-mouse-pointer" title="Ebene ist abfragbar"></i>';a.layertree&&a.layertree.query_icon&&(n=a.layertree.query_icon);(!0===k.query||k.query_url&&""!==k.query_url)&&n&&""!==n&&(m+='<span class="netgis-right">'+n+"</span>");n=k.removable;h=!0===l?this.tree.addRadioButton(g,h,m,k.active,this.createDefaultDetails(k,!0,n)):this.tree.addCheckbox(g,h,m,k.active,!1,this.createDefaultDetails(k,
!0,n));h.addEventListener("contextmenu",this.onTreeItemMenu.bind(this));!0===k.hidden&&h.classList.add("netgis-hide")}this.tree.updateFolderChecks();if(c&&c.buttons)for(a=a.layertree.buttons,f=0;f<a.length;f++)d=a[f],this.tree.addButton(null,d.id,d.title,this.onTreeButtonClick.bind(this));c&&!0===c.open&&this.panel.show()};
netgis.LayerTree.prototype.attachTo=function(a){this.panel.attachTo(a);a.addEventListener(netgis.Events.CLIENT_CONTEXT_RESPONSE,this.onClientContextResponse.bind(this));a.addEventListener(netgis.Events.LAYERTREE_TOGGLE,this.onLayerTreeToggle.bind(this));a.addEventListener(netgis.Events.MAP_LAYER_CREATE,this.onMapLayerCreate.bind(this));a.addEventListener(netgis.Events.MAP_LAYER_TOGGLE,this.onMapLayerToggle.bind(this));a.addEventListener(netgis.Events.IMPORT_LAYER_ACCEPT,this.onImportLayerAccept.bind(this));
a.addEventListener(netgis.Events.IMPORT_GEOPORTAL_SUBMIT,this.onImportGeoportalSubmit.bind(this));a.addEventListener(netgis.Events.CONTEXTMENU_SLIDER_CHANGE,this.onContextMenuSliderChange.bind(this));a.addEventListener(netgis.Events.MAP_EDIT_LAYER_LOADED,this.onMapEditLayerChange.bind(this));a.addEventListener(netgis.Events.MAP_EDIT_LAYER_CHANGE,this.onMapEditLayerChange.bind(this))};
netgis.LayerTree.prototype.createDefaultDetails=function(a,b,c){var d=[];!0===b&&d.push({title:"<i class='netgis-icon fas fa-eye-slash'></i> Transparenz:",type:"slider",val:a.transparency?Math.round(100*a.transparency):0});!0===c&&d.push({title:"<i class='netgis-icon fas fa-times'></i> Entfernen",type:"button",callback:this.onTreeItemDeleteClick.bind(this)});return d};
netgis.LayerTree.prototype.onTreeItemChange=function(a){var b=a.detail;netgis.util.invoke(a.target,netgis.Events.MAP_LAYER_TOGGLE,{id:b.id,on:b.checked})};netgis.LayerTree.prototype.onClientContextResponse=function(a){this.initConfig(a.detail.context.config,!0)};netgis.LayerTree.prototype.onLayerTreeToggle=function(a){this.panel.toggle()};netgis.LayerTree.prototype.onTreeButtonClick=function(a){a=a.currentTarget;var b=a.getAttribute("data-id");netgis.Client.handleCommand(a,b)};
netgis.LayerTree.prototype.onMapLayerCreate=function(a){this.addLayerItem(a.detail,null)};netgis.LayerTree.prototype.onMapLayerToggle=function(a){a=a.detail;this.tree.setItemChecked(a.id,a.on,!0);this.tree.updateFolderChecks()};netgis.LayerTree.prototype.onImportLayerAccept=function(a){a=a.detail;this.importFolder||(this.importFolder=this.tree.addFolder(null,"_import","Import",!0,!1));this.addLayerItem(a,this.importFolder);this.tree.updateFolderChecks()};
netgis.LayerTree.prototype.onMapLayerCreate=function(a){this.addLayerItem(a.detail,null)};netgis.LayerTree.prototype.onMapLayerToggle=function(a){a=a.detail;this.tree.setItemChecked(a.id,a.on,!0);this.tree.updateFolderChecks()};netgis.LayerTree.prototype.onImportLayerAccept=function(a){a=a.detail;!0!==a.editable&&(this.importFolder||(this.importFolder=this.tree.addFolder(null,"_import","Import",!0,!1)),this.addLayerItem(a,this.importFolder),this.tree.updateFolderChecks())};
netgis.LayerTree.prototype.addLayerItem=function(a,b){var c=a.title,d='<i class="fas fa-mouse-pointer" title="Ebene ist abfragbar"></i>';config.layertree&&config.layertree.query_icon&&(d=config.layertree.query_icon);(!0===a.query||a.query_url&&""!==a.query_url)&&d&&""!==d&&(c+='<span class="netgis-right">'+d+"</span>");this.tree.addCheckbox(b,a.id,c,!0,!0,this.createDefaultDetails(a,!0,!0)).addEventListener("contextmenu",this.onTreeItemMenu.bind(this))};
netgis.LayerTree.prototype.onImportGeoportalSubmit=function(a){a=a.detail;var b=a.folder.id,c=this.tree.getFolder(b);c||(c=this.tree.addFolder(null,b,a.folder.title,!0,!1));var d=a.layer.id;b={id:d,folder:b,title:a.layer.title,active:!0,type:netgis.LayerTypes.WMS,url:a.layer.url,name:a.layer.name,order:1E4,transparency:0};this.config.layers.push(b);this.tree.addCheckbox(c,d,a.layer.title,!1,!1,this.createDefaultDetails(b,!0,!0));this.tree.setItemChecked(d,!0,!1)};
netgis.LayerTree.prototype.onTreeItemMenu=function(a){a.preventDefault();return!1};netgis.LayerTree.prototype.onContextMenuSliderChange=function(a){var b=a.detail;a=.01*b.val;var c=null;0===b.id.indexOf("layer_trans_")&&(c=b.id.split("layer_trans_")[1]);b=this.config.layers;for(var d=0;d<b.length;d++){var e=b[d];if(e.id===c){e.transparency=a;break}}netgis.util.invoke(this.tree.container,netgis.Events.MAP_LAYER_TRANSPARENCY,{id:c,transparency:a})};
@@ -264,11 +262,11 @@ netgis.Map.prototype.gmlParseCoordinates=function(a,b){a=a.split(" ");for(var c=
netgis.Map.prototype.createLayerGeoPackage=function(a){var b=new ol.layer.Vector({source:new ol.source.Vector({features:[]})}),c=this;a=new Uint8Array(a);window.GeoPackage.setSqljsWasmLocateFile(function(a){return c.config["import"].geopackage_lib+a});window.GeoPackage.GeoPackageAPI.open(a).then(function(a){for(var d=[],f=new ol.format.GeoJSON,g=a.getFeatureTables(),h=0;h<g.length;h++)for(var k=a.queryForGeoJSONFeaturesInTable(g[h]),l=0;l<k.length;l++){var m=f.readGeometry(k[l].geometry,{featureProjection:c.view.getProjection()});
m=new ol.Feature({geometry:m});d.push(m)}b.getSource().addFeatures(d)});return b};
netgis.Map.prototype.createLayerSpatialite=function(a){var b=new ol.layer.Vector({source:new ol.source.Vector({features:[]})}),c=this;window.initSqlJs().then(function(d){var e=[],f=new Uint8Array(a);d=new d.Database(f);var g=d.exec("SELECT name FROM sqlite_schema WHERE type = 'table' \n\t\t\t\t\tAND name NOT LIKE 'sqlite_%' \n\t\t\t\t\tAND name NOT LIKE 'sql_%' \n\t\t\t\t\tAND name NOT LIKE 'idx_%' \n\t\t\t\t\tAND name NOT LIKE 'spatial_ref_sys%' \n\t\t\t\t\tAND name NOT LIKE 'spatialite_%' \n\t\t\t\t\tAND name NOT LIKE 'geometry_columns%' \n\t\t\t\t\tAND name NOT LIKE 'views_%' \n\t\t\t\t\tAND name NOT LIKE 'virts_%' \n\t\t\t\t\tAND name NOT LIKE 'SpatialIndex' \n\t\t\t\t\tAND name NOT LIKE 'KNN%' \n\t\t\t\t\tAND name NOT LIKE 'ElementaryGeometries' \n\t\t\t\t;");f=
g[0].values;for(var h=0;h<f.length;h++){g=d.exec("SELECT * FROM "+f[h][0]);var k=g[0];g=null;for(var l=0;l<k.columns.length;l++){if("geometry"===k.columns[l].toLowerCase()){g=l;break}if("geom"===k.columns[l].toLowerCase()){g=l;break}}if(null!==g)for(k=k.values,l=0;l<k.length;l++){var m=k[l][g],n=new Uint8Array(m.length-43-1+5);n[0]=m[1];n[1]=m[39];n[2]=m[40];n[3]=m[41];n[4]=m[42];for(var p=m.length-43-1,q=0;q<p;q++)n[5+q]=m[43+q];m=(new ol.format.WKB).readGeometry(n,{featureProjection:c.view.getProjection()});
g[0].values;for(var h=0;h<f.length;h++){g=d.exec("SELECT * FROM "+f[h][0]);var k=g[0];g=null;for(var l=0;l<k.columns.length;l++){if("geometry"===k.columns[l].toLowerCase()){g=l;break}if("geom"===k.columns[l].toLowerCase()){g=l;break}}if(null!==g)for(k=k.values,l=0;l<k.length;l++){var m=k[l][g],n=new Uint8Array(m.length-43-1+5);n[0]=m[1];n[1]=m[39];n[2]=m[40];n[3]=m[41];n[4]=m[42];for(var q=m.length-43-1,p=0;p<q;p++)n[5+p]=m[43+p];m=(new ol.format.WKB).readGeometry(n,{featureProjection:c.view.getProjection()});
e.push(new ol.Feature({geometry:m}))}}b.getSource().addFeatures(e)});return b};netgis.Map.prototype.createLayerShapefile=function(a){var b=new ol.layer.Vector({source:new ol.source.Vector({features:[]})}),c=this;shp(a).then(function(a){var d=new ol.format.GeoJSON;d.readProjection(a);a=d.readFeatures(a,{featureProjection:c.view.getProjection()});b.getSource().addFeatures(a)});return b};
netgis.Map.prototype.createLayerWKT=function(a){for(var b=new ol.format.WKT,c=[],d=0;d<a.length;d++){var e=a[d],f=b.readGeometry(e.geometry),g=e.properties;g.geometry=f;g.wkt=e.geometry;f=new ol.Feature(g);f.setId(e.id);c.push(f)}return new ol.layer.Vector({source:new ol.source.Vector({features:c})})};
netgis.Map.prototype.createLayerWFS=function(a,b,c,d,e,f){"?"!==a[a.length-1]&&(a+="?");a+="service=WFS&version=1.1.0&request=GetFeature";c||(c=this.view.getProjection().getCode());d=d?netgis.util.replace(d," ","+"):"application/json";var g=new ol.source.Vector({format:new ol.format.GeoJSON,strategy:ol.loadingstrategy.bbox,loader:function(h,k,n,p,q){h=a+"&typename="+b+"&srsname="+c+"&bbox="+h.join(",")+","+c+"&outputFormat="+d;var l=new XMLHttpRequest;l.open("GET",h);e&&f&&l.setRequestHeader("Authorization",
"Basic "+window.btoa(e+":"+f));l.onerror=function(){console.error("WFS request error");q()};l.onload=function(){if(200===l.status){g.clear();var a=g.getFormat().readFeatures(l.responseText);g.addFeatures(a);p(a)}else console.error("WFS request status",l.status),q()};l.send()}}),h=new ol.layer.Vector({source:g}),k=this;g.on("featuresloadstart",function(a){k.removeSnapLayer(h)});g.on("featuresloadend",function(a){window.setTimeout(function(){k.addSnapLayer(h)},10)});return h};
netgis.Map.prototype.createLayerWFS=function(a,b,c,d,e,f){"?"!==a[a.length-1]&&(a+="?");a+="service=WFS&version=1.1.0&request=GetFeature";c||(c=this.view.getProjection().getCode());d=d?netgis.util.replace(d," ","+"):"application/json";var g=new ol.source.Vector({format:new ol.format.GeoJSON,strategy:ol.loadingstrategy.bbox,loader:function(h,k,n,q,p){h=a+"&typename="+b+"&srsname="+c+"&bbox="+h.join(",")+","+c+"&outputFormat="+d;var l=new XMLHttpRequest;l.open("GET",h);e&&f&&l.setRequestHeader("Authorization",
"Basic "+window.btoa(e+":"+f));l.onerror=function(){console.error("WFS request error");p()};l.onload=function(){if(200===l.status){g.clear();var a=g.getFormat().readFeatures(l.responseText);g.addFeatures(a);q(a)}else console.error("WFS request status",l.status),p()};l.send()}}),h=new ol.layer.Vector({source:g}),k=this;g.on("featuresloadstart",function(a){k.removeSnapLayer(h)});g.on("featuresloadend",function(a){window.setTimeout(function(){k.addSnapLayer(h)},10)});return h};
netgis.Map.prototype.createLayerVectorTiles=function(a,b,c,d){return new ol.layer.VectorTile({extent:b,source:new ol.source.VectorTile({format:new ol.format.MVT,overlaps:!0,url:a,minZoom:c,maxZoom:d})})};
netgis.Map.prototype.createLayerKML=function(a){var b=new ol.layer.Vector({source:new ol.source.Vector({features:[]})}),c=this;netgis.util.request(a,function(a){a=(new ol.format.KML).readFeatures(a,{featureProjection:c.view.getProjection()});for(var d=0;d<a.length;d++){var f=a[d],g=f.getProperties(),h={fill:"rgba( 127, 127, 127, 0.5 )",stroke:"rgba( 127, 127, 127, 1.0 )",radius:5,width:3},k;for(k in g){var l=g[k];switch(k){case "fill":h.fill=l;break;case "fill-opacity":h["fill-opacity"]=l;break;case "stroke":h.stroke=
l;break;case "stroke-opacity":h["stroke-opacity"]=l;break;case "stroke-width":h.width=l}}h["fill-opacity"]&&(g=netgis.util.hexToRGB(h.fill),g="rgba("+g.join(",")+","+h["fill-opacity"]+")",h.fill=g);h["stroke-opacity"]&&(g=netgis.util.hexToRGB(h.stroke),g="rgba("+g.join(",")+","+h["stroke-opacity"]+")",h.stroke=g);h=new ol.style.Style({image:new ol.style.Circle({radius:h.radius,fill:new ol.style.Fill({color:h.stroke})}),fill:new ol.style.Fill({color:h.fill}),stroke:new ol.style.Stroke({color:h.stroke,
@@ -287,10 +285,10 @@ netgis.Map.prototype.zoomFeature=function(a,b){a=this.layers[a].getSource().getF
netgis.Map.prototype.addViewHistory=function(a,b){if(0<this.viewHistory.length){var c=this.viewHistory[this.viewHistory.length-1],d=!0;10<Math.abs(a[0]-c.center[0])&&(d=!1);10<Math.abs(a[1]-c.center[1])&&(d=!1);.1<Math.abs(b-c.zoom)&&(d=!1);if(!0===d)return}this.viewHistory.push({center:a,zoom:b});this.viewHistory.length>this.viewHistoryMax&&this.viewHistory.shift();this.viewIndex=this.viewHistory.length-1};
netgis.Map.prototype.gotoViewHistory=function(a){if(!(1>this.viewHistory.length)){var b=this.viewHistory.length-1;0>a&&(a=b);a>b&&(a=0);a!==this.viewIndex&&(b=this.viewHistory[a],this.viewIndex=a,this.viewFromHistory=!0,this.view.setCenter(b.center),this.view.setZoom(b.zoom))}};netgis.Map.prototype.setPadding=function(a,b,c,d){var e=this.paddingBuffer;this.view.padding=[a+e,b+e,c+e,d+e]};
netgis.Map.prototype.exportImage=function(a,b,c,d,e){var f=this,g=this.container,h=this.map,k=this.config["export"],l=new Image;l.onload=function(){var m=document.createElement("div");m.style.position="fixed";m.style.top="0px";m.style.left="0px";m.style.width=b+"px";m.style.height=c+"px";m.style.background="white";m.style.zIndex=-1;m.style.opacity=0;m.style.pointerEvents="none";g.appendChild(m);h.setTarget(m);h.once("rendercomplete",function(){var n=document.createElement("canvas");n.width=b;n.height=
c;var p=n.getContext("2d");p.webkitImageSmoothingEnabled=!1;p.mozImageSmoothingEnabled=!1;p.imageSmoothingEnabled=!1;Array.prototype.forEach.call(document.querySelectorAll(".ol-layer canvas"),function(a){if(0<a.width){var b=a.parentNode.style.opacity;p.globalAlpha=""===b?1:Number(b);b=a.style.transform.match(/^matrix\(([^\(]*)\)$/)[1].split(",").map(Number);CanvasRenderingContext2D.prototype.setTransform.apply(p,b);p.drawImage(a,0,0)}});p.drawImage(l,0,0);p.fillStyle="#fff";p.fillRect(0,n.height-
30,140,30);p.fillStyle="#000";p.font="4mm sans-serif";p.fillText(netgis.util.getTimeStamp(),10,n.height-10);var q=document.createElement("a");switch(a){case "pdf":e=e?e:0;var r=297-e-e,t=210-e-e,u=n.width/n.height;if(!d){var v=r;r=t;t=v}if(n.height>n.width){var w=t;v=w*u;v>r&&(v=r,w=v/u)}else v=r,w=v/u,w>t&&(w=t,v=w*u);u=new jsPDF(d?"l":"p");var x=e;x+=(r-v)/2;r=e;r+=(t-w)/2;u.addImage(n.toDataURL("image/png,1.0",1),"PNG",x,r,v,w);u.setFillColor(255,255,255);u.rect(x,r+w-11,80,11,"F");u.setFontSize(8);
u.text("Datum: "+netgis.util.getTimeStamp(),x+2,r+w-2-4);u.text("Quelle: "+window.location.href,x+2,r+w-2);n=u.output("bloburl",{filename:k.default_filename+".pdf"});window.open(n,"_blank");break;case "jpeg":window.navigator.msSaveBlob?window.navigator.msSaveBlob(n.msToBlob(),k.default_filename+".jpg"):(q.setAttribute("download",k.default_filename+".jpg"),q.setAttribute("href",n.toDataURL("image/jpeg",1)),q.click());break;case "png":window.navigator.msSaveBlob?window.navigator.msSaveBlob(n.msToBlob(),
k.default_filename+".png"):(q.setAttribute("download",k.default_filename+".png"),q.setAttribute("href",n.toDataURL("image/png",1)),q.click());break;case "gif":q.setAttribute("download",k.default_filename+".gif"),t=new GIF({workerScript:k.gif_worker,quality:1}),t.addFrame(n),t.on("finished",function(a){q.setAttribute("href",window.URL.createObjectURL(a));q.click()}),t.render()}h.setTarget(g);g.removeChild(m);netgis.util.invoke(f.container,netgis.Events.EXPORT_END,null)});h.renderSync()};l.src=k.logo};
c;var q=n.getContext("2d");q.webkitImageSmoothingEnabled=!1;q.mozImageSmoothingEnabled=!1;q.imageSmoothingEnabled=!1;Array.prototype.forEach.call(document.querySelectorAll(".ol-layer canvas"),function(a){if(0<a.width){var b=a.parentNode.style.opacity;q.globalAlpha=""===b?1:Number(b);b=a.style.transform.match(/^matrix\(([^\(]*)\)$/)[1].split(",").map(Number);CanvasRenderingContext2D.prototype.setTransform.apply(q,b);q.drawImage(a,0,0)}});q.drawImage(l,0,0);q.fillStyle="#fff";q.fillRect(0,n.height-
30,140,30);q.fillStyle="#000";q.font="4mm sans-serif";q.fillText(netgis.util.getTimeStamp(),10,n.height-10);var p=document.createElement("a");switch(a){case "pdf":e=e?e:0;var r=297-e-e,v=210-e-e,t=n.width/n.height;if(!d){var w=r;r=v;v=w}if(n.height>n.width){var u=v;w=u*t;w>r&&(w=r,u=w/t)}else w=r,u=w/t,u>v&&(u=v,w=u*t);t=new jsPDF(d?"l":"p");var x=e;x+=(r-w)/2;r=e;r+=(v-u)/2;t.addImage(n.toDataURL("image/png,1.0",1),"PNG",x,r,w,u);t.setFillColor(255,255,255);t.rect(x,r+u-11,80,11,"F");t.setFontSize(8);
t.text("Datum: "+netgis.util.getTimeStamp(),x+2,r+u-2-4);t.text("Quelle: "+window.location.href,x+2,r+u-2);n=t.output("bloburl",{filename:k.default_filename+".pdf"});window.open(n,"_blank");break;case "jpeg":window.navigator.msSaveBlob?window.navigator.msSaveBlob(n.msToBlob(),k.default_filename+".jpg"):(p.setAttribute("download",k.default_filename+".jpg"),p.setAttribute("href",n.toDataURL("image/jpeg",1)),p.click());break;case "png":window.navigator.msSaveBlob?window.navigator.msSaveBlob(n.msToBlob(),
k.default_filename+".png"):(p.setAttribute("download",k.default_filename+".png"),p.setAttribute("href",n.toDataURL("image/png",1)),p.click());break;case "gif":p.setAttribute("download",k.default_filename+".gif"),v=new GIF({workerScript:k.gif_worker,quality:1}),v.addFrame(n),v.on("finished",function(a){p.setAttribute("href",window.URL.createObjectURL(a));p.click()}),v.render()}h.setTarget(g);g.removeChild(m);netgis.util.invoke(f.container,netgis.Events.EXPORT_END,null)});h.renderSync()};l.src=k.logo};
netgis.Map.prototype.exportFeatures=function(a){var b=this.editLayer.getSource().getFeatures();!0===a&&(a=this.nonEditLayer.getSource().getFeatures(),b=b.concat(a));b=(new ol.format.GeoJSON).writeFeaturesObject(b,{featureProjection:this.view.getProjection(),dataProjection:"EPSG:4326"});a=this.config["export"].default_filename+".geojson";b.name=a;netgis.util.downloadJSON(b,a);netgis.util.invoke(this.container,netgis.Events.EXPORT_END,null)};netgis.Map.prototype.onClientContextResponse=function(a){this.initConfig(a.detail.context.config)};
netgis.Map.prototype.onEditLayerLoaded=function(a){a=a.detail.geojson;var b=new ol.format.GeoJSON;b.readProjection(a);a=b.readFeatures(a,{featureProjection:this.view.getProjection().getCode()});var c=this,d=a.slice();window.setTimeout(function(){c.zoomFeatures(d)},10);b=[];for(var e=0;e<a.length;e++){var f=a[e];!0===f.getProperties().editable&&b.push(f)}for(e=0;e<b.length;e++)a.splice(a.indexOf(b[e]),1);this.editEventsSilent=!0;this.editLayer.getSource().addFeatures(b);this.nonEditLayer.getSource().addFeatures(a);
this.editEventsSilent=!1};netgis.Map.prototype.onClientSetMode=function(a){this.setMode(a.detail.mode)};netgis.Map.prototype.onPanelResize=function(a){this.setPadding(0,0,0,a.detail.width);this.redrawVectorLayers()};
@@ -307,7 +305,8 @@ d,b,a)};netgis.Map.prototype.onPointerLeave=function(a){this.hoverFeature&&(this
netgis.Map.prototype.onPointerClick=function(a){var b=a.pixel;a=a.coordinate;this.popupOverlay.setPosition(a);var c={resolution:this.view.getResolution(),projection:this.view.getProjection().getCode(),bbox:this.view.calculateExtent(this.map.getSize()),width:this.map.getSize()[0],height:this.map.getSize()[1]},d=ol.proj.toLonLat(a,this.view.getProjection()),e={};for(g in this.layers){var f=this.layers[g].getSource();f.getFeatureInfoUrl&&(e[g]=f.getFeatureInfoUrl(a,c.resolution,c.projection,{INFO_FORMAT:"text/html"}))}var g=
{mode:this.mode,pixel:b,coords:a,lon:d[0],lat:d[1],overlay:this.popupOverlay.getElement(),view:c,info:e};this.mode===netgis.Modes.VIEW&&netgis.util.invoke(this.container,netgis.Events.MAP_CLICK,g);var h=[],k=this;this.map.forEachFeatureAtPixel(b,function(a,b){b&&b!==k.nonEditLayer&&b!==k.boundsLayer&&b!==k.measureLayer&&b!==k.previewLayer&&(-1<k.sketchFeatures.indexOf(a)||h.push({feature:a,layer:b}))});g=!0;this.mode===netgis.Modes.VIEW&&(g=!1);this.mode===netgis.Modes.MEASURE_LINE&&(g=!1);this.mode===
netgis.Modes.MEASURE_AREA&&(g=!1);this.mode===netgis.Modes.DRAW_POINTS&&(g=!1);this.mode===netgis.Modes.DRAW_LINES&&(g=!1);this.mode===netgis.Modes.DRAW_POLYGONS&&(g=!1);this.mode===netgis.Modes.CUT_FEATURES_DRAW&&(g=!1);g&&(0<h.length&&!1===this.selectMultiple&&(this.selectedFeatures=[]),0===h.length&&!1===this.selectMultiple&&(this.selectedFeatures=[]),!0===this.selectReset&&(this.selectedFeatures=[],this.selectReset=!1),this.mode===netgis.Modes.BUFFER_FEATURES_DYNAMIC&&this.updateBufferFeaturesSketch(this.bufferFeaturesRadius,
this.bufferFeaturesSegments));for(c=0;c<h.length;c++)d=h[c],g&&(e=this.selectedFeatures.indexOf(d.feature),-1<e?this.selectedFeatures.splice(e,1):this.selectedFeatures.push(d.feature)),this.onFeatureClick(d.feature,d.layer,b,a);this.redrawVectorLayers()};netgis.Map.prototype.onContainerClick=function(a){if(2===a.detail)this.onDoubleClick(a)};
this.bufferFeaturesSegments));for(c=0;c<h.length;c++)if(d=h[c],g&&(e=this.selectedFeatures.indexOf(d.feature),-1<e?this.selectedFeatures.splice(e,1):this.selectedFeatures.push(d.feature)),e=!1,this.mode===netgis.Modes.VIEW&&(e=!0),this.mode===netgis.Modes.DELETE_FEATURES&&(e=!0),this.mode===netgis.Modes.BUFFER_FEATURES_DYNAMIC&&(e=!0),this.mode===netgis.Modes.CUT_FEATURES&&(e=!0),this.mode===netgis.Modes.SEARCH_PARCEL&&(e=!0),e)this.onFeatureClick(d.feature,d.layer,b,a);this.redrawVectorLayers()};
netgis.Map.prototype.onContainerClick=function(a){if(2===a.detail)this.onDoubleClick(a)};
netgis.Map.prototype.onDoubleClick=function(a){switch(this.mode){case netgis.Modes.MEASURE_LINE:this.interactions[netgis.Modes.MEASURE_LINE]&&this.interactions[netgis.Modes.MEASURE_LINE][2].finishDrawing();break;case netgis.Modes.MEASURE_AREA:this.interactions[netgis.Modes.MEASURE_AREA]&&this.interactions[netgis.Modes.MEASURE_AREA][2].finishDrawing();break;case netgis.Modes.DRAW_LINES:this.interactions[netgis.Modes.DRAW_LINES]&&this.interactions[netgis.Modes.DRAW_LINES][0].finishDrawing();break;case netgis.Modes.DRAW_POLYGONS:this.interactions[netgis.Modes.DRAW_POLYGONS]&&
this.interactions[netgis.Modes.DRAW_POLYGONS][0].finishDrawing();break;case netgis.Modes.CUT_FEATURES_DRAW:this.interactions[netgis.Modes.CUT_FEATURES_DRAW]&&this.interactions[netgis.Modes.CUT_FEATURES_DRAW][0].finishDrawing()}};
netgis.Map.prototype.onRightClick=function(a){switch(this.mode){case netgis.Modes.MEASURE_LINE:this.interactions[netgis.Modes.MEASURE_LINE]&&this.interactions[netgis.Modes.MEASURE_LINE][2].finishDrawing();break;case netgis.Modes.MEASURE_AREA:this.interactions[netgis.Modes.MEASURE_AREA]&&this.interactions[netgis.Modes.MEASURE_AREA][2].finishDrawing();break;case netgis.Modes.DRAW_LINES:this.interactions[netgis.Modes.DRAW_LINES]&&this.interactions[netgis.Modes.DRAW_LINES][0].finishDrawing();break;case netgis.Modes.DRAW_POLYGONS:this.interactions[netgis.Modes.DRAW_POLYGONS]&&
@@ -319,9 +318,9 @@ netgis.Map.prototype.onKeyUp=function(a){a=a.keyCode||a.which;switch(this.mode){
!1,!0===this.config.tools.select_multi_reset&&(this.selectReset=!0))};
netgis.Map.prototype.onFeatureEnter=function(a,b,c,d){if(b){switch(this.mode){case netgis.Modes.VIEW:this.container.classList.add("netgis-clickable");break;case netgis.Modes.DELETE_FEATURES:case netgis.Modes.BUFFER_FEATURES:case netgis.Modes.BUFFER_FEATURES_DYNAMIC:case netgis.Modes.CUT_FEATURES:this.container.classList.add("netgis-clickable");a.setStyle(this.styleHover.bind(this));break;case netgis.Modes.SEARCH_PARCEL:this.container.classList.add("netgis-clickable"),a.setStyle(this.styleHover.bind(this))}netgis.util.invoke(this.container,
netgis.Events.MAP_FEATURE_ENTER,{pixel:c,coords:d,layer:b.get("id"),properties:a.getProperties()})}};netgis.Map.prototype.onFeatureHover=function(a,b,c,d){};
netgis.Map.prototype.onFeatureClick=function(a,b,c,d){var e=ol.proj.toLonLat(d,this.view.getProjection());c={pixel:c,coords:d,lon:e[0],lat:e[1],overlay:this.popupOverlay.getElement(),layer:b.get("id"),id:a.getId(),properties:a.getProperties(),mode:this.mode};netgis.util.invoke(this.container,netgis.Events.MAP_FEATURE_CLICK,c);switch(this.mode){case netgis.Modes.DELETE_FEATURES:b.getSource().removeFeature(a);this.onFeatureLeave(a,b);netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.VIEW});
break;case netgis.Modes.BUFFER_FEATURES:case netgis.Modes.BUFFER_FEATURES_EDIT:this.onFeatureLeave(a,b);this.selectMultiple?netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.BUFFER_FEATURES}):netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.BUFFER_FEATURES_EDIT});break;case netgis.Modes.BUFFER_FEATURES_DYNAMIC:this.updateBufferFeaturesSketch(this.bufferFeaturesRadius,this.bufferFeaturesSegments);break;case netgis.Modes.CUT_FEATURES:if(a.getGeometry()instanceof
ol.geom.Point)this.onFeatureLeave(a,b);else this.selectMultiple||netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.CUT_FEATURES_DRAW})}};
netgis.Map.prototype.onFeatureClick=function(a,b,c,d){if(this.mode!==netgis.Modes.SEARCH_PARCEL||"searchparcel_districts"===b.get("id")||"searchparcel_fields"===b.get("id")||"searchparcel_parcels"===b.get("id")){var e=ol.proj.toLonLat(d,this.view.getProjection());c={pixel:c,coords:d,lon:e[0],lat:e[1],overlay:this.popupOverlay.getElement(),layer:b.get("id"),id:a.getId(),properties:a.getProperties(),mode:this.mode};netgis.util.invoke(this.container,netgis.Events.MAP_FEATURE_CLICK,c);switch(this.mode){case netgis.Modes.DELETE_FEATURES:b.getSource().removeFeature(a);
this.onFeatureLeave(a,b);netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.VIEW});break;case netgis.Modes.BUFFER_FEATURES:case netgis.Modes.BUFFER_FEATURES_EDIT:this.onFeatureLeave(a,b);this.selectMultiple?netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.BUFFER_FEATURES}):netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.BUFFER_FEATURES_EDIT});break;case netgis.Modes.BUFFER_FEATURES_DYNAMIC:this.updateBufferFeaturesSketch(this.bufferFeaturesRadius,
this.bufferFeaturesSegments);break;case netgis.Modes.CUT_FEATURES:if(a.getGeometry()instanceof ol.geom.Point)this.onFeatureLeave(a,b);else this.selectMultiple||netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.CUT_FEATURES_DRAW})}}};
netgis.Map.prototype.onFeatureLeave=function(a,b,c,d){netgis.util.invoke(this.container,netgis.Events.MAP_FEATURE_LEAVE,{pixel:c,coords:d,layer:b?b.get("id"):null,properties:a.getProperties()});switch(this.mode){case netgis.Modes.DELETE_FEATURES:case netgis.Modes.BUFFER_FEATURES:case netgis.Modes.BUFFER_FEATURES_DYNAMIC:case netgis.Modes.CUT_FEATURES:case netgis.Modes.CUT_FEATURES_DRAW:this.container.classList.remove("netgis-clickable");a.setStyle(null);break;case netgis.Modes.SEARCH_PARCEL:this.container.classList.remove("netgis-clickable"),
a.setStyle(null)}};netgis.Map.prototype.onEditLayerAdd=function(a){this.editEventsSilent||this.updateEditOutput();this.snapFeatures.push(a.feature)};netgis.Map.prototype.onEditLayerRemove=function(a){this.editEventsSilent||this.updateEditOutput();this.snapFeatures.remove(a.feature)};netgis.Map.prototype.onEditLayerChange=function(a){this.editEventsSilent||this.updateEditOutput()};
netgis.Map.prototype.onCopyFeatureToEdit=function(a){a=a.detail;var b=this.layers[a.source].getSource().getFeatureById(a.id);b?this.editLayer.getSource().getFeatureById(a.id)||(b.setStyle(void 0),this.selectedFeatures=[],this.editLayer.getSource().addFeature(b)):console.error("feature to copy not found",a)};netgis.Map.prototype.onGeolocToggleActive=function(a){a.detail.on?this.geolocLayer.setVisible(!0):this.geolocLayer.setVisible(!1)};
@@ -331,14 +330,15 @@ d.intersects(g)&&(d=d.intersection(g))}c=c.write(d);a.setGeometry(c)}var h=this.
netgis.Map.prototype.onDrawBufferChange=function(a){a=a.detail;this.drawBufferRadius=a.radius;this.drawBufferSegments=a.segments;this.updateDrawBufferPreview()};netgis.Map.prototype.onBufferChange=function(a){a=a.detail;this.updateBufferFeaturesSketch(a.radius,a.segments);this.bufferFeaturesRadius=a.radius;this.bufferFeaturesSegments=a.segments};
netgis.Map.prototype.updateBufferFeaturesSketch=function(a,b){var c=this.selectedFeatures,d=this.editLayer.getSource();this.clearSketchFeatures();for(var e=0;e<c.length;e++){var f=this.createBufferFeature(this.selectedFeatures[e].getGeometry(),a,b);d.addFeature(f);this.sketchFeatures.push(f)}};netgis.Map.prototype.onBufferAccept=function(a){a=this.selectedFeatures;for(var b=this.editLayer.getSource(),c=0;c<a.length;c++)b.removeFeature(a[c]);this.sketchFeatures=[];this.selectedFeatures=[]};
netgis.Map.prototype.onCutFeaturesDrawEnd=function(a){a=a.feature;for(var b=0;b<this.selectedFeatures.length;b++){var c=this.selectedFeatures[b];if(c)if(this.onFeatureLeave(c,null),c.getGeometry()instanceof ol.geom.Point)console.error("trying to cut a point feature",c);else{var d=new jsts.io.OL3Parser,e=d.read(c.getGeometry()),f=d.read(a.getGeometry());e=e.difference(f);d=d.write(e);d=new ol.Feature({geometry:d});e=this.editLayer.getSource();e.removeFeature(c);e.addFeature(d)}}this.selectedFeatures=
[];this.editEventsSilent=!0;this.splitMultiPolygons(this.editLayer);this.editEventsSilent=!1;this.updateEditOutput();netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.VIEW})};netgis.Map.prototype.onImportLayerAccept=function(a){a=a.detail;a=this.addLayer(a.id,a);var b=a.getSource();b instanceof ol.source.Vector&&0<b.getFeatures().length&&this.view.fit(a.getSource().getExtent(),{duration:600})};netgis.Map.prototype.onImportGeoportalSubmit=function(a){};
[];this.editEventsSilent=!0;this.splitMultiPolygons(this.editLayer);this.editEventsSilent=!1;this.updateEditOutput();netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.VIEW})};
netgis.Map.prototype.onImportLayerAccept=function(a){a=a.detail;a.editable?(a=this.createLayerGeoJSON(a.data),a=a.getSource(),this.editLayer.getSource().addFeatures(a.getFeatures())):(a=this.addLayer(a.id,a),a=a.getSource());a instanceof ol.source.Vector&&0<a.getFeatures().length&&this.view.fit(a.getExtent(),{duration:600})};netgis.Map.prototype.onImportGeoportalSubmit=function(a){};
netgis.Map.prototype.onImportLayerPreview=function(a){a=a.detail;var b=this.createLayer(a),c=this.view.getProjection().getCode();netgis.util.invoke(this.container,netgis.Events.IMPORT_LAYER_PREVIEW_FEATURES,{id:a.id,title:a.title,layer:b,proj:c})};netgis.Map.prototype.onSearchParcelReset=function(a){(a=this.config.searchparcel.districts_service.min_zoom)&&this.view.setZoom(a)};netgis.Map.prototype.onSearchParcelItemEnter=function(a){a=a.detail.id;this.layers.searchparcel_parcels.getSource().getFeatureById(a).setStyle(this.styleHover.bind(this))};
netgis.Map.prototype.onSearchParcelItemLeave=function(a){a=a.detail.id;this.layers.searchparcel_parcels.getSource().getFeatureById(a).setStyle(null)};netgis.Map.prototype.onSearchParcelItemClick=function(a){this.zoomFeature("searchparcel_parcels",a.detail.id)};netgis.Map.prototype.onSearchParcelItemImport=function(a){};netgis.Map.prototype.onDrawPointsUpdateGeom=function(a,b,c){b?b.setCoordinates(a):b=new ol.geom.Point(a);return b};
netgis.Map.prototype.onDrawLinesUpdateGeom=function(a,b,c){b?b.setCoordinates(a):b=new ol.geom.LineString(a);this.drawError=!this.isGeomInsideLayer(this.boundsLayer,b);return b};
netgis.Map.prototype.onDrawPolygonsUpdateGeom=function(a,b,c){b?(a=[a[0].concat([a[0][0]])],b.setCoordinates(a)):b=new ol.geom.Polygon(a);c=!0;if(4>a[0].length)for(var d=0;d<a[0].length;d++){if(!this.isPointInsideLayer(this.boundsLayer,a[0][d])){c=!1;break}}else c=this.isGeomInsideLayer(this.boundsLayer,b);this.drawError=!c;return b};
netgis.Map.prototype.onDrawPointsEnd=function(a){if(this.boundsLayer){var b=a.feature,c=this.editLayer;this.isPointInsideLayer(this.boundsLayer,b.getGeometry().getCoordinates())||window.setTimeout(function(){c.getSource().removeFeature(b)},10)}};netgis.Map.prototype.onDrawLinesEnd=function(a){if(this.boundsLayer){var b=a.feature,c=this.editLayer;this.isGeomInsideLayer(this.boundsLayer,b.getGeometry())||window.setTimeout(function(){c.getSource().removeFeature(b)},10)}};
netgis.Map.prototype.onDrawPolygonsEnd=function(a){if(this.boundsLayer){var b=a.feature,c=this.editLayer;this.isGeomInsideLayer(this.boundsLayer,b.getGeometry())||window.setTimeout(function(){c.getSource().removeFeature(b)},10)}};netgis.Map.prototype.onExportBegin=function(a){a=a.detail;switch(a.format){case "geojson":this.exportFeatures(a.nonEdits);break;default:this.exportImage(a.format,a.width,a.height,a.landscape,a.padding)}};
netgis.Map.prototype.onScalebarSelectChange=function(a){netgis.util.invoke(this.scalebarSelect,netgis.Events.MAP_ZOOM_SCALE,{scale:this.scalebarSelect.value,anim:!0})};netgis.Map.prototype.onTimeSliderShow=function(a){};netgis.Map.prototype.onTimeSliderHide=function(a){};netgis.Map.prototype.onTimeSliderSelect=function(a){a=a.detail;console.info("Time Slider Select:",a);this.layers[a.layer].getSource().updateParams({TIME:a.time})};netgis=netgis||{};netgis.Menu=function(a){this.config=a;this.initElements();this.initConfig(a)};netgis.Menu.Config={header:"<a href='.' target='_self'>NetGIS Client</a>",items:[],compact:!0};
netgis.Map.prototype.onScalebarSelectChange=function(a){netgis.util.invoke(this.scalebarSelect,netgis.Events.MAP_ZOOM_SCALE,{scale:this.scalebarSelect.value,anim:!0})};netgis.Map.prototype.onTimeSliderShow=function(a){};netgis.Map.prototype.onTimeSliderHide=function(a){};netgis.Map.prototype.onTimeSliderSelect=function(a){a=a.detail;this.layers[a.layer].getSource().updateParams({TIME:a.time})};netgis=netgis||{};netgis.Menu=function(a){this.config=a;this.initElements();this.initConfig(a)};netgis.Menu.Config={header:"<a href='.' target='_self'>NetGIS Client</a>",items:[],compact:!0};
netgis.Menu.prototype.initElements=function(){this.container=document.createElement("nav");this.container.className="netgis-menu netgis-noselect netgis-color-a netgis-gradient-a netgis-shadow-large";this.toggle=document.createElement("button");this.toggle.setAttribute("type","button");this.toggle.addEventListener("click",this.onToggleClick.bind(this));this.toggle.className="netgis-menu-toggle netgis-hover-c";this.toggle.innerHTML="<i class='fas fa-bars'></i>";this.container.appendChild(this.toggle)};
netgis.Menu.prototype.initConfig=function(a){var b=a.menu;if(b&&(b.header&&this.addHeader(b.header),!0===b.compact&&this.container.classList.add("netgis-compact"),b.items)){b=b.items;for(var c=0;c<b.length;c++){var d=b[c];if(d.items){var e=d.items;if("scales"===d.id)for(var f=this.getScaleItems(),g=0;g<f.length;g++)e.push(f[g]);this.addDropdown(d.title,e)}else if(d.url&&0<d.url.length)this.addLink(d.url,d.title);else if(d.options)if("scales"===d.options){e={0:"1:X"};for(g=0;g<a.map.scales.length;g++)e[a.map.scales[g]]=
"1:"+a.map.scales[g];var h=a.map.default_scale;this.addSelect(d.id,d.title,e,h).options[0].classList.add("netgis-hide")}else{e=d.options;if(d.value)h=d.value;else for(var k in e){h=k;break}this.addSelect(d.id,d.title,e,h)}else this.addButton(d.id,d.title)}}};netgis.Menu.prototype.attachTo=function(a){a.appendChild(this.container);a.addEventListener(netgis.Events.MAP_VIEW_CHANGE,this.onMapViewChange.bind(this))};
@@ -359,10 +359,7 @@ netgis.Modal.prototype.initEvents=function(){};netgis.Modal.prototype.attachTo=f
netgis.Modal.prototype.addHeader=function(a,b,c){var d=document.createElement("button");d.className="netgis-button netgis-clip-text netgis-color-c netgis-gradient-a";d.innerHTML="<span>"+b+"</span><i class='netgis-icon fas fa-times'></i>";d.setAttribute("type","button");c&&(d.onclick=c);a&&a.appendChild(d);return d};netgis.Modal.prototype.onHeaderClick=function(a){this.hide()};netgis.Modal.prototype.onContainerClick=function(a){a.target===this.container&&this.hide()};netgis=netgis||{};
netgis.Modes=Object.freeze({VIEW:"view",ZOOM_BOX:"zoom-box",MEASURE_LINE:"measure-line",MEASURE_AREA:"measure-area",DRAW_POINTS:"draw-points",DRAW_LINES:"draw-lines",DRAW_POLYGONS:"draw-polygons",MODIFY_FEATURES:"modify-features",DELETE_FEATURES:"delete-features",BUFFER_FEATURES:"buffer-features",BUFFER_FEATURES_EDIT:"buffer-features-edit",BUFFER_FEATURES_DYNAMIC:"buffer-features-dynamic",CUT_FEATURES:"cut-features",CUT_FEATURES_DRAW:"cut-features-draw",CUT_FEATURES_DYNAMIC:"cut-features-dynamic",SEARCH_PARCEL:"search-parcel"});netgis=netgis||{};netgis.Modules={menu:!0,layertree:!0,map:!0,controls:!0,attribution:!0,legend:!0,geolocation:!0,info:!0,searchplace:!0,searchparcel:!0,toolbox:!0,"import":!0,"export":!0,timeslider:!0};netgis=netgis||{};
netgis.OWS=function(){return{read:function(a,b){console.info("OWS READ:",a);console.info("OWS PROPS:",a.properties);a=a.features;for(b=0;b<a.length;b++){var c=a[b];console.info("OWS FEATURE:",b,c);var d=c;c=d.properties;switch(d.type){case "Feature":d=c.minScaleDenominator;var e=c.maxScaleDenominator;console.info("TITLE:",c.title,"FOLDER:",c.folder);console.info("MIN/MAX SCALE:",d,e);c=c.offerings;for(d=0;d<c.length;d++){e=c[d];console.info("OFFERING:",d,e);var f=e,g=f.code;e=f.content;switch(g){case "http://www.opengis.net/spec/owc-atom/1.0/req/kml":for(f=
0;f<e.length;f++)switch(g=e[f],g.type){case "application/vnd.google-earth.kml+xml":break;default:console.error("OWS: unknown offering content type '"+g.type+"'",g)}break;default:console.error("OWS: unknown offering code '"+g+"'",f)}}break;default:console.error("OWS: unknown feature type '"+d.type+"'",d)}}}}}();netgis=netgis||{};
netgis.OWS=function(){return{read:function(a,b){b={layers:[],folders:[]};netgis.util.isDefined(a.properties)&&(b.bbox=a.properties.bbox);a=a.features;for(var c=0;c<a.length;c++){var d=a[c];if("Feature"===d.type){var e=d.properties;e=e.folder;var f=!1;for(d=0;d<b.folders.length;d++)if(b.folders[d].id===e){f=!0;break}if(!f){d=e.split("/");e=[];for(f=0;f<d.length;f++){var g=d[f];0<g.length&&e.push(g)}var h=-1;for(f=0;f<e.length;f++){g=e[f];var k="/"+e.slice(0,f+1).join("/"),l=!1;for(d=0;d<b.folders.length;d++)if(b.folders[d].path===
k){h=d;l=!0;break}l||(d=b.folders.length,b.folders.push({title:g,parent:h,path:k}),h=d)}}}}for(c=0;c<a.length;c++)if(d=a[c],"Feature"===d.type){e=d.properties;f=-1;for(d=0;d<b.folders.length;d++)if(b.folders[d].path===e.folder){f=d;break}d=e.offerings;for(g=0;g<d.length;g++)switch(h=d[g],k=h.operations,h.code){case "http://www.opengis.net/spec/owc-geojson/1.0/req/wms":b.layers.push({folder:f,type:netgis.LayerTypes.WMS,url:k[0].href,title:e.title,attribution:e.rights,active:e.active});break;case "http://www.opengis.net/spec/owc-geojson/1.0/req/xyz":h=
k[0];b.layers.push({folder:f,type:netgis.LayerTypes.XYZ,url:h.href,title:e.title,attribution:e.rights,active:e.active});break;case "http://www.opengis.net/spec/owc-geojson/1.0/req/osm":h=k[0],b.layers.push({folder:f,type:netgis.LayerTypes.XYZ,url:h.href,title:e.title,attribution:e.rights,active:e.active})}}return b}}}();netgis.OWS.Config={url:""};netgis=netgis||{};netgis.Panel=function(a){this.initElements(a);this.initEvents()};netgis.Panel.prototype.initElements=function(a){var b=document.createElement("section");b.className="netgis-panel netgis-resize-right netgis-color-e netgis-shadow";this.content=document.createElement("div");b.appendChild(this.content);this.header=this.addHeader(b,a,this.onHeaderClick.bind(this));this.container=b};netgis.Panel.prototype.initEvents=function(){this.resizeObserver=(new ResizeObserver(this.onResize.bind(this))).observe(this.container)};
0;f<e.length;f++)switch(g=e[f],g.type){case "application/vnd.google-earth.kml+xml":break;default:console.error("OWS: unknown offering content type '"+g.type+"'",g)}break;default:console.error("OWS: unknown offering code '"+g+"'",f)}}break;default:console.error("OWS: unknown feature type '"+d.type+"'",d)}}}}}();netgis=netgis||{};netgis.Panel=function(a){this.initElements(a);this.initEvents()};netgis.Panel.prototype.initElements=function(a){var b=document.createElement("section");b.className="netgis-panel netgis-resize-right netgis-color-e netgis-shadow";this.content=document.createElement("div");b.appendChild(this.content);this.header=this.addHeader(b,a,this.onHeaderClick.bind(this));this.container=b};netgis.Panel.prototype.initEvents=function(){this.resizeObserver=(new ResizeObserver(this.onResize.bind(this))).observe(this.container)};
netgis.Panel.prototype.attachTo=function(a){a.appendChild(this.container);a.addEventListener(netgis.Events.PANEL_TOGGLE,this.onPanelToggle.bind(this))};netgis.Panel.prototype.addHeader=function(a,b,c){var d=document.createElement("button");d.className="netgis-button netgis-clip-text netgis-color-c netgis-gradient-a netgis-shadow";d.innerHTML="<span>"+b+"</span><i class='netgis-icon fas fa-times'></i>";d.setAttribute("type","button");c&&(d.onclick=c);a&&a.appendChild(d);return d};
netgis.Panel.prototype.show=function(){this.container.classList.contains("netgis-show")||(this.container.classList.add("netgis-show"),netgis.util.invoke(this.container,netgis.Events.PANEL_TOGGLE,{container:this.container,visible:!0,width:this.container.getBoundingClientRect().width}))};
netgis.Panel.prototype.hide=function(){this.container.classList.contains("netgis-show")&&(this.container.classList.remove("netgis-show"),netgis.util.invoke(this.container,netgis.Events.PANEL_TOGGLE,{container:this.container,visible:!1}))};
@@ -381,16 +378,16 @@ netgis.Popup.prototype.show=function(){this.container.classList.add("netgis-show
netgis.Popup.prototype.hideLoader=function(){this.loader.parentNode===this.content&&this.content.removeChild(this.loader)};
netgis.Popup.prototype.setPosition=function(a,b){var c=this.container.parentNode.getBoundingClientRect(),d=this.arrow.getBoundingClientRect();a>c.width-d.width&&(a=c.width-d.width);a<d.width&&(a=d.width);switch(this.options.direction){default:case "down":this.container.style.left=a+"px";this.container.style.top=b+"px";break;case "right":this.container.style.right=c.width-a+"px",this.container.style.top=b+"px"}this.content.style.left="";a=this.content.getBoundingClientRect();0>a.x?this.content.style.left=
-a.x+"px":a.x+a.width>c.width&&(this.content.style.left=-(a.x+a.width-c.width)+"px")};netgis.Popup.prototype.setHeader=function(a){this.closer.getElementsByTagName("span")[0].innerHTML=a};netgis.Popup.prototype.setContent=function(a){this.wrapper.innerHTML=a};netgis.Popup.prototype.clearContent=function(){this.wrapper.innerHTML=""};netgis.Popup.prototype.addContent=function(a){this.wrapper.innerHTML+=a};netgis.Popup.prototype.onDocumentPointerDown=function(a){};
netgis.Popup.prototype.onPointerDown=function(a){a.stopPropagation()};netgis.Popup.prototype.onCloserClick=function(a){this.hide()};netgis=netgis||{};netgis.Search=function(a){this.debounce=400;this.initElements(a);this.initEvents()};
netgis.Search.prototype.initElements=function(a){var b=document.createElement("div");b.className="netgis-search";this.container=b;var c=document.createElement("label");b.appendChild(c);this.label=c;var d=document.createElement("input");d.className="netgis-round netgis-shadow";d.setAttribute("type","text");d.setAttribute("placeholder",a);c.appendChild(d);this.input=d;a=document.createElement("button");a.setAttribute("type","button");a.innerHTML="<i class='fas fa-search'></i>";c.appendChild(a);this.button=
a;a=document.createElement("button");a.setAttribute("type","button");a.className="netgis-hide";a.innerHTML="<i class='fas fa-times'></i>";c.appendChild(a);this.closer=a;c=document.createElement("ul");b.appendChild(c);this.results=c};
netgis.Popup.prototype.onPointerDown=function(a){a.stopPropagation()};netgis.Popup.prototype.onCloserClick=function(a){this.hide()};netgis=netgis||{};netgis.Search=function(a){this.debounce=400;this.autocomplete=!0;this.initElements(a);this.initEvents()};
netgis.Search.prototype.initElements=function(a){var b=document.createElement("div");b.className="netgis-search";this.container=b;var c=document.createElement("label");b.appendChild(c);this.label=c;var d=document.createElement("input");d.className="netgis-round netgis-shadow";d.setAttribute("type","text");d.setAttribute("placeholder",a);c.appendChild(d);this.input=d;a=document.createElement("button");a.setAttribute("type","button");a.innerHTML="<i class='fas fa-search'></i>";a.className="netgis-no-background";
c.appendChild(a);this.button=a;a=document.createElement("button");a.setAttribute("type","button");a.className="netgis-hide netgis-no-background";a.innerHTML="<i class='fas fa-times'></i>";c.appendChild(a);this.closer=a;c=document.createElement("ul");b.appendChild(c);this.results=c};
netgis.Search.prototype.initEvents=function(){this.input.addEventListener("change",this.onInputChange.bind(this));this.input.addEventListener("keydown",this.onInputKeyDown.bind(this));this.input.addEventListener("keyup",this.onInputKeyUp.bind(this));this.button.addEventListener("click",this.onButtonClick.bind(this));this.closer.addEventListener("click",this.onCloserClick.bind(this))};netgis.Search.prototype.attachTo=function(a){a.appendChild(this.container)};netgis.Search.prototype.show=function(){this.container.classList.remove("netgis-hide")};
netgis.Search.prototype.hide=function(){this.container.classList.add("netgis-hide")};netgis.Search.prototype.toggle=function(){this.container.classList.toggle("netgis-hide")};netgis.Search.prototype.isVisible=function(){return!this.container.classList.contains("netgis-hide")};netgis.Search.prototype.minimize=function(){this.container.classList.remove("netgis-color-e","netgis-shadow");this.input.classList.add("netgis-shadow")};
netgis.Search.prototype.maximize=function(){this.container.classList.add("netgis-color-e","netgis-shadow");this.input.classList.remove("netgis-shadow")};netgis.Search.prototype.focus=function(){this.input.focus()};netgis.Search.prototype.setTitle=function(a){this.input.setAttribute("placeholder",a)};
netgis.Search.prototype.addResult=function(a,b){var c=document.createElement("li"),d=document.createElement("button");d.className="netgis-button netgis-clip-text netgis-color-e netgis-hover-a";d.innerHTML=a;d.setAttribute("type","button");d.setAttribute("title",d.innerText);d.setAttribute("data-result",b);d.addEventListener("click",this.onResultClick.bind(this));c.appendChild(d);0===this.results.childNodes.length&&this.showClearButton(!0);this.results.appendChild(c);this.maximize();return c};
netgis.Search.prototype.clearResults=function(){this.results.innerHTML="";this.minimize();netgis.util.invoke(this.container,netgis.Events.SEARCH_CLEAR,null)};netgis.Search.prototype.clearAll=function(){this.clearResults();this.lastQuery=null;this.input.value="";this.showClearButton(!1)};netgis.Search.prototype.requestSearch=function(a){this.lastQuery&&this.lastQuery===a||(this.lastQuery=a,netgis.util.invoke(this.container,netgis.Events.SEARCH_CHANGE,{query:a}))};
netgis.Search.prototype.showClearButton=function(a){!1===a?(this.button.classList.remove("netgis-hide"),this.closer.classList.add("netgis-hide")):(this.button.classList.add("netgis-hide"),this.closer.classList.remove("netgis-hide"))};netgis.Search.prototype.onInputKeyDown=function(a){if(13===a.keyCode)return a.preventDefault(),!1};
netgis.Search.prototype.onInputKeyUp=function(a){switch(a.keyCode){case 13:a=this.results.getElementsByTagName("button");0<a.length&&a[0].click();break;case 27:this.clearAll();break;default:this.onInputChange()}};netgis.Search.prototype.onInputChange=function(a){this.timeout&&window.clearTimeout(this.timeout);this.timeout=window.setTimeout(this.onInputTimeout.bind(this),this.debounce)};
netgis.Search.prototype.onInputKeyUp=function(a){switch(a.keyCode){case 13:if(this.autocomplete)a=this.results.getElementsByTagName("button"),0<a.length&&a[0].click();else this.onInputTimeout();break;case 27:this.clearAll();break;default:if(this.autocomplete)this.onInputChange()}};netgis.Search.prototype.onInputChange=function(a){this.timeout&&window.clearTimeout(this.timeout);this.timeout=window.setTimeout(this.onInputTimeout.bind(this),this.debounce)};
netgis.Search.prototype.onInputTimeout=function(){var a=this.input.value;a=a.trim();0<a.length?this.requestSearch(a):this.clearAll()};netgis.Search.prototype.onButtonClick=function(a){this.input.focus()};netgis.Search.prototype.onCloserClick=function(a){this.clearAll();this.input.focus()};netgis.Search.prototype.onResultClick=function(a){a=a.currentTarget;var b=a.getAttribute("data-result");netgis.util.invoke(a,netgis.Events.SEARCH_SELECT,{item:a,data:b})};netgis=netgis||{};netgis.SearchParcel=function(a){this.config=a;this.districtsLayerID="searchparcel_districts";this.fieldsLayerID="searchparcel_fields";this.parcelsLayerID="searchparcel_parcels";this.selected={};this.initElements();this.initEvents();this.initConfig(a)};
netgis.SearchParcel.Config={open:!1,name_url:"./proxy.php?https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_alkis/gem_search.php?placename={q}",parcel_url:"./proxy.php?https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_alkis/flur_search.php?gmk_gmn={district}&fln={field}&fsn_zae={parcelA}&fsn_nen={parcelB}&export=json",districts_service:{type:"WFS",url:"http://geo5.service24.rlp.de/wfs/verwaltungsgrenzen_rp.fcgi?",name:"vermkv:gemarkungen_rlp",format:"application/json; subtype=geojson",
min_zoom:12},fields_service:{url:"http://geo5.service24.rlp.de/wfs/verwaltungsgrenzen_rp.fcgi?",name:"vermkv:fluren_rlp",filter_property:"gmkgnr"}};
@@ -452,19 +449,19 @@ c.addEventListener("click",this.onHeaderButtonClick.bind(this));c.className="net
netgis.Tabs.prototype.setActiveTab=function(a){for(var b=this.header.getElementsByClassName("netgis-button"),c=this.content.getElementsByTagName("section"),d=0;d<b.length;d++){var e=b[d],f=c[d];d===a?(e.classList.add("netgis-color-e"),e.classList.add("netgis-text-a"),e.classList.add("netgis-bar-a"),e.classList.add("netgis-active"),e.scrollIntoView({behavior:"smooth"}),f.classList.remove("netgis-hide"),f.scrollTop=0):(e.classList.remove("netgis-color-e"),e.classList.remove("netgis-text-a"),e.classList.remove("netgis-bar-a"),
e.classList.remove("netgis-active"),f.classList.add("netgis-hide"))}};netgis.Tabs.prototype.getContentSection=function(a){return this.content.getElementsByTagName("section")[a]};netgis.Tabs.prototype.appendContent=function(a,b){this.content.getElementsByTagName("section")[a].appendChild(b)};
netgis.Tabs.prototype.updateHeaderScroll=function(){for(var a=0,b=this.header.getElementsByTagName("button"),c=0;c<b.length;c++){var d=b[c];a+=d.getBoundingClientRect().width;d.classList.contains("netgis-active")&&d.scrollIntoView()}b=this.header.getBoundingClientRect().width;a>b?this.container.classList.add("netgis-scroll"):this.container.classList.remove("netgis-scroll")};
netgis.Tabs.prototype.onHeaderButtonClick=function(a){a=a.currentTarget;for(var b=this.header.getElementsByClassName("netgis-button"),c=0,d=0;d<b.length;d++)if(b[d]===a){c=d;break}this.setActiveTab(c)};netgis=netgis||{};netgis.TimeSlider=function(a){this.config=a;this.insertTop=!0;this.initElements();for(a=1900;2E3>=a;a++);var b=this;window.setTimeout(function(){b.container.scrollLeft=0},1)};netgis.TimeSlider.Config={};
netgis.Tabs.prototype.onHeaderButtonClick=function(a){a=a.currentTarget;for(var b=this.header.getElementsByClassName("netgis-button"),c=0,d=0;d<b.length;d++)if(b[d]===a){c=d;break}this.setActiveTab(c)};netgis=netgis||{};netgis.TimeSlider=function(a){this.config=a;this.insertTop=!0;this.initElements();var b=this;window.setTimeout(function(){b.container.scrollLeft=0},1)};netgis.TimeSlider.Config={};
netgis.TimeSlider.prototype.initElements=function(){this.container=document.createElement("section");this.container.className="netgis-timeslider netgis-footer netgis-noselect netgis-color-e netgis-hide";document.addEventListener("pointermove",this.onPointerMove.bind(this));document.addEventListener("pointerup",this.onPointerUp.bind(this));this.header=document.createElement("button");this.header.className="netgis-header netgis-button netgis-clip-text netgis-color-a netgis-hover-c netgis-shadow";this.header.innerHTML=
"<i class='netgis-icon fas fa-clock'></i><span>TimeSlider</span><i class='netgis-icon fas fa-times'></i>";this.header.setAttribute("type","button");this.header.addEventListener("click",this.onHeaderClick.bind(this));this.container.appendChild(this.header);this.wrapper=document.createElement("div");this.wrapper.className="netgis-wrapper";this.wrapper.addEventListener("pointerdown",this.onPointerDown.bind(this));this.container.appendChild(this.wrapper);this.table=document.createElement("table");this.wrapper.appendChild(this.table);
this.top=document.createElement("tr");this.table.appendChild(this.top);this.bottom=document.createElement("tr")};netgis.TimeSlider.prototype.attachTo=function(a){a.appendChild(this.container);a.addEventListener(netgis.Events.TIMESLIDER_SHOW,this.onTimeSliderShow.bind(this));a.addEventListener(netgis.Events.TIMESLIDER_HIDE,this.onTimeSliderHide.bind(this))};
netgis.TimeSlider.prototype.setVisible=function(a){a?(this.container.classList.remove("netgis-hide"),this.container.parentNode.classList.add("netgis-footer")):(this.container.classList.add("netgis-hide"),this.container.parentNode.classList.remove("netgis-footer"))};netgis.TimeSlider.prototype.setTitle=function(a){this.header.getElementsByTagName("span")[0].innerHTML=a};netgis.TimeSlider.prototype.clearTimeSteps=function(){this.top.innerHTML=""};
netgis.TimeSlider.prototype.addTimeStep=function(a,b){var c=document.createElement("td");c.setAttribute("data-id",a);a=document.createElement("button");a.className="netgis-button netgis-color-e netgis-hover-d";a.innerHTML="<i class='netgis-icon netgis-text-a far fa-calendar'></i><span>"+b+"</span>";a.setAttribute("type","button");a.addEventListener("click",this.onTimeStepClick.bind(this));c.appendChild(a);this.top.appendChild(c)};
netgis.TimeSlider.prototype.addTimeStep_01=function(a,b){a=document.createElement("td");var c=document.createElement("td");this.insertTop?a.innerHTML=b:c.innerHTML=b;this.top.appendChild(a);this.bottom.appendChild(c);this.insertTop=!this.insertTop};
netgis.TimeSlider.prototype.setActiveTimeStep=function(a){var b=this.top.getElementsByTagName("td"),c=b[a],d=c.getAttribute("data-id");console.info("Set Active Step:",a,d);for(var e=0;e<b.length;e++){var f=b[e],g=f.getElementsByClassName("netgis-icon")[0];e===a?(f.classList.add("netgis-active"),g.classList.remove("fa-calendar"),g.classList.add("fa-calendar-check")):(f.classList.remove("netgis-active"),g.classList.add("fa-calendar"),g.classList.remove("fa-calendar-check"))}c.scrollIntoView();netgis.util.invoke(c,
netgis.Events.TIMESLIDER_SELECT,{layer:this.layerID,time:d})};netgis.TimeSlider.prototype.requestServiceWMST=function(a,b){a=a.trim();1>a.length||(-1===a.indexOf("GetCapabilities")&&(a+="&REQUEST=GetCapabilities"),netgis.util.request(a,this.onServiceResponseWMST.bind(this),{id:b}))};
netgis.TimeSlider.prototype.setActiveTimeStep=function(a){for(var b=this.top.getElementsByTagName("td"),c=b[a],d=c.getAttribute("data-id"),e=0;e<b.length;e++){var f=b[e],g=f.getElementsByClassName("netgis-icon")[0];e===a?(f.classList.add("netgis-active"),g.classList.remove("fa-calendar"),g.classList.add("fa-calendar-check")):(f.classList.remove("netgis-active"),g.classList.add("fa-calendar"),g.classList.remove("fa-calendar-check"))}c.scrollIntoView();netgis.util.invoke(c,netgis.Events.TIMESLIDER_SELECT,
{layer:this.layerID,time:d})};netgis.TimeSlider.prototype.requestServiceWMST=function(a,b){a=a.trim();1>a.length||(-1===a.indexOf("GetCapabilities")&&(a+="&REQUEST=GetCapabilities"),netgis.util.request(a,this.onServiceResponseWMST.bind(this),{id:b}))};
netgis.TimeSlider.prototype.onServiceResponseWMST=function(a,b){for(var c=(new DOMParser).parseFromString(a,"text/xml").documentElement.getElementsByTagName("Layer"),d,e=0;e<c.length;e++){a=c[e];var f=a.getElementsByTagName("Name")[0].textContent;if(f===b.id){d={name:f,title:a.getElementsByTagName("Title")[0].textContent,abstract:a.getElementsByTagName("Abstract")[0].textContent,queryable:"1"===a.getAttribute("queryable"),opaque:"1"===a.getAttribute("opaque")};e=a.getElementsByTagName("SRS");0<e.length&&
(d.projection=e[0].textContent);e=a.getElementsByTagName("CRS");0<e.length&&(d.projection=e[0].textContent);e=a.getElementsByTagName("BoundingBox")[0];d.bbox=[Number.parseFloat(e.getAttribute("minx")),Number.parseFloat(e.getAttribute("miny")),Number.parseFloat(e.getAttribute("maxx")),Number.parseFloat(e.getAttribute("maxy"))];b=a.getElementsByTagName("Dimension");for(e=0;e<b.length;e++)if(c=b[e],"time"===c.getAttribute("name")){var g={defaultTime:c.getAttribute("default"),extent:c.textContent.split("/")};
break}a=a.getElementsByTagName("Extent");for(e=0;e<a.length;e++)if(b=a[e],"time"===b.getAttribute("name")){g.defaultTime=b.getAttribute("default");g.extent=b.textContent.split("/");break}d.time=g;break}}console.info("WMST Layer:",d);this.setTitle(d.title);if(d.time.extent){g=new Date(d.time.extent[0]);a=new Date(d.time.extent[1]);b=d.time.extent[2];d=[];switch(b){case "P1D":for(;g<=a;)d.push(new Date(g)),g.setDate(g.getDate()+1);break;default:console.error("unsupported WMST date range",b,g,a)}for(e=
0;e<d.length;e++)b=d[e],a=g=b.toISOString(),a=a.replace("T",", "),a=a.replace("Z",""),this.addTimeStep(g,a);this.setActiveTimeStep(d.length-1)}};netgis.TimeSlider.prototype.onTimeSliderShow=function(a){a=a.detail;console.info("TimeSlider Show:",a);this.layerID=a.layer;this.setTitle(a.title);this.clearTimeSteps();this.requestServiceWMST(a.url,a.name);this.setVisible(!0)};netgis.TimeSlider.prototype.onTimeSliderHide=function(a){this.setVisible(!1)};netgis.TimeSlider.prototype.onHeaderClick=function(a){this.setVisible(!1)};
break}a=a.getElementsByTagName("Extent");for(e=0;e<a.length;e++)if(b=a[e],"time"===b.getAttribute("name")){g.defaultTime=b.getAttribute("default");g.extent=b.textContent.split("/");break}d.time=g;break}}this.setTitle(d.title);if(d.time.extent){g=new Date(d.time.extent[0]);a=new Date(d.time.extent[1]);b=d.time.extent[2];d=[];switch(b){case "P1D":for(;g<=a;)d.push(new Date(g)),g.setDate(g.getDate()+1);break;default:console.error("unsupported WMST date range",b,g,a)}for(e=0;e<d.length;e++)b=d[e],a=g=
b.toISOString(),a=a.replace("T",", "),a=a.replace("Z",""),this.addTimeStep(g,a);this.setActiveTimeStep(d.length-1)}};netgis.TimeSlider.prototype.onTimeSliderShow=function(a){a=a.detail;this.layerID=a.layer;this.setTitle(a.title);this.clearTimeSteps();this.requestServiceWMST(a.url,a.name);this.setVisible(!0)};netgis.TimeSlider.prototype.onTimeSliderHide=function(a){this.setVisible(!1)};netgis.TimeSlider.prototype.onHeaderClick=function(a){this.setVisible(!1)};
netgis.TimeSlider.prototype.onPointerDown=function(a){a=a.pageX-this.wrapper.offsetLeft;this.down=!0;this.downX=a;this.downScroll=this.wrapper.scrollLeft;this.container.classList.add("netgis-active")};netgis.TimeSlider.prototype.onPointerMove=function(a){this.down&&(a.preventDefault(),this.wrapper.scrollLeft=this.downScroll-(a.pageX-this.wrapper.offsetLeft-this.downX))};netgis.TimeSlider.prototype.onPointerUp=function(a){this.down&&a.preventDefault();this.down=!1;this.container.classList.remove("netgis-active")};
netgis.TimeSlider.prototype.onTimeStepClick=function(a){a=a.currentTarget.parentNode;a.getAttribute("data-id");if(!(5<Math.abs(this.wrapper.scrollLeft-this.downScroll))){for(var b=this.top.getElementsByTagName("td"),c=-1,d=0;d<b.length;d++)if(b[d]===a){c=d;break}this.setActiveTimeStep(c)}};netgis=netgis||{};netgis.Toolbox=function(a){this.config=a;this.bottomPanels={};this.initElements(a);this.initOptions(a);this.initEvents();this.setActiveButton(netgis.Commands.VIEW)};netgis.Toolbox.Config={open:!1,items:[],options:{buffer_features:{title:"Buffer",items:[{id:"buffer_radius",type:"integer",title:"Radius (Meter)"},{id:"buffer_segments",type:"integer",title:"Segments"},{id:"buffer_submit",type:"button",title:"<i class='netgis-icon netgis-text-a fas fa-arrow-circle-right'></i><span>Akzeptieren</span>"}]}}};
netgis.Toolbox.prototype.initElements=function(a){this.panel=new netgis.Panel("Toolbox");this.panel.content.classList.add("netgis-toolbox");this.top=document.createElement("section");this.panel.content.appendChild(this.top);for(var b=a.toolbox.items,c=0;c<b.length;c++){var d=b[c];if(this.config.tools&&!1===this.config.tools.editable)switch(d.id){case "draw_points":continue;case "draw_lines":continue;case "draw_polygons":continue;case "modify_features":continue;case "delete_features":continue;case "buffer_features":continue;
@@ -517,9 +514,9 @@ netgis.Tree.prototype.setFolderOpen=function(a,b){var c=this.container.getElemen
netgis.Tree.prototype.setItemChecked=function(a,b,c){var d=this.container.getElementsByClassName("netgis-item");a=a.toString();for(var e=0;e<d.length;e++){var f=d[e],g=f.getElementsByTagName("input")[0];if(g.getAttribute("data-id")===a){if("radio"===g.getAttribute("type")){f=f.parentNode.getElementsByTagName("input");for(var h=0;h<f.length;h++){var k=f[h];k!==g&&"radio"===k.getAttribute("type")&&!1!==k.checked&&(k.checked=!1,netgis.util.invoke(g,netgis.Events.TREE_ITEM_CHANGE,{id:k.getAttribute("data-id"),
checked:!1}))}}c?g.checked=b:g.checked!==b&&g.click()}}};netgis.Tree.prototype.setFolderParent=function(a,b){var c=a.parentNode;c&&c.removeChild(a);null!==b?b.getElementsByTagName("ul")[0].appendChild(a):this.container.appendChild(a)};netgis.Tree.prototype.updateFolderChecks=function(){for(var a=this.container.getElementsByClassName("netgis-folder"),b=0;b<a.length;b++)this.updateFolderCheck(a[b])};
netgis.Tree.prototype.updateFolderCheck=function(a){a||(a=this.container);for(var b=a.getElementsByClassName("netgis-item"),c=0,d=0;d<b.length;d++){var e=b[d].getElementsByTagName("input")[0];e.checked&&c++}e=a.getElementsByTagName("input")[0];d=0;0<c&&(d=1);0<c&&c===b.length&&(d=2);switch(d){case 0:e.checked=!1;e.classList.remove("netgis-partial");break;case 1:e.checked=!0;e.classList.add("netgis-partial");break;case 2:e.checked=!0,e.classList.remove("netgis-partial")}(a=a.parentElement)&&a!==this.container&&
(a=a.parentElement.parentElement)&&-1!==a.className.search("netgis-folder")&&this.updateFolderCheck(a)};netgis.Tree.prototype.onFolderChange=function(a){var b=a.currentTarget;a=b.checked;b=b.parentElement.parentElement.parentElement.parentElement;for(var c=b.getElementsByTagName("input"),d=1;d<c.length;d++){var e=c[d];e.checked!==a&&e.click()}this.updateFolderCheck(b);a=b.parentElement.parentElement.parentElement;-1!==a.className.search("netgis-folder")&&this.updateFolderCheck(a)};
netgis.Tree.prototype.onItemChange=function(a){a=a.currentTarget;var b=a.checked,c=a.getAttribute("data-id"),d=a.parentElement.parentElement.parentElement.parentElement.parentElement;if("radio"===a.getAttribute("type"))for(var e=a.parentNode.parentNode.parentNode.parentNode.getElementsByTagName("input"),f=0;f<e.length;f++){var g=e[f].getAttribute("data-id");g&&g!==c&&netgis.util.invoke(a,netgis.Events.TREE_ITEM_CHANGE,{id:g,checked:!1})}netgis.util.invoke(a,netgis.Events.TREE_ITEM_CHANGE,{id:c,checked:b});
-1!==d.className.search("netgis-folder")&&this.updateFolderCheck(d)};netgis.Tree.prototype.onRadioChange=function(a){a=a.currentTarget;var b=a.checked,c=a.getAttribute("data-id"),d=Number.parseInt(c);Number.isNaN(d)||(c=d);d=a.parentElement.parentElement.parentElement.parentElement.parentElement;netgis.util.invoke(a,netgis.Events.TREE_ITEM_CHANGE,{id:c,checked:b});-1!==d.className.search("netgis-folder")&&this.updateFolderCheck(d)};
(a=a.parentElement.parentElement)&&-1!==a.className.search("netgis-folder")&&this.updateFolderCheck(a)};netgis.Tree.prototype.onFolderChange=function(a){var b=a.currentTarget;a=b.checked;b=b.parentElement.parentElement.parentElement.parentElement.getElementsByTagName("input");for(var c=1;c<b.length;c++){var d=b[c];d.checked!==a&&d.click()}this.updateFolderChecks()};
netgis.Tree.prototype.onItemChange=function(a){a=a.currentTarget;var b=a.checked,c=a.getAttribute("data-id");if("radio"===a.getAttribute("type"))for(var d=a.parentNode.parentNode.parentNode.parentNode.getElementsByTagName("input"),e=0;e<d.length;e++){var f=d[e].getAttribute("data-id");f&&f!==c&&netgis.util.invoke(a,netgis.Events.TREE_ITEM_CHANGE,{id:f,checked:!1})}netgis.util.invoke(a,netgis.Events.TREE_ITEM_CHANGE,{id:c,checked:b});this.updateFolderChecks()};
netgis.Tree.prototype.onRadioChange=function(a){a=a.currentTarget;var b=a.checked,c=a.getAttribute("data-id"),d=Number.parseInt(c);Number.isNaN(d)||(c=d);d=a.parentElement.parentElement.parentElement.parentElement.parentElement;netgis.util.invoke(a,netgis.Events.TREE_ITEM_CHANGE,{id:c,checked:b});-1!==d.className.search("netgis-folder")&&this.updateFolderCheck(d)};
netgis.Tree.prototype.onButtonClick=function(a){a=a.currentTarget;var b=a.getAttribute("data-id");netgis.util.invoke(a,netgis.Events.TREE_BUTTON_CLICK,{id:b})};netgis.Tree.prototype.onItemSliderChange=function(a){a=a.currentTarget;var b=a.getAttribute("data-id");netgis.util.invoke(a,netgis.Events.TREE_ITEM_SLIDER_CHANGE,{id:b,val:Number.parseFloat(a.value)})};
netgis.Tree.prototype.onDragStart=function(a){a.stopPropagation();var b=a.currentTarget,c=b.getElementsByTagName("input")[0].getAttribute("data-id");b.classList.add("netgis-dragging");a.dataTransfer.setData("text/plain",c);a.dataTransfer.dropEffect="move";this.dragElement=b};netgis.Tree.prototype.onDragEnter=function(a){a=a.currentTarget;a!==this.dragElement&&a.classList.add("netgis-dragover")};
netgis.Tree.prototype.onDragOver=function(a){a.preventDefault();a=a.currentTarget;a!==this.dragElement&&a.classList.add("netgis-dragover")};netgis.Tree.prototype.onDragLeave=function(a){a.currentTarget.classList.remove("netgis-dragover")};
@@ -547,13 +544,7 @@ netgis.WMC.prototype.requestLayers=function(a){var b=this.config.wmc;b?(b=b.laye
netgis.WMC.prototype.toConfig=function(){var a=this.data.wmc,b=this.data.layerList,c=this.layers.wms.srv,d={},e=a.bbox;e=e.split(",");for(var f=0;f<e.length;f++)e[f]=Number.parseFloat(e[f]);d.map={projection:a.crs,bbox:e};d.attribution||(d.attribution={});d.attribution.prefix=d.attribution.prefix?a.title+", "+d.attribution.prefix:a.title;a=d.folders=[];e=d.layers=[];this.parseServiceLayers(c,b,a,e);b=1E3;for(c=0;c<a.length;c++){f=a[c];for(var g=e.length-1;0<=g;g--){var h=e[g];h.folder===f.id&&(h.order=
b,b+=1)}}return d};
netgis.WMC.prototype.parseServiceLayers=function(a,b,c,d){c||(c=[]);d||(d=[]);for(var e=0;e<a.length;e++)for(var f=a[e],g=0;g<f.layer.length;g++){var h=f.layer[g];h.isRoot&&c.push({id:h.id,title:h.title,open:"1"===f.isopen});var k=h.layer;if(k){b&&k.sort(function(a,c){a=a.id;var d=c.id,e=null;c=null;for(var f=0;f<b.length;f++){var g=b[f];g.layerId.toString()===a&&(e=g);g.layerId.toString()===d&&(c=g)}a=e.layerPos;c=c.layerPos;return a<c?-1:a>c?1:0});for(var l=k.length-1;0<=l;l--){var m=k[l],n=m.id,
p=null;if(b)for(var q=0;q<b.length;q++)if(b[q].layerId.toString()===n){p=b[q];break}m=this.parseServiceLayer(n,f,h.id,m,p);d.push(m)}}}return{folders:c,layers:d}};netgis.WMC.prototype.parseServiceLayer=function(a,b,c,d,e){return{id:a,folder:c,title:d.title,active:e?e.active:!0,query:1===d.queryable,transparency:e?1-.01*e.opacity:0,order:1E3,type:netgis.LayerTypes.WMS,url:b.getMapUrl,name:d.name,format:e?e.currentFormat:"image/png"}};
netgis.WMC.prototype.toConfig_02=function(){var a=this.data.wmc,b=this.layers.wms.srv,c=this.data.layerList,d={},e=a.bbox;e=e.split(",");for(var f=0;f<e.length;f++)e[f]=Number.parseFloat(e[f]);d.map={projection:a.crs,bbox:e};d.attribution||(d.attribution={});d.attribution.prefix=d.attribution.prefix?a.title+", "+d.attribution.prefix:a.title;a=d.folders=[];e=d.layers=[];var g=[];for(f=0;f<c.length;f++){var h=c[f],k={id:Number.parseInt(h.layerId),type:"layer",order:h.layerPos,active:h.active,opacity:h.opacity};
g.push(k)}for(f=0;f<b.length;f++){c=b[f];k={id:Number.parseInt(c.id),type:"service",title:c.title,url:c.getMapUrl,open:"1"===c.isopen};g.push(k);for(var l=0;l<c.layer.length;l++){h=c.layer[l];var m=this.parseLayer(h,k.id,g);if(h.layer)for(var n=0;n<h.layer.length;n++){var p=h.layer[n],q=this.parseLayer(p,m.id,g);if(p.layer)for(var r=0;r<p.layer.length;r++){var t=p.layer[r],u=this.parseLayer(t,q.id,g);if(t.layer)for(var v=0;v<t.layer.length;v++)this.parseLayer(t.layer[v],u.id,g)}}}}console.info("WMC ITEMS:",
g);for(f=0;f<g.length;f++)switch(k=g[f],k.type){case "service":a.push({id:k.id,title:k.title,open:k.open});break;case "layer":h={},e.push(h)}return d};
q=null;if(b)for(var p=0;p<b.length;p++)if(b[p].layerId.toString()===n){q=b[p];break}m=this.parseServiceLayer(n,f,h.id,m,q);d.push(m)}}}return{folders:c,layers:d}};netgis.WMC.prototype.parseServiceLayer=function(a,b,c,d,e){return{id:a,folder:c,title:d.title,active:e?e.active:!0,query:1===d.queryable,transparency:e?1-.01*e.opacity:0,order:1E3,type:netgis.LayerTypes.WMS,url:b.getMapUrl,name:d.name,format:e?e.currentFormat:"image/png"}};
netgis.WMC.prototype.parseLayer=function(a,b,c){for(var d=null,e=0;e<c.length;e++)if(c[e].id===Number.parseInt(a.id)){d=c[e];break}d||(d={id:Number.parseInt(a.id),type:"layer"},c.push(d));d.title=a.title;d.name=a.name;d.parent=b;a.getLegendGraphicUrl&&a.getLegendGraphicUrlFormat&&(d.legendURL=a.getLegendGraphicUrl,d.legendFormat=a.getLegendGraphicUrlFormat);a.legendUrl&&(d.legendURL=window.decodeURIComponent(a.legendUrl),d.legendFormat=a.getLegendGraphicUrlFormat);d.queryable=1===a.layerQueryable||
1===a.queryable?!0:!1;if(a.bbox){a=a.bbox.split(",");for(b=0;b<a.length;b++)a[b]=parseFloat(a[b]);d.bbox=[a[0],a[1],a[2],a[3]]}return d};
netgis.WMC.prototype.toConfig_01=function(){var a=this.data.wmc,b=this.layers.wms.srv,c=this.data.layerList,d={},e=a.bbox;e=e.split(",");for(var f=0;f<e.length;f++)e[f]=Number.parseFloat(e[f]);d.map={attribution:a.title+", GeoPortal RLP",projection:a.crs,bbox:e};e=[];var g={};for(f=0;f<b.length;f++){var h=b[f],k={id:h.id,title:h.title,open:"1"===h.isopen};e.push(k);for(k=0;k<h.layer.length;k++){var l=h.layer[k];l.getMapUrl||(l.getMapUrl=h.getMapUrl);g[l.id.toString()]=l;if(l.layer)for(var m=0;m<l.layer.length;m++){var n=
l.layer[m];n.getMapUrl||(n.getMapUrl=l.getMapUrl);g[n.id.toString()]=n}}}console.info("WMC FOLDERS:",b,"---\x3e",e);b=[];h=9999;(this.kmloverlay=a.kmloverlay)&&""!==this.kmloverlay&&(k={id:"kmloverlay",title:"Meine Geodaten",parent:null},e.push(k),l={id:"kmloverlay",folder:"kmloverlay",title:"KML Overlay",active:!0,order:h,type:netgis.LayerTypes.KML,url:this.kmloverlay,query:!0},b.push(l),--h);for(f=c.length-1;0<=f;f--)a=c[f],m=a.layerId.toString(),(l=g[m])&&!l.isRoot&&(k=null!==a.layerParent?e[a.layerParent].id:
null,l={id:m,folder:k,title:l.title,active:a.active,query:1===a.layerQueryable,transparency:1-.01*a.opacity,order:h,type:netgis.LayerTypes.WMS,url:l.getMapUrl,name:l.name,format:a.currentFormat},"image/tiff"===l.format&&(l.format="image/png"),b.push(l),--h);d.folders=e;d.layers=b;return d};
netgis.build="20251010";
netgis.build="20251027";

View File

@@ -56,7 +56,7 @@
{% if user.is_staff or user.is_superuser %}
<a class="dropdown-item" target="_blank" href="{% url 'admin:index' %}">{% fa5_icon 'tools' %} {% trans 'Admin' %}</a>
{% endif %}
<a class="dropdown-item" href="{% url 'user:detail' %}">{% fa5_icon 'cogs' %} {% trans 'Settings' %}</a>
<a class="dropdown-item" href="{% url 'user:index' %}">{% fa5_icon 'cogs' %} {% trans 'Settings' %}</a>
<a class="dropdown-item" href="{% url 'logout' %}">{% fa5_icon 'sign-out-alt' %} {% trans 'Logout' %}</a>
</div>
</li>

View File

@@ -38,7 +38,7 @@ class UserNotificationForm(BaseForm):
self.form_title = _("Edit notifications")
self.form_caption = _("")
self.action_url = reverse("user:notifications")
self.cancel_redirect = reverse("user:detail")
self.cancel_redirect = reverse("user:index")
# Insert all notifications into form field by creating choices as tuples
notifications = UserNotification.objects.filter(

View File

@@ -26,7 +26,7 @@ class UserViewTestCase(BaseViewTestCase):
self.team.users.add(self.superuser)
self.team.admins.add(self.superuser)
# Prepare urls
self.index_url = reverse("user:detail", args=())
self.index_url = reverse("user:index", args=())
self.notification_url = reverse("user:notifications", args=())
self.api_token_url = reverse("user:api-token", args=())
self.contact_url = reverse("user:contact", args=(self.superuser.id,))

View File

@@ -233,7 +233,7 @@ class UserNotificationFormTestCase(BaseTestCase):
self.assertEqual(form.form_title, str(_("Edit notifications")))
self.assertEqual(form.form_caption, "")
self.assertEqual(form.action_url, reverse("user:notifications"))
self.assertEqual(form.cancel_redirect, reverse("user:detail"))
self.assertEqual(form.cancel_redirect, reverse("user:index"))
def test_save(self):
selected_notification = UserNotification.objects.first()

View File

@@ -15,15 +15,15 @@ from user.views.views import *
app_name = "user"
urlpatterns = [
path("", UserDetailView.as_view(), name="detail"),
path("", index_view, name="index"),
path("propagate/", PropagateUserView.as_view(), name="propagate"),
path("notifications/", NotificationsView.as_view(), name="notifications"),
path("notifications/", notifications_view, name="notifications"),
path("token/api", APITokenView.as_view(), name="api-token"),
path("token/api/new", new_api_token_view, name="api-token-new"),
path("contact/<id>", ContactView.as_view(), name="contact"),
path("team/", TeamIndexView.as_view(), name="team-index"),
path("contact/<id>", contact_view, name="contact"),
path("team/", index_team_view, name="team-index"),
path("team/new", new_team_view, name="team-new"),
path("team/<id>", TeamDetailModalView.as_view(), name="team-data"),
path("team/<id>", data_team_view, name="team-data"),
path("team/<id>/edit", edit_team_view, name="team-edit"),
path("team/<id>/remove", remove_team_view, name="team-remove"),
path("team/<id>/leave", leave_team_view, name="team-leave"),

View File

@@ -1,10 +1,8 @@
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.views.base import BaseView, BaseModalFormView
from user.forms.modals.team import NewTeamModalForm, EditTeamModalForm, RemoveTeamModalForm, LeaveTeamModalForm
from user.forms.modals.user import UserContactForm
from user.forms.team import TeamDataForm
@@ -15,132 +13,129 @@ from django.shortcuts import render, redirect, get_object_or_404
from django.utils.translation import gettext_lazy as _
from konova.contexts import BaseContext
from konova.decorators import login_required_modal
from konova.decorators import any_group_check, login_required_modal
class UserBaseView(BaseView):
def _user_has_shared_access(self, user, **kwargs):
return True
@login_required
@any_group_check
def index_view(request: HttpRequest):
""" Renders the user's data index view
def _user_has_permission(self, user):
return True
Args:
request ():
Returns:
"""
template = "user/index.html"
context = {
"user": request.user,
TAB_TITLE_IDENTIFIER: _("User settings"),
}
context = BaseContext(request, context).context
return render(request, template, context)
class UserDetailView(LoginRequiredMixin, UserBaseView):
_TEMPLATE = "user/index.html"
_TAB_TITLE = _("User settings")
@login_required
@any_group_check
def notifications_view(request: HttpRequest):
""" Renders the notifications settings view
def get(self, request: HttpRequest):
context = {
"user": request.user,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
Args:
request ():
Returns:
class NotificationsView(LoginRequiredMixin, UserBaseView):
_TEMPLATE = "user/notifications.html"
_TAB_TITLE = _("User notifications")
"""
template = "user/notifications.html"
user = request.user
def get(self, request: HttpRequest):
user = request.user
form = UserNotificationForm(user=user, data=None)
context = {
"user": user,
"form": form,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
def post(self, request: HttpRequest):
user = request.user
form = UserNotificationForm(user=user, data=request.POST)
form = UserNotificationForm(user=user, data=request.POST or None)
if request.method == "POST":
if form.is_valid():
form.save()
messages.success(
request,
_("Notifications edited")
)
return redirect("user:detail")
context = {
"user": user,
"form": form,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
return redirect("user:index")
elif request.method == "GET":
# Implicit
pass
else:
raise NotImplementedError
context = {
"user": user,
"form": form,
TAB_TITLE_IDENTIFIER: _("User notifications"),
}
context = BaseContext(request, context).context
return render(request, template, context)
class ContactView(LoginRequiredMixin, BaseModalFormView):
def get(self, request: HttpRequest, id: str):
""" Renders contact modal view of a users contact data
@login_required_modal
@login_required
def contact_view(request: HttpRequest, id: str):
""" Renders contact modal view of a users contact data
Args:
request (HttpRequest): The incoming request
id (str): The user's id
Args:
request (HttpRequest): The incoming request
id (str): The user's id
Returns:
Returns:
"""
user = get_object_or_404(User, id=id)
form = UserContactForm(request.POST or None, instance=user, request=request)
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
def _user_has_shared_access(self, user, **kwargs):
# No specific constraints
return True
def _user_has_permission(self, user):
# No specific constraints
return True
"""
user = get_object_or_404(User, id=id)
form = UserContactForm(request.POST or None, instance=user, request=request)
template = "modal/modal_form.html"
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(
request,
template,
context
)
class TeamDetailModalView(LoginRequiredMixin, BaseModalFormView):
def get(self, request: HttpRequest, id: str):
""" Renders team data
@login_required_modal
@login_required
def data_team_view(request: HttpRequest, id: str):
""" Renders team data
Args:
request (HttpRequest): The incoming request
id (str): The team's id
Args:
request (HttpRequest): The incoming request
id (str): The team's id
Returns:
Returns:
"""
team = get_object_or_404(Team, id=id)
form = TeamDataForm(request.POST or None, instance=team, request=request)
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
def _user_has_shared_access(self, user, **kwargs):
# No specific constraints
return True
def _user_has_permission(self, user):
# No specific constraints
return True
"""
team = get_object_or_404(Team, id=id)
form = TeamDataForm(request.POST or None, instance=team, request=request)
template = "modal/modal_form.html"
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(
request,
template,
context
)
class TeamIndexView(LoginRequiredMixin, UserBaseView):
_TEMPLATE = "user/team/index.html"
_TAB_TITLE = _("Teams")
def get(self, request: HttpRequest):
user = request.user
context = {
"teams": user.shared_teams,
"tab_title": self._TAB_TITLE,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@login_required
def index_team_view(request: HttpRequest):
template = "user/team/index.html"
user = request.user
context = {
"teams": user.shared_teams,
"tab_title": _("Teams"),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required_modal