diff --git a/src/c3nav/api/permissions.py b/src/c3nav/api/permissions.py index 45a06df4..434a2497 100644 --- a/src/c3nav/api/permissions.py +++ b/src/c3nav/api/permissions.py @@ -1,6 +1,5 @@ from django.conf import settings from django.utils.translation import ugettext_lazy as _ - from rest_framework.exceptions import PermissionDenied from rest_framework.permissions import BasePermission diff --git a/src/c3nav/api/serializers.py b/src/c3nav/api/serializers.py index 2642e77d..a971cea3 100644 --- a/src/c3nav/api/serializers.py +++ b/src/c3nav/api/serializers.py @@ -1,7 +1,7 @@ from django.conf import settings +from rest_framework import serializers -from rest_framework.serializers import ModelSerializer - +from ..editor.hosters import get_hoster_for_package from ..mapdata.models import Level, Package, Source from .permissions import can_access_package @@ -14,13 +14,13 @@ class BoundsMixin: return result -class LevelSerializer(ModelSerializer): +class LevelSerializer(serializers.ModelSerializer): class Meta: model = Level fields = ('name', 'altitude', 'package') -class PackageSerializer(BoundsMixin, ModelSerializer): +class PackageSerializer(BoundsMixin, serializers.ModelSerializer): class Meta: model = Package fields = ('name', 'home_repo', 'depends') @@ -28,12 +28,27 @@ class PackageSerializer(BoundsMixin, ModelSerializer): def to_representation(self, obj): result = super().to_representation(obj) result['public'] = obj.name in settings.PUBLIC_PACKAGES + hoster = get_hoster_for_package(obj) if 'request' in self.context: - result['access'] = can_access_package(self.context['request'], obj) + result['access_granted'] = can_access_package(self.context['request'], obj) + if hoster is not None: + result['hoster'] = hoster.name return result -class SourceSerializer(BoundsMixin, ModelSerializer): +class SourceSerializer(BoundsMixin, serializers.ModelSerializer): class Meta: model = Source fields = ('name', 'package') + + +class HosterSerializer(serializers.Serializer): + name = serializers.CharField() + base_url = serializers.CharField() + + def to_representation(self, obj): + result = super().to_representation(obj) + result['packages'] = obj.get_packages().values_list('name', flat=True) + if 'request' in self.context: + result['signed_in'] = obj.is_access_granted(self.context['request']) + return result diff --git a/src/c3nav/api/urls.py b/src/c3nav/api/urls.py index 9fb9f26b..68484981 100644 --- a/src/c3nav/api/urls.py +++ b/src/c3nav/api/urls.py @@ -1,13 +1,14 @@ from django.conf.urls import include, url - from rest_framework.routers import DefaultRouter -from .views import map as map_views +from .views import editor as editor_views +from .views import mapdata as mapdata_views router = DefaultRouter() -router.register(r'map/levels', map_views.LevelViewSet) -router.register(r'map/packages', map_views.PackageViewSet) -router.register(r'map/sources', map_views.SourceViewSet) +router.register(r'levels', mapdata_views.LevelViewSet) +router.register(r'packages', mapdata_views.PackageViewSet) +router.register(r'sources', mapdata_views.SourceViewSet) +router.register(r'hosters', editor_views.HosterViewSet, base_name='hoster') urlpatterns = [ diff --git a/src/c3nav/api/views/editor.py b/src/c3nav/api/views/editor.py new file mode 100644 index 00000000..76c625dc --- /dev/null +++ b/src/c3nav/api/views/editor.py @@ -0,0 +1,21 @@ +from django.http import Http404 +from rest_framework.response import Response +from rest_framework.viewsets import ViewSet + +from ...editor.hosters import hosters +from ..serializers import HosterSerializer + + +class HosterViewSet(ViewSet): + """ + Get Package Hosters + """ + def list(self, request, version=None): + serializer = HosterSerializer(hosters.values(), many=True, context={'request': request}) + return Response(serializer.data) + + def retrieve(self, request, pk=None, version=None): + if pk not in hosters: + raise Http404 + serializer = HosterSerializer(hosters[pk], context={'request': request}) + return Response(serializer.data) diff --git a/src/c3nav/api/views/map.py b/src/c3nav/api/views/mapdata.py similarity index 99% rename from src/c3nav/api/views/map.py rename to src/c3nav/api/views/mapdata.py index 27a18d8b..1fc07317 100644 --- a/src/c3nav/api/views/map.py +++ b/src/c3nav/api/views/mapdata.py @@ -4,7 +4,6 @@ import os from django.conf import settings from django.core.files import File from django.http import HttpResponse - from rest_framework.decorators import detail_route from rest_framework.viewsets import ReadOnlyModelViewSet diff --git a/src/c3nav/editor/__init__.py b/src/c3nav/editor/__init__.py index e69de29b..01e2b19a 100644 --- a/src/c3nav/editor/__init__.py +++ b/src/c3nav/editor/__init__.py @@ -0,0 +1 @@ +default_app_config = 'c3nav.editor.apps.EditorConfig' diff --git a/src/c3nav/editor/apps.py b/src/c3nav/editor/apps.py new file mode 100644 index 00000000..9f209c23 --- /dev/null +++ b/src/c3nav/editor/apps.py @@ -0,0 +1,9 @@ +from django.apps import AppConfig + + +class EditorConfig(AppConfig): + name = 'c3nav.editor' + + def ready(self): + from .hosters import init_hosters + init_hosters() diff --git a/src/c3nav/editor/hosters/__init__.py b/src/c3nav/editor/hosters/__init__.py new file mode 100644 index 00000000..390ce6df --- /dev/null +++ b/src/c3nav/editor/hosters/__init__.py @@ -0,0 +1,29 @@ +from django.conf import settings +from .github import GithubHoster # noqa +from .gitlab import GitlabHoster # noqa + +from collections import OrderedDict + + +hosters = {} + + +def init_hosters(): + global hosters + hosters = OrderedDict((name, create_hoster(name=name, **data)) for name, data in settings.EDITOR_HOSTERS.items()) + + +def create_hoster(api, **kwargs): + if api == 'github': + return GithubHoster(**kwargs) + elif api == 'gitlab': + return GitlabHoster(**kwargs) + else: + raise ValueError('Unknown hoster API: %s' % api) + + +def get_hoster_for_package(package): + for name, hoster in hosters.items(): + if package.home_repo.startswith(hoster.base_url): + return hoster + return None diff --git a/src/c3nav/editor/hosters/base.py b/src/c3nav/editor/hosters/base.py new file mode 100644 index 00000000..9ac1a113 --- /dev/null +++ b/src/c3nav/editor/hosters/base.py @@ -0,0 +1,16 @@ +from ...mapdata.models import Package + + +class Hoster: + def __init__(self, name, base_url): + self.name = name + self.base_url = base_url + + def get_packages(self): + return Package.objects.filter(home_repo__startswith=self.base_url) + + def _get_session_data(self, request): + return request.session.setdefault('hosters', {}).setdefault(self.name, {}) + + def is_access_granted(self, request): + return self._get_session_data(request).get('access_granted', False) diff --git a/src/c3nav/editor/hosters/github.py b/src/c3nav/editor/hosters/github.py new file mode 100644 index 00000000..544af48e --- /dev/null +++ b/src/c3nav/editor/hosters/github.py @@ -0,0 +1,8 @@ +from .base import Hoster + + +class GithubHoster(Hoster): + def __init__(self, app_id, app_secret, **kwargs): + super().__init__(**kwargs) + self._app_id = app_id + self._app_secret = app_secret diff --git a/src/c3nav/editor/hosters/gitlab.py b/src/c3nav/editor/hosters/gitlab.py new file mode 100644 index 00000000..31b7304d --- /dev/null +++ b/src/c3nav/editor/hosters/gitlab.py @@ -0,0 +1,8 @@ +from .base import Hoster + + +class GitlabHoster(Hoster): + def __init__(self, app_id, app_secret, **kwargs): + super().__init__(**kwargs) + self._app_id = app_id + self._app_secret = app_secret diff --git a/src/c3nav/settings.py b/src/c3nav/settings.py index e9dab0e1..9d6593c4 100644 --- a/src/c3nav/settings.py +++ b/src/c3nav/settings.py @@ -3,6 +3,7 @@ import configparser import os import string import sys +from collections import OrderedDict from django.contrib.messages import constants as messages from django.utils.crypto import get_random_string @@ -48,6 +49,8 @@ debug_fallback = "runserver" in sys.argv DEBUG = config.getboolean('django', 'debug', fallback=debug_fallback) PUBLIC_PACKAGES = [n for n in config.get('c3nav', 'public_packages', fallback='').split(',') if n] +EDITOR_HOSTERS = OrderedDict((name[7:], data) for name, data in config.items() if name.startswith('hoster:')) + db_backend = config.get('database', 'backend', fallback='sqlite3') DATABASES = { 'default': {