diff --git a/src/c3nav/access/apply.py b/src/c3nav/access/apply.py index 4f1c95ce..7f3f09d2 100644 --- a/src/c3nav/access/apply.py +++ b/src/c3nav/access/apply.py @@ -1,5 +1,6 @@ from django.conf import settings +from c3nav.mapdata.inclusion import get_maybe_invisible_areas_names from c3nav.mapdata.utils.cache import get_packages_cached @@ -29,3 +30,9 @@ def can_access_package(request, package): def filter_queryset_by_package_access(request, queryset): return queryset if request.c3nav_full_access else queryset.filter(package__in=get_unlocked_packages(request)) + + +def get_visible_areas(request): + areas = [':full' if request.c3nav_full_access else ':base'] + areas += [name for name in get_maybe_invisible_areas_names() if name in request.c3nav_access_list] + return areas diff --git a/src/c3nav/access/middleware.py b/src/c3nav/access/middleware.py index 627a8f72..81b09440 100644 --- a/src/c3nav/access/middleware.py +++ b/src/c3nav/access/middleware.py @@ -1,6 +1,7 @@ import re from datetime import timedelta +from django.conf import settings from django.db import transaction from django.db.models import Q from django.utils import timezone @@ -17,8 +18,8 @@ class AccessTokenMiddleware: request.c3nav_access = None request.c3nav_new_access = False - request.c3nav_full_access = False - request.c3nav_access_list = None + request.c3nav_full_access = settings.DEBUG + request.c3nav_access_list = () access_cookie = request.COOKIES.get('c3nav_access') if access_cookie and re.match(r'^[0-9]+:[a-zA-Z0-9]+$', access_cookie): diff --git a/src/c3nav/mapdata/api.py b/src/c3nav/mapdata/api.py index 05b0290f..18adaf10 100644 --- a/src/c3nav/mapdata/api.py +++ b/src/c3nav/mapdata/api.py @@ -12,7 +12,7 @@ from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet from c3nav.access.apply import filter_queryset_by_package_access, get_unlocked_packages_names from c3nav.mapdata.models import GEOMETRY_MAPITEM_TYPES, AreaLocation, Level, LocationGroup, Package, Source from c3nav.mapdata.models.geometry import DirectedLineGeometryMapItemWithLevel -from c3nav.mapdata.models.locations import get_location +from c3nav.mapdata.search import get_location from c3nav.mapdata.serializers.main import LevelSerializer, PackageSerializer, SourceSerializer from c3nav.mapdata.utils.cache import (CachedReadOnlyViewSetMixin, cache_mapdata_api_response, get_levels_cached, get_packages_cached) diff --git a/src/c3nav/mapdata/inclusion.py b/src/c3nav/mapdata/inclusion.py index 2cf9c518..73d57b05 100644 --- a/src/c3nav/mapdata/inclusion.py +++ b/src/c3nav/mapdata/inclusion.py @@ -1,9 +1,7 @@ from collections import OrderedDict -from django.conf import settings from django.utils.translation import ugettext_lazy as _ -from c3nav.access.apply import can_access_package from c3nav.mapdata.models import AreaLocation, LocationGroup @@ -29,20 +27,21 @@ def get_includables_avoidables(request): locations = list(AreaLocation.objects.exclude(routing_inclusion='default')) locations += list(LocationGroup.objects.exclude(routing_inclusion='default')) - if settings.DEBUG: + if request.c3nav_full_access: includables.append((':nonpublic', _('non-public areas'))) avoidables.append((':public', _('public areas'))) + from c3nav.access.apply import can_access_package + for location in locations: item = (location.location_id, location.title) - # Todo: allow by access token if not can_access_package(request, location.package): continue - # Todo: allow by access token - if location.routing_inclusion == 'needs_permission' and not settings.DEBUG: - continue + if location.routing_inclusion == 'needs_permission': + if not request.c3nav_full_access and location.location_id not in request.c3nav_access_list: + continue if location.routing_inclusion == 'allow_avoid': avoidables.append(item) @@ -52,6 +51,14 @@ def get_includables_avoidables(request): return OrderedDict(includables), OrderedDict(avoidables) +def get_maybe_invisible_areas(): + return AreaLocation.objects.exclude(routing_inclusion='default') + + +def get_maybe_invisible_areas_names(): + return tuple(area.name for area in get_maybe_invisible_areas()) + + def parse_include_avoid(request, include_input, avoid_input): includable, avoidable = get_includables_avoidables(request) include_input = set(include_input) & set(includable) diff --git a/src/c3nav/mapdata/models/locations.py b/src/c3nav/mapdata/models/locations.py index ce06af0f..36c4f8c9 100644 --- a/src/c3nav/mapdata/models/locations.py +++ b/src/c3nav/mapdata/models/locations.py @@ -1,20 +1,16 @@ -import re from collections import OrderedDict import numpy as np from django.core.cache import cache from django.db import models -from django.db.models import Q from django.utils.functional import cached_property from django.utils.translation import ugettext_lazy as _ -from c3nav.access.apply import filter_queryset_by_package_access from c3nav.mapdata.fields import JSONField from c3nav.mapdata.lastupdate import get_last_mapdata_update from c3nav.mapdata.models import Level from c3nav.mapdata.models.base import MapItem from c3nav.mapdata.models.geometry import GeometryMapItemWithLevel -from c3nav.mapdata.utils.cache import get_levels_cached class Location: @@ -238,49 +234,6 @@ class AreaLocation(LocationModelMixin, GeometryMapItemWithLevel): return self.title -def get_location(request, name): - match = re.match('^c:(?P[a-z0-9-_]+):(?P[0-9]+):(?P[0-9]+)$', name) - if match: - levels = get_levels_cached() - level = levels.get(match.group('level')) - if level is None: - return None - return PointLocation(level=level, x=int(match.group('x'))/100, y=int(match.group('y'))/100) - - if name.startswith('g:'): - return filter_queryset_by_package_access(request, LocationGroup.objects.filter(name=name[2:])).first() - - return filter_queryset_by_package_access(request, AreaLocation.objects.filter(name=name)).first() - - -def filter_words(queryset, words): - for word in words: - queryset = queryset.filter(Q(name__icontains=word) | Q(titles__icontains=word)) - return queryset - - -def search_location(request, search): - results = [] - location = get_location(request, search) - if location: - results.append(location) - - words = search.split(' ')[:10] - - queryset = AreaLocation.objects.all() - if isinstance(location, AreaLocation): - queryset.exclude(name=location.name) - results += sorted(filter_words(filter_queryset_by_package_access(request, queryset), words), - key=AreaLocation.get_sort_key, reverse=True) - - queryset = LocationGroup.objects.all() - if isinstance(location, LocationGroup): - queryset.exclude(name=location.name) - results += list(filter_words(filter_queryset_by_package_access(request, queryset), words)[:10]) - - return results - - class PointLocation(Location): def __init__(self, level: Level, x: int, y: int): self.level = level diff --git a/src/c3nav/mapdata/render/compose.py b/src/c3nav/mapdata/render/compose.py deleted file mode 100644 index 3c2b75be..00000000 --- a/src/c3nav/mapdata/render/compose.py +++ /dev/null @@ -1,23 +0,0 @@ -import mimetypes - -from django.conf import settings -from django.core.files import File -from django.http import HttpResponse - -from c3nav.mapdata.utils.misc import get_render_path - - -class LevelComposer: - def get_level_image(self, request, level): - if settings.DIRECT_EDITING: - img = get_render_path('png', level.name, 'full', True) - else: - img = get_render_path('png', level.name, 'full', False) - - response = HttpResponse(content_type=mimetypes.guess_type(img)[0]) - for chunk in File(open(img, 'rb')).chunks(): - response.write(chunk) - return response - - -composer = LevelComposer() diff --git a/src/c3nav/mapdata/search.py b/src/c3nav/mapdata/search.py new file mode 100644 index 00000000..8289e959 --- /dev/null +++ b/src/c3nav/mapdata/search.py @@ -0,0 +1,51 @@ +import re + +from django.db.models import Q + +from c3nav.access.apply import filter_queryset_by_package_access +from c3nav.mapdata.models import AreaLocation, LocationGroup +from c3nav.mapdata.models.locations import PointLocation +from c3nav.mapdata.utils.cache import get_levels_cached + + +def get_location(request, name): + match = re.match('^c:(?P[a-z0-9-_]+):(?P[0-9]+):(?P[0-9]+)$', name) + if match: + levels = get_levels_cached() + level = levels.get(match.group('level')) + if level is None: + return None + return PointLocation(level=level, x=int(match.group('x'))/100, y=int(match.group('y'))/100) + + if name.startswith('g:'): + return filter_queryset_by_package_access(request, LocationGroup.objects.filter(name=name[2:])).first() + + return filter_queryset_by_package_access(request, AreaLocation.objects.filter(name=name)).first() + + +def filter_words(queryset, words): + for word in words: + queryset = queryset.filter(Q(name__icontains=word) | Q(titles__icontains=word)) + return queryset + + +def search_location(request, search): + results = [] + location = get_location(request, search) + if location: + results.append(location) + + words = search.split(' ')[:10] + + queryset = AreaLocation.objects.all() + if isinstance(location, AreaLocation): + queryset.exclude(name=location.name) + results += sorted(filter_words(filter_queryset_by_package_access(request, queryset), words), + key=AreaLocation.get_sort_key, reverse=True) + + queryset = LocationGroup.objects.all() + if isinstance(location, LocationGroup): + queryset.exclude(name=location.name) + results += list(filter_words(filter_queryset_by_package_access(request, queryset), words)[:10]) + + return results diff --git a/src/c3nav/site/static/site/css/c3nav.css b/src/c3nav/site/static/site/css/c3nav.css index 1214edd3..550bc462 100644 --- a/src/c3nav/site/static/site/css/c3nav.css +++ b/src/c3nav/site/static/site/css/c3nav.css @@ -86,7 +86,7 @@ body, .btn { .locationselect-map .scroll-hint { color:#FFFFFF; position:absolute; - top:150px; + top:250px; left:16px; } diff --git a/src/c3nav/site/static/site/js/c3nav.js b/src/c3nav/site/static/site/js/c3nav.js index 01edf2a5..b0ad5e21 100644 --- a/src/c3nav/site/static/site/js/c3nav.js +++ b/src/c3nav/site/static/site/js/c3nav.js @@ -5,6 +5,7 @@ c3nav = { c3nav.svg_width = parseInt(c3nav.main_view.attr('data-svg-width')); c3nav.svg_height = parseInt(c3nav.main_view.attr('data-svg-height')); + c3nav.visible_areas = c3nav.main_view.attr('data-visible-areas').split(';'); c3nav._typeahead_locations = new Bloodhound({ datumTokenizer: function(data) { @@ -77,11 +78,13 @@ c3nav = { $(this).siblings().removeClass('active'); $(this).addClass('active'); map_container.find('img').remove(); - map_container.append($('').attr({ - 'src': '/map/'+level+'.png', - 'width': c3nav.svg_width, - 'height': c3nav.svg_height - })); + for (var i=0;i').attr({ + 'src': '/map/'+level+'/'+c3nav.visible_areas[i]+'.png', + 'width': c3nav.svg_width, + 'height': c3nav.svg_height + })); + } map_container.attr('data-level', level); }, _locationselect_click_image: function(e) { diff --git a/src/c3nav/site/templates/site/fragment_location.html b/src/c3nav/site/templates/site/fragment_location.html index 78388f05..55f0afbc 100644 --- a/src/c3nav/site/templates/site/fragment_location.html +++ b/src/c3nav/site/templates/site/fragment_location.html @@ -31,8 +31,10 @@
{% if map_level %} - -

{% trans 'You have Javascript deactivated. Please scroll in this direction ⇘' %}

+ {% for area in visible_areas %} + + {% endfor %} +

{% trans 'Javascript is disabled. Please scroll in this direction ⇘' %}

{% endif %}
diff --git a/src/c3nav/site/templates/site/main.html b/src/c3nav/site/templates/site/main.html index 69af835f..7985211a 100644 --- a/src/c3nav/site/templates/site/main.html +++ b/src/c3nav/site/templates/site/main.html @@ -4,7 +4,8 @@ {% load i18n %} {% block content %} -
+ {% trans "Location" as heading %} diff --git a/src/c3nav/site/urls.py b/src/c3nav/site/urls.py index c0643399..2876afed 100644 --- a/src/c3nav/site/urls.py +++ b/src/c3nav/site/urls.py @@ -3,7 +3,7 @@ from django.conf.urls import url from c3nav.site.views import level_image, main urlpatterns = [ - url(r'^map/(?P[a-z0-9-_:]+).png$', level_image, name='site.level_image'), + url(r'^map/(?P[a-z0-9-_:]+)/(?P[a-z0-9-_:]+).png$', level_image, name='site.level_image'), url(r'^l/(?P[a-z0-9-_:]+)/$', main, name='site.location'), url(r'^o/(?P[a-z0-9-_:]+)/$', main, name='site.origin'), url(r'^d/(?P[a-z0-9-_:]+)/$', main, name='site.destination'), diff --git a/src/c3nav/site/views.py b/src/c3nav/site/views.py index 67c23390..65ea6294 100644 --- a/src/c3nav/site/views.py +++ b/src/c3nav/site/views.py @@ -1,16 +1,18 @@ +import mimetypes from datetime import timedelta -from django.http import Http404 +from django.core.files import File +from django.http import Http404, HttpResponse from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils import timezone +from c3nav.access.apply import get_visible_areas from c3nav.mapdata.inclusion import get_includables_avoidables, parse_include_avoid from c3nav.mapdata.models import Level -from c3nav.mapdata.models.locations import get_location, search_location -from c3nav.mapdata.render.compose import composer +from c3nav.mapdata.search import get_location, search_location from c3nav.mapdata.utils.cache import get_levels_cached -from c3nav.mapdata.utils.misc import get_dimensions +from c3nav.mapdata.utils.misc import get_dimensions, get_render_path from c3nav.routing.exceptions import AlreadyThere, NoRouteFound, NotYetRoutable from c3nav.routing.graph import Graph @@ -68,6 +70,7 @@ def main(request, location=None, origin=None, destination=None): 'full_access': request.c3nav_full_access, 'access_list': request.c3nav_access_list, + 'visible_areas': get_visible_areas(request), } width, height = get_dimensions() @@ -209,6 +212,18 @@ def main(request, location=None, origin=None, destination=None): return response -def level_image(request, level): +def level_image(request, area, level): level = get_object_or_404(Level, name=level, intermediate=False) - return composer.get_level_image(request, level) + if area == ':base': + img = get_render_path('png', level.name, 'full', True) + elif area == ':full': + if not request.c3nav_full_access: + raise Http404 + img = get_render_path('png', level.name, 'full', False) + else: + raise Http404 + + response = HttpResponse(content_type=mimetypes.guess_type(img)[0]) + for chunk in File(open(img, 'rb')).chunks(): + response.write(chunk) + return response