class ListModelMixin(object): """ Turns the django-rest-framework mixin into an etag-aware one. """ empty_error = "Empty list and '%(class_name)s.allow_empty' is False." @method_decorator(etag(etag_func)) def list_response(self, request, data): # Switch between paginated or standard style responses page = self.paginate_queryset(self.object_list) if page is not None: serializer = self.get_pagination_serializer(page) else: serializer = self.get_serializer(self.object_list, many=True) return Response(serializer.data) def list(self, request, *args, **kwargs): self.object_list = self.filter_queryset(self.get_queryset()) # Default is to allow empty querysets. This can be altered by setting # `.allow_empty = False`, to raise 404 errors on empty querysets. if not self.allow_empty and not self.object_list: warnings.warn( 'The `allow_empty` parameter is due to be deprecated. ' 'To use `allow_empty=False` style behavior, You should override ' '`get_queryset()` and explicitly raise a 404 on empty querysets.', PendingDeprecationWarning) class_name = self.__class__.__name__ error_msg = self.empty_error % {'class_name': class_name} raise Http404(error_msg) return self.list_response(request, self.object_list)
def as_view(cls, **initkwargs): def normalize_key_prefix(key_prefix): def _key_prefix(request, *args, **kwargs): return str( key_prefix(request, *args, **kwargs) ).replace(' ', '_') return _key_prefix patched_view = view = super().as_view(**initkwargs) patched_view = http.etag(cls.etag)(patched_view) patched_view = http.last_modified(cls.last_modified)(patched_view) patched_view = cache_page( cache_timeout=cls._expires, key_prefix=normalize_key_prefix(cls.last_modified), )(patched_view) view = decorators.replace_if( lambda request, *args, **kwargs: request.method in ('GET', 'HEAD'), replacement=patched_view, )(view) @functools.wraps(cls.as_view) def logging_view(request, *args, **kwargs): request_logger.debug( 'request_method: %(request_method)s, ' 'request_path: %(request_path)s, ' 'request_headers: %(request_headers)s, ' 'request_params: %(request_params)s, ' 'request_data: %(request_data)s, ', dict( request_method=request.method, request_path=request.path, request_headers=request.META, request_params=request.GET, request_data=request.POST, ), ) response = view(request, *args, **kwargs) response_logger.debug( 'response_code: %(response_code)s, ' 'response_headers: %(response_headers)s, ' 'response_data: %(response_data)s', dict( response_code=response.status_code, response_headers=response.items(), response_data=response.content, ), ) return response return logging_view
class RetrieveModelMixin(object): """ Turns the django-rest-framework mixin into an etag-aware one. """ @method_decorator(etag(etag_func)) def retrieve_response(self, request, data): return Response(data) def retrieve(self, request, *args, **kwargs): self.object = self.get_object() serializer = self.get_serializer(self.object) return self.retrieve_response(request, serializer.data)
def tag_cached(func, tag, *args): def tag_func(*args): return tag def check_cache(*args): response = cache.get(tag) if response: return response response = func(*args) if response.status_code == 200: cache.set(tag, response, 86400) return response decorator = etag(tag_func) view_func = decorator(check_cache) return view_func(*args)
class ZipContentView(View): @xframe_options_exempt @add_security_headers def options(self, request, *args, **kwargs): """ Handles OPTIONS requests which may be sent as "preflight CORS" requests to check permissions. """ return HttpResponse() @method_decorator(etag(calculate_zip_content_etag)) @cache_forever @xframe_options_exempt @add_security_headers def get(self, request, zipped_filename, embedded_filepath): """ Handles GET requests and serves a static file from within the zip file. """ zipped_path = get_path_or_404(zipped_filename) # if client has a cached version, use that (we can safely assume nothing has changed, due to MD5) if request.META.get("HTTP_IF_MODIFIED_SINCE"): return HttpResponseNotModified() with zipfile.ZipFile(zipped_path) as zf: # handle H5P files if zipped_path.endswith("h5p"): if not embedded_filepath or embedded_filepath.startswith("dist/"): response = get_h5p(zf, embedded_filepath) else: # Don't bother doing any hashi parsing of HTML content for h5p response = get_embedded_file( request, zf, zipped_filename, embedded_filepath, skip_hashi=True ) else: response = get_embedded_file( request, zf, zipped_filename, embedded_filepath ) # ensure the browser knows not to try byte-range requests, as we don't support them here response["Accept-Ranges"] = "none" return response
class UpdateModelMixin(DJRUpdateModelMixin): """ Turns the django-rest-framework mixin into an etag-aware one. """ @method_decorator(etag(etag_func)) def update_response(self, request, data, serializer, save_kwargs, created, success_status_code): self.pre_save(serializer.object) self.object = serializer.save(**save_kwargs) self.post_save(self.object, created=created) return Response(serializer.data, status=success_status_code) def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) self.object = self.get_object_or_none() if self.object is None: created = True save_kwargs = {'force_insert': True} success_status_code = status.HTTP_201_CREATED else: created = False save_kwargs = {'force_update': True} success_status_code = status.HTTP_200_OK serializer = self.get_serializer(self.object, data=request.DATA, files=request.FILES, partial=partial) if serializer.is_valid(): request.initial_etag = serializer.object.etag return self.update_response(request, serializer.object, serializer, save_kwargs, created, success_status_code) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# This is not a valid Django URL configuration, as the final # configuration is done by the pretix.multidomain package. js_info_dict = { 'packages': ('pretix',), } # Yes, we want to regenerate this every time the module has been imported to # refresh the cache at least at every code deployment import_date = timezone.now().strftime("%Y%m%d%H%M") base_patterns = [ url(r'^download/(?P<id>[^/]+)/$', cachedfiles.DownloadView.as_view(), name='cachedfile.download'), url(r'^jsi18n/$', etag(lambda *s, **k: import_date)(cache_page(3600, key_prefix='js18n-%s' % import_date)(javascript_catalog)), js_info_dict, name='javascript-catalog'), ] control_patterns = [ url(r'^control/', include(pretix.control.urls, namespace='control')), ] debug_patterns = [] if settings.DEBUG: import debug_toolbar debug_patterns.append(url(r'^__debug__/', include(debug_toolbar.urls))) common_patterns = base_patterns + control_patterns + debug_patterns
from django.views.decorators.http import condition, etag, last_modified from django.http import HttpResponse from .tests import FULL_RESPONSE, LAST_MODIFIED, ETAG def index(request): return HttpResponse(FULL_RESPONSE) index = condition(lambda r: ETAG, lambda r: LAST_MODIFIED)(index) def last_modified_view1(request): return HttpResponse(FULL_RESPONSE) last_modified_view1 = condition(last_modified_func=lambda r: LAST_MODIFIED)(last_modified_view1) def last_modified_view2(request): return HttpResponse(FULL_RESPONSE) last_modified_view2 = last_modified(lambda r: LAST_MODIFIED)(last_modified_view2) def etag_view1(request): return HttpResponse(FULL_RESPONSE) etag_view1 = condition(etag_func=lambda r: ETAG)(etag_view1) def etag_view2(request): return HttpResponse(FULL_RESPONSE) etag_view2 = etag(lambda r: ETAG)(etag_view2)
results, channel_ids, content_kinds, total_results = self.search( value, max_results) data = self.serialize(results) return Response({ "channel_ids": channel_ids, "content_kinds": content_kinds, "results": data, "total_results": total_results, }) def get_cache_key(*args, **kwargs): return str(ContentCacheKey.get_cache_key()) @method_decorator(etag(get_cache_key), name="retrieve") class ContentNodeGranularViewset(mixins.RetrieveModelMixin, viewsets.GenericViewSet): serializer_class = serializers.ContentNodeGranularSerializer def get_queryset(self): return (models.ContentNode.objects.all().prefetch_related( "files__local_file").filter( renderable_contentnodes_q_filter).distinct()) def get_serializer_context(self): context = super(ContentNodeGranularViewset, self).get_serializer_context() context.update({"channel_stats": self.channel_stats}) return context
# -*- coding:utf-8 -*- from django.views.decorators.http import condition, etag, last_modified from django.http import HttpResponse from models import FULL_RESPONSE, LAST_MODIFIED, ETAG def index(request): return HttpResponse(FULL_RESPONSE) index = condition(lambda r: ETAG, lambda r: LAST_MODIFIED)(index) def last_modified_view1(request): return HttpResponse(FULL_RESPONSE) last_modified_view1 = condition(last_modified_func=lambda r: LAST_MODIFIED)(last_modified_view1) def last_modified_view2(request): return HttpResponse(FULL_RESPONSE) last_modified_view2 = last_modified(lambda r: LAST_MODIFIED)(last_modified_view2) def etag_view1(request): return HttpResponse(FULL_RESPONSE) etag_view1 = condition(etag_func=lambda r: ETAG)(etag_view1) def etag_view2(request): return HttpResponse(FULL_RESPONSE) etag_view2 = etag(lambda r: ETAG)(etag_view2)
from django.conf.urls import url from django.views.decorators.http import etag from nextcloudappstore.api.v1.views import AppView, AppReleaseView, \ CategoryView, SessionObtainAuthToken, RegenerateAuthToken, AppRatingView, \ AppRegisterView, NextcloudReleaseView from nextcloudappstore.core.caching import app_ratings_etag, categories_etag, \ apps_etag, nextcloud_release_etag from nextcloudappstore.core.versioning import SEMVER_REGEX urlpatterns = [ url(r'^platforms\.json$', etag(nextcloud_release_etag)(NextcloudReleaseView.as_view()), name='platforms'), url(r'^platform/(?P<version>\d+\.\d+\.\d+)/apps\.json$', etag(apps_etag)(AppView.as_view()), name='app'), url(r'^apps/releases/?$', AppReleaseView.as_view(), name='app-release-create'), url(r'^apps/?$', AppRegisterView.as_view(), name='app-register'), url(r'^apps/(?P<pk>[a-z0-9_]+)/?$', AppView.as_view(), name='app-delete'), url(r'^ratings.json$', etag(app_ratings_etag)(AppRatingView.as_view()), name='app-ratings'), url(r'^apps/(?P<app>[a-z_]+)/releases/(?:(?P<nightly>nightly)/)?' r'(?P<version>' + SEMVER_REGEX + ')/?$', AppReleaseView.as_view(), name='app-release-delete'), url(r'^token/?$', SessionObtainAuthToken.as_view(), name='user-token'), url(r'^token/new/?$', RegenerateAuthToken.as_view(), name='user-token-new'), url(r'^categories.json$', etag(categories_etag)(CategoryView.as_view()), name='category'), ]
update_map_feature_detail_view = login_or_401( json_api_call( instance_request( creates_instance_user(update_map_feature_detail)))) delete_tree_view = login_or_401( json_api_call( instance_request( creates_instance_user(delete_tree)))) delete_map_feature_view = login_or_401( json_api_call( instance_request( creates_instance_user(delete_map_feature)))) get_plot_eco_view = instance_request(etag(_map_feature_hash)( render_template('treemap/partials/plot_eco.html', plot_detail))) get_map_feature_sidebar_view = instance_request(etag(_map_feature_hash)( render_template('treemap/partials/sidebar.html', plot_detail))) map_feature_popup_view = instance_request(etag(_map_feature_hash)( render_template('treemap/partials/map_feature_popup.html', map_feature_popup))) plot_accordion_view = instance_request( render_template('treemap/plot_accordion.html', plot_detail)) add_map_feature_view = require_http_method("POST")( login_or_401( json_api_call( instance_request(
ELSE=do( login_or_401, json_api_call, creates_instance_user, route( PUT=update_map_feature_detail, DELETE=delete_map_feature)))) map_feature_accordion_view = do( instance_request, render_template('treemap/partials/map_feature_accordion.html'), map_feature_detail) get_map_feature_sidebar_view = do( instance_request, etag(map_feature_hash), render_template('treemap/partials/sidebar.html'), map_feature_detail) map_feature_popup_view = do( instance_request, etag(map_feature_hash), render_template('treemap/partials/map_feature_popup.html'), map_feature_popup) add_map_feature_photo_endpoint = add_map_feature_photo_do( add_map_feature_photo) rotate_map_feature_photo_endpoint = add_map_feature_photo_do( rotate_map_feature_photo)
from django.conf.urls import url from django.views.decorators.http import etag from nextcloudappstore.api.v1.views import AppView, AppReleaseView, \ CategoryView, SessionObtainAuthToken, RegenerateAuthToken, AppRatingView, \ AppRegisterView from nextcloudappstore.core.caching import app_ratings_etag, categories_etag, \ apps_etag from nextcloudappstore.core.versioning import SEMVER_REGEX urlpatterns = [ url(r'^platform/(?P<version>\d+\.\d+\.\d+)/apps\.json$', etag(apps_etag)(AppView.as_view()), name='app'), url(r'^apps/releases/?$', AppReleaseView.as_view(), name='app-release-create'), url(r'^apps/?$', AppRegisterView.as_view(), name='app-register'), url(r'^apps/(?P<pk>[a-z0-9_]+)/?$', AppView.as_view(), name='app-delete'), url(r'^ratings.json$', etag(app_ratings_etag)(AppRatingView.as_view()), name='app-ratings'), url(r'^apps/(?P<app>[a-z_]+)/releases/(?:(?P<nightly>nightly)/)?' r'(?P<version>' + SEMVER_REGEX + ')/?$', AppReleaseView.as_view(), name='app-release-delete'), url(r'^token/?$', SessionObtainAuthToken.as_view(), name='user-token'), url(r'^token/new/?$', RegenerateAuthToken.as_view(), name='user-token-new'), url(r'^categories.json$', etag(categories_etag)(CategoryView.as_view()), name='category'), ]
PUT=feature_views.update_map_feature_detail, DELETE=feature_views.delete_map_feature)))) map_feature_detail_partial = do( instance_request, require_http_method('GET'), feature_views.render_map_feature_detail_partial) map_feature_accordion = do( instance_request, render_template('treemap/partials/map_feature_accordion.html'), feature_views.context_map_feature_detail) get_map_feature_sidebar = do( instance_request, etag(feature_views.map_feature_hash), render_template('treemap/partials/sidebar.html'), feature_views.context_map_feature_detail) map_feature_popup = do( instance_request, etag(feature_views.map_feature_hash), render_template('treemap/partials/map_feature_popup.html'), feature_views.map_feature_popup) canopy_popup = do( instance_request, feature_views.canopy_popup) add_map_feature_photo = add_map_feature_photo_do( feature_views.add_map_feature_photo)
from django.conf.urls import url from django.views.decorators.http import etag from nextcloudappstore.api.v1.views import AppView, AppReleaseView, \ CategoryView, SessionObtainAuthToken, RegenerateAuthToken, AppRatingView, \ AppRegisterView, NextcloudReleaseView, AppsView from nextcloudappstore.core.caching import app_ratings_etag, categories_etag, \ apps_etag, nextcloud_release_etag, apps_all_etag from nextcloudappstore.core.versioning import SEMVER_REGEX app_name = 'v1' urlpatterns = [ url(r'^platforms\.json$', etag(nextcloud_release_etag)(NextcloudReleaseView.as_view()), name='platforms'), url(r'^platform/(?P<version>\d+\.\d+\.\d+)/apps\.json$', etag(apps_etag)(AppView.as_view()), name='app'), url(r'^apps\.json$', etag(apps_all_etag)(AppsView.as_view()), name='apps'), url(r'^apps/releases/?$', AppReleaseView.as_view(), name='app-release-create'), url(r'^apps/?$', AppRegisterView.as_view(), name='app-register'), url(r'^apps/(?P<pk>[a-z0-9_]+)/?$', AppView.as_view(), name='app-delete'), url(r'^ratings.json$', etag(app_ratings_etag)(AppRatingView.as_view()), name='app-ratings'), url(r'^apps/(?P<app>[a-z_]+)/releases/(?:(?P<nightly>nightly)/)?' r'(?P<version>' + SEMVER_REGEX + ')/?$', AppReleaseView.as_view(), name='app-release-delete'),
class ZipContentView(View): @xframe_options_exempt @add_security_headers def options(self, request, *args, **kwargs): """ Handles OPTIONS requests which may be sent as "preflight CORS" requests to check permissions. """ return HttpResponse() @method_decorator(etag(calculate_zip_content_etag)) @cache_forever @xframe_options_exempt @add_security_headers def get(self, request, zipped_filename, embedded_filepath): """ Handles GET requests and serves a static file from within the zip file. """ zipped_path = get_path_or_404(zipped_filename) # Sometimes due to URL concatenation, we get URLs with double-slashes in them, like //path/to/file.html. # the zipped_filename and embedded_filepath are defined by the regex capturing groups in the URL defined # in urls.py in the same folder as this file: # r"^zipcontent/(?P<zipped_filename>[^/]+)/(?P<embedded_filepath>.*)" # If the embedded_filepath contains a leading slash because of an input URL like: # /zipcontent/filename.zip//file.html # then the embedded_filepath will have a value of "/file.html" # we detect this leading slash in embedded_filepath and remove it. if embedded_filepath.startswith("/"): embedded_filepath = embedded_filepath[1:] # Any double-slashes later in the URL will be present as double-slashes, such as: # /zipcontent/filename.zip/path//file.html # giving an embedded_filepath value of "path//file.html" # Normalize the path by converting double-slashes occurring later in the path to a single slash. # This would change our example embedded_filepath to "path/file.html" which will resolve properly. embedded_filepath = embedded_filepath.replace("//", "/") # if client has a cached version, use that (we can safely assume nothing has changed, due to MD5) if request.META.get("HTTP_IF_MODIFIED_SINCE"): return HttpResponseNotModified() with zipfile.ZipFile(zipped_path) as zf: # handle H5P files if zipped_path.endswith("h5p"): if not embedded_filepath or embedded_filepath.startswith( "dist/"): response = get_h5p(zf, embedded_filepath) else: # Don't bother doing any hashi parsing of HTML content for h5p response = get_embedded_file(request, zf, zipped_filename, embedded_filepath, skip_hashi=True) else: response = get_embedded_file(request, zf, zipped_filename, embedded_filepath) # ensure the browser knows not to try byte-range requests, as we don't support them here response["Accept-Ranges"] = "none" return response
class InfoView(TemplateView): @method_decorator(etag(get_template_etag)) def get(self, request, *args, **kwargs): self.template_name = request.template_name response = super(InfoView, self).get(request, *args, **kwargs) return response
GET=feature_views.render_map_feature_detail, ELSE=do( json_api_edit, return_400_if_validation_errors, route( PUT=feature_views.update_map_feature_detail, DELETE=feature_views.delete_map_feature)))) map_feature_accordion = do( instance_request, render_template('treemap/partials/map_feature_accordion.html'), feature_views.map_feature_detail) get_map_feature_sidebar = do( instance_request, etag(feature_views.map_feature_hash), render_template('treemap/partials/sidebar.html'), feature_views.map_feature_detail) map_feature_popup = do( instance_request, etag(feature_views.map_feature_hash), render_template('treemap/partials/map_feature_popup.html'), feature_views.map_feature_popup) add_map_feature_photo = add_map_feature_photo_do( feature_views.add_map_feature_photo) rotate_map_feature_photo = add_map_feature_photo_do( feature_views.rotate_map_feature_photo)
map_feature_detail_view = do( instance_request, route(GET=render_map_feature_detail, ELSE=do( json_api_edit, return_400_if_validation_errors, route(PUT=update_map_feature_detail, DELETE=delete_map_feature)))) map_feature_accordion_view = do( instance_request, render_template('treemap/partials/map_feature_accordion.html'), map_feature_detail) get_map_feature_sidebar_view = do( instance_request, etag(map_feature_hash), render_template('treemap/partials/sidebar.html'), map_feature_detail) map_feature_popup_view = do( instance_request, etag(map_feature_hash), render_template('treemap/partials/map_feature_popup.html'), map_feature_popup) add_map_feature_photo_endpoint = add_map_feature_photo_do( add_map_feature_photo) rotate_map_feature_photo_endpoint = add_map_feature_photo_do( rotate_map_feature_photo) ##################################### # plot
url(r'^developer/apps/releases/new/?$', AppUploadView.as_view(), name='app-upload'), url(r'^developer/apps/new/?$', AppRegisterView.as_view(), name='app-register'), url(r'^apps/(?P<id>[\w_]+)/?$', AppDetailView.as_view(), name='app-detail'), url(r'^apps/(?P<id>[\w_]+)/releases/?$', AppReleasesView.as_view(), name='app-releases'), url(r'^apps/(?P<id>[\w_]+)/description/?$', app_description, name='app-description'), url(r'^apps/(?P<id>[\w_]+)/ratings.json$', etag(app_rating_etag)(AppRatingApi.as_view()), name='app-ratings'), url(r'^api/', include('nextcloudappstore.api.urls', namespace='api')), url(r'^account/', include('nextcloudappstore.user.urls', namespace='user')), url(r'^admin/', admin.site.urls), url(r'^i18n/', include('django.conf.urls.i18n')), ] urlpatterns += i18n_patterns( url(r'feeds/releases.rss', AppReleaseRssFeed(), name='feeds-releases-rss'), url(r'feeds/releases.atom', AppReleaseAtomFeed(), name='feeds-releases-atom'), )
# configuration is done by the pretix.multidomain package. js_info_dict = { 'packages': ('pretix',), } # Yes, we want to regenerate this every time the module has been imported to # refresh the cache at least at every code deployment import_date = timezone.now().strftime("%Y%m%d%H%M") base_patterns = [ url(r'^download/(?P<id>[^/]+)/$', cachedfiles.DownloadView.as_view(), name='cachedfile.download'), url(r'^healthcheck/$', health.healthcheck, name='cachedfile.download'), url(r'^jsi18n/$', etag(lambda *s, **k: import_date)(cache_page(3600, key_prefix='js18n-%s' % import_date)(javascript_catalog)), js_info_dict, name='javascript-catalog'), ] control_patterns = [ url(r'^control/', include(pretix.control.urls, namespace='control')), ] debug_patterns = [] if settings.DEBUG: try: import debug_toolbar debug_patterns.append(url(r'^__debug__/', include(debug_toolbar.urls))) except ImportError: pass
ELSE=do( json_api_edit, return_400_if_validation_errors, route(PUT=feature_views.update_map_feature_detail, DELETE=feature_views.delete_map_feature)))) map_feature_detail_partial = do( instance_request, require_http_method('GET'), feature_views.render_map_feature_detail_partial) map_feature_accordion = do( instance_request, render_template('treemap/partials/map_feature_accordion.html'), feature_views.context_map_feature_detail) get_map_feature_sidebar = do(instance_request, etag(feature_views.map_feature_hash), render_template('treemap/partials/sidebar.html'), feature_views.context_map_feature_detail) map_feature_popup = do( instance_request, etag(feature_views.map_feature_hash), render_template('treemap/partials/map_feature_popup.html'), feature_views.map_feature_popup) canopy_popup = do(instance_request, feature_views.canopy_popup) add_map_feature_photo = add_map_feature_photo_do( feature_views.add_map_feature_photo) delete_photo = do(require_http_method("DELETE"), login_or_401, instance_request,
class TastypieBaseResource(object): """ A TastypieBaseResource for Tastypie. """ def form_errors(self, forms): errors = format_form_errors(forms) response = http.HttpBadRequest(json.dumps(errors), content_type='application/json') raise ImmediateHttpResponse(response=response) def dehydrate(self, bundle): bundle.data['resource_pk'] = bundle.obj.pk return super(TastypieBaseResource, self).dehydrate(bundle) def _handle_500(self, request, exception): return handle_500(request, exception) def deserialize(self, request, data, format='application/json'): result = (super(TastypieBaseResource, self).deserialize(request, data, format=format)) if settings.DUMP_REQUESTS: formatted_json(result) return result def dispatch(self, request_type, request, **kw): method = request.META['REQUEST_METHOD'] delay = request.META.get('HTTP_SOLITUDE_ASYNC', False) if delay: # Move the import here to remove warnings in management commands. from lib.delayable.tasks import delayable # Only do async on these requests. if method not in ['PATCH', 'POST', 'PUT']: raise ImmediateHttpResponse( response=http.HttpMethodNotAllowed()) # Create a delayed dispatch. uid = str(uuid.uuid4()) # We only need a subset of meta. whitelist = ['PATH_INFO', 'REQUEST_METHOD', 'QUERY_STRING'] meta = dict([k, request.META[k]] for k in whitelist) # Celery could magically serialise some of this, but I don't # trust it that much. delayable.delay(self.__class__.__module__, self.__class__.__name__, request_type, meta, request.body, kw, uid) content = json.dumps({ 'replay': '/delay/replay/%s/' % uid, 'result': '/delay/result/%s/' % uid }) return http.HttpResponse(content, status=202, content_type='application/json') # Log the call with CEF and logging. if settings.DUMP_REQUESTS: print colorize('brace', method), request.get_full_path() else: log.info('%s %s' % (colorize('brace', method), request.get_full_path())) msg = '%s:%s' % (kw.get('api_name', 'unknown'), kw.get('resource_name', 'unknown')) log_cef(msg, request, severity=2) return super(TastypieBaseResource, self).dispatch(request_type, request, **kw) def build_filters(self, filters=None): # Override the filters so we can stop Tastypie silently ignoring # invalid filters. That will cause an invalid filtering just to return # lots of results. if filters is None: filters = {} qs_filters = {} for filter_expr, value in filters.items(): filter_bits = filter_expr.split(LOOKUP_SEP) field_name = filter_bits.pop(0) filter_type = 'exact' if not field_name in self.fields: # Don't just ignore this. Tell the world. Shame I have to # override all this, just to do this. raise InvalidFilterError('Not a valid filtering field: %s' % field_name) if len(filter_bits) and filter_bits[-1] in QUERY_TERMS: filter_type = filter_bits.pop() lookup_bits = self.check_filtering(field_name, filter_type, filter_bits) if value in ['true', 'True', True]: value = True elif value in ['false', 'False', False]: value = False elif value in ('nil', 'none', 'None', None): value = None # Split on ',' if not empty string and either an in or range # filter. if filter_type in ('in', 'range') and len(value): if hasattr(filters, 'getlist'): value = filters.getlist(filter_expr) else: value = value.split(',') db_field_name = LOOKUP_SEP.join(lookup_bits) qs_filter = '%s%s%s' % (db_field_name, LOOKUP_SEP, filter_type) qs_filters[qs_filter] = value return dict_strip_unicode_keys(qs_filters) def is_valid(self, bundle, request): # Tastypie will check is_valid on the object by validating the form, # but on PUTes and PATCHes it does so without instantiating the object. # Without the object on the model.instance, the uuid check does not # exclude the original object being changed and so the validation # will fail. This patch will force the object to be added before # validation, # # There are two ways to spot when we should be doing this: # 1. When there is a specific resource_pk in the PUT or PATCH. # 2. When the request.path resolves to having a pk in it. # If either of those match, get_via_uri will do the right thing. if 'resource_uri' in bundle.data or 'pk' in resolve(request.path)[2]: try: bundle.obj = self.get_via_uri(request.path) if request.method == 'PUT': # In case of a PUT modification, we need to keep # the initial values for the given object to check # the Etag header. request.initial_etag = getattr(bundle.obj, 'etag', '') except ObjectDoesNotExist: pass return super(TastypieBaseResource, self).is_valid(bundle, request) @method_decorator(etag(etag_func)) def create_response(self, request, data, response_class=HttpResponse, **response_kwargs): return super(TastypieBaseResource, self).create_response(request, data, response_class, **response_kwargs) @method_decorator(etag(etag_func)) def create_patch_response(self, request, original_bundle, new_data): self.update_in_place(request, original_bundle, new_data) return http.HttpAccepted() def patch_detail(self, request, **kwargs): request = convert_post_to_patch(request) try: obj = self.cached_obj_get(request=request, **self.remove_api_resource_names(kwargs)) except ObjectDoesNotExist: return http.HttpNotFound() except MultipleObjectsReturned: return http.HttpMultipleChoices('More than one resource' 'is found at this URI.') bundle = self.build_bundle(obj=obj, request=request) bundle = self.full_dehydrate(bundle) bundle = self.alter_detail_data_to_serialize(request, bundle) # Now update the bundle in-place. deserialized = self.deserialize(request, request.raw_post_data, format=request.META.get( 'CONTENT_TYPE', 'application/json')) # In case of a patch modification, we need to store # the initial values for the given object to check # the Etag header. request.initial_etag = bundle.obj.etag return self.create_patch_response(request, bundle, deserialized) def deserialize_body(self, request): # Trying to standardize on JSON in the body for most things if we # can. Similar to elastic search. Retaining query string for tastypie # record filtering. data = request.raw_post_data if not data: # Don't raise an error if the body is empty. return {} return self.deserialize(request, data, format='application/json')
from django.conf.urls import url from django.views.decorators.http import etag from nextcloudappstore.core.api.v1.views import Apps, AppReleases, app_api_etag urlpatterns = [ url(r'^platform/(?P<version>\d+\.\d+\.\d+)/apps\.json$', etag(app_api_etag)(Apps.as_view()), name='apps'), url(r'^apps/releases/?$', AppReleases.as_view(), name='app-release-create'), url(r'^apps/(?P<pk>[a-z_]+)/?$', Apps.as_view(), name='app-delete'), url(r'^apps/(?P<app>[a-z_]+)/releases/(?P<version>\d+\.\d+\.\d+)/?$', AppReleases.as_view(), name='app-release-delete'), ]
url(r'^categories/(?P<id>[\w]*)/?$', CategoryAppListView.as_view(), name='category-app-list'), url(r'^developer/apps/generate/?$', AppScaffoldingView.as_view(), name='app-scaffold'), url(r'^developer/apps/releases/new/?$', AppUploadView.as_view(), name='app-upload'), url(r'^developer/apps/new/?$', AppRegisterView.as_view(), name='app-register'), url(r'^apps/(?P<id>[\w_]+)/?$', AppDetailView.as_view(), name='app-detail'), url(r'^apps/(?P<id>[\w_]+)/releases/?$', AppReleasesView.as_view(), name='app-releases'), url(r'^apps/(?P<id>[\w_]+)/description/?$', app_description, name='app-description'), url(r'^apps/(?P<id>[\w_]+)/ratings.json$', etag(app_rating_etag)(AppRatingApi.as_view()), name='app-ratings'), url(r'^api/', include('nextcloudappstore.core.api.urls', namespace='api')), url(r'^account/', include('nextcloudappstore.core.user.urls', namespace='user')), url(r'^admin/', admin.site.urls), url(r'^i18n/', include('django.conf.urls.i18n')), ] urlpatterns += i18n_patterns( url(r'feeds/releases.rss', AppReleaseRssFeed(), name='feeds-releases-rss'), url(r'feeds/releases.atom', AppReleaseAtomFeed(), name='feeds-releases-atom'), ) if settings.DEBUG:
from django.conf.urls import url from django.views.decorators.http import etag from nextcloudappstore.core.api.v1.views import AppView, AppReleaseView, \ CategoryView, app_api_etag, category_api_etag urlpatterns = [ url(r'^platform/(?P<version>\d+\.\d+\.\d+)/apps\.json$', etag(app_api_etag)(AppView.as_view()), name='app'), url(r'^apps/releases/?$', AppReleaseView.as_view(), name='app-release-create'), url(r'^apps/(?P<pk>[a-z_]+)/?$', AppView.as_view(), name='app-delete'), url(r'^apps/(?P<app>[a-z_]+)/releases/(?P<version>\d+\.\d+\.\d+' r'(?:-nightly)?)/?$', AppReleaseView.as_view(), name='app-release-delete'), url(r'^categories.json$', etag(category_api_etag)(CategoryView.as_view()), name='category'), ]
map_feature_detail = do( instance_request, route(GET=feature_views.render_map_feature_detail, ELSE=do( json_api_edit, return_400_if_validation_errors, route(PUT=feature_views.update_map_feature_detail, DELETE=feature_views.delete_map_feature)))) map_feature_accordion = do( instance_request, render_template('treemap/partials/map_feature_accordion.html'), feature_views.context_map_feature_detail) get_map_feature_sidebar = do(instance_request, etag(feature_views.map_feature_hash), render_template('treemap/partials/sidebar.html'), feature_views.context_map_feature_detail) map_feature_popup = do( instance_request, etag(feature_views.map_feature_hash), render_template('treemap/partials/map_feature_popup.html'), feature_views.map_feature_popup) add_map_feature_photo = add_map_feature_photo_do( feature_views.add_map_feature_photo) rotate_map_feature_photo = add_map_feature_photo_do( feature_views.rotate_map_feature_photo) map_feature_photo_detail = do(
return HttpResponseRedirect(reverse("map", kwargs={"instance_url_name": instance.url_name})) edits_view = instance_request(requires_feature("recent_edits_report")(render_template("treemap/edits.html", edits))) index_view = instance_request(index) map_view = instance_request(render_template("treemap/map.html", _get_map_view_context)) get_plot_detail_view = instance_request(render_template("treemap/plot_detail.html", plot_detail)) edit_plot_detail_view = login_required( instance_request(creates_instance_user(render_template("treemap/plot_detail.html", plot_detail))) ) get_plot_eco_view = instance_request(etag(_plot_hash)(render_template("treemap/partials/plot_eco.html", plot_detail))) get_plot_sidebar_view = instance_request( etag(_plot_hash)(render_template("treemap/partials/sidebar.html", plot_detail)) ) update_plot_detail_view = login_or_401(json_api_call(instance_request(creates_instance_user(update_plot_detail)))) plot_popup_view = instance_request(etag(_plot_hash)(render_template("treemap/partials/plot_popup.html", plot_detail))) plot_accordion_view = instance_request(render_template("treemap/plot_accordion.html", plot_detail)) add_plot_view = require_http_method("POST")( login_or_401(json_api_call(instance_request(creates_instance_user(add_plot)))) )