def _get_tiddler_content(environ, tiddler): """ Extract the content of the tiddler, either straight up if the content is not considered text, or serialized if it is. """ config = environ['tiddlyweb.config'] default_serializer = config['default_serializer'] default_serialize_type = config['serializers'][default_serializer][0] serialize_type, mime_type, accept = get_serialize_type(environ, accept_type=True) extension = environ.get('tiddlyweb.extension') serialized = False # If this is a tiddler with a CANONICAL_URI_FIELD redirect # there unless we are requesting a json form if (CANONICAL_URI_FIELD in tiddler.fields and CANONICAL_URI_PASS_TYPE not in mime_type): raise HTTP302(tiddler.fields[CANONICAL_URI_FIELD]) if not renderable(tiddler, environ): if (serialize_type == default_serialize_type or accept.startswith(tiddler.type) or extension == 'html'): mime_type = tiddler.type content = tiddler.text return content, mime_type, serialized serializer = Serializer(serialize_type, environ) serializer.object = tiddler try: content = serializer.to_string() serialized = True except (TiddlerFormatError, NoSerializationError) as exc: raise HTTP415(exc) return content, mime_type, serialized
def _post_tiddler_revisions(environ, start_response, tiddler): """ We have a list of revisions, put them in a new place. """ length, content_type = content_length_and_type(environ) if content_type not in CHRONICLE_TYPES: raise HTTP415('application/vnd.tiddlyweb+json required') # we need a matching etag in order to be able to do # this operation. This will raise exception if there # isn't a valid etag. _require_valid_etag_for_write(environ, tiddler) bag = Bag(tiddler.bag) # both create and write required for this action check_bag_constraint(environ, bag, 'create') check_bag_constraint(environ, bag, 'write') content = environ['wsgi.input'].read(int(length)) _store_tiddler_revisions(environ, content, tiddler) response = [('Location', tiddler_url(environ, tiddler))] start_response("204 No Content", response) return []
def send_entity(environ, start_response, entity): """ Send a :py:class:`bag <tiddlyweb.model.bag.Bag>` or :py:class:`recipe <tiddlyweb.model.recipe.Recipe>` out over HTTP, first :py:class:`serializing <tiddlyweb.serializer.Serializer>` to the correct type. If an incoming ``Etag`` validates, raise a ``304`` response. """ etag_string = entity_etag(environ, entity) check_incoming_etag(environ, etag_string) try: serialize_type, mime_type = get_serialize_type(environ) serializer = Serializer(serialize_type, environ) serializer.object = entity content = serializer.to_string() except NoSerializationError: raise HTTP415('Content type %s not supported' % mime_type) start_response("200 OK", [('Content-Type', mime_type), ('Cache-Control', 'no-cache'), ('ETag', etag_string), ('Vary', 'Accept')]) if isinstance(content, basestring): return [content] else: return content
def send_tiddlers(environ, start_response, tiddlers=None): """ Output the :py:class:`tiddlers <tiddlyweb.model.tiddler.Tiddler>` contained in the provided :py:class:`Tiddlers collection <tiddlyweb.model.collections.Tiddlers>` in a :py:mod:`Negotiated <tiddlyweb.web.negotiate>` representation. """ download = environ['tiddlyweb.query'].get('download', [None])[0] filters = environ['tiddlyweb.filters'] store = environ['tiddlyweb.store'] if tiddlers.store is None and not filters: LOGGER.warn('Incoming tiddlers no store set %s', inspect.stack()[1]) if filters: candidate_tiddlers = _filter_tiddlers(filters, store, tiddlers) else: candidate_tiddlers = tiddlers last_modified, etag = _validate_tiddler_list(environ, candidate_tiddlers) serialize_type, mime_type = get_serialize_type(environ, collection=True) response = [('Content-Type', mime_type), ('Cache-Control', 'no-cache'), ('Vary', 'Accept')] if download: response.append(('Content-Disposition', 'attachment; filename="%s"' % encode_name(download))) if last_modified: response.append(last_modified) if etag: response.append(etag) try: serializer = Serializer(serialize_type, environ) output = serializer.list_tiddlers(candidate_tiddlers) except NoSerializationError as exc: raise HTTP415('Content type not supported: %s:%s, %s' % (serialize_type, mime_type, exc)) except FilterError as exc: # serializations may filter tildders raise HTTP400('malformed filter or tiddler during filtering: %s' % exc) start_response("200 OK", response) if isinstance(output, basestring): return [output] else: return output
def put(environ, start_response): """ Handle ``PUT`` on a single recipe URI. Put a :py:class:`recipe <tiddlyweb.model.recipe.Recipe>` to the server, meaning the description, policy and recipe list of the recipe, if :py:class:`policy <tiddlyweb.model.policy.Policy>` allows. """ recipe_name = web.get_route_value(environ, 'recipe_name') recipe_name = web.handle_extension(environ, recipe_name) recipe = Recipe(recipe_name) store = environ['tiddlyweb.store'] length, _ = web.content_length_and_type(environ) usersign = environ['tiddlyweb.usersign'] try: recipe = store.get(recipe) recipe.policy.allows(usersign, 'manage') except NoRecipeError: create_policy_check(environ, 'recipe', usersign) try: serialize_type = web.get_serialize_type(environ)[0] except TypeError: raise HTTP400('Content-type header required') try: serializer = Serializer(serialize_type, environ) serializer.object = recipe content = web.read_request_body(environ, length) serializer.from_string(content.decode('utf-8')) recipe.policy.owner = usersign['name'] _validate_recipe(environ, recipe) store.put(recipe) except RecipeFormatError as exc: raise HTTP400('unable to put recipe: %s' % exc) except TypeError as exc: raise HTTP400('malformed input: %s' % exc) except NoSerializationError: raise HTTP415('Content type %s not supported' % serialize_type) start_response("204 No Content", [('Location', web.recipe_url(environ, recipe))]) return []
def profile(environ, start_response): """ Choose between an atom or html profile. """ http_host, host_url = determine_host(environ) if http_host != host_url: # Or should it be a 302? raise HTTP404('No profiles at this host: %s' % http_host) _, mime_type = get_serialize_type(environ) if 'atom' in mime_type: return atom_profile(environ, start_response) elif 'html' in mime_type: return html_profile(environ, start_response) else: raise HTTP415('Atom and HTML only')
def put(environ, start_response): """ Handle ``PUT`` on a single bag URI. Put a :py:class:`bag <tiddlyweb.model.bag.Bag>` to the server, meaning the description and policy of the bag, if :py:class:`policy <tiddlyweb.model.policy.Policy>` allows. """ bag_name = web.get_route_value(environ, 'bag_name') bag_name = web.handle_extension(environ, bag_name) bag = Bag(bag_name) store = environ['tiddlyweb.store'] length, _ = web.content_length_and_type(environ) usersign = environ['tiddlyweb.usersign'] try: bag = store.get(bag) bag.policy.allows(usersign, 'manage') except NoBagError: create_policy_check(environ, 'bag', usersign) try: serialize_type = web.get_serialize_type(environ)[0] serializer = Serializer(serialize_type, environ) serializer.object = bag content = web.read_request_body(environ, length) serializer.from_string(content.decode('utf-8')) bag.policy.owner = usersign['name'] _validate_bag(environ, bag) store.put(bag) except BagFormatError as exc: raise HTTP400('unable to put bag: %s' % exc) except TypeError: raise HTTP400('Content-type header required') except NoSerializationError: raise HTTP415('Content type not supported: %s' % serialize_type) start_response("204 No Content", [('Location', web.bag_url(environ, bag))]) return []
def list_entities(environ, start_response, method_name, store_list=None, serializer_list=None): """ Get an optionally :py:mod:`filtered <tiddlyweb.filters>` list of all the :py:class:`bags <tiddlyweb.model.bag.Bag>` or :py:class:`recipes <tiddlyweb.model.recipe.Recipe>` the current ``tiddlyweb.usersign`` can read. """ store = environ['tiddlyweb.store'] serialize_type, mime_type = get_serialize_type(environ, collection=True) serializer = Serializer(serialize_type, environ) filters = environ['tiddlyweb.filters'] if method_name: if not store_list: store_list = getattr(store, method_name) if not serializer_list: serializer_list = getattr(serializer, method_name) try: kept_entities = _filter_readable(environ, store_list(), filters) except FilterError as exc: raise HTTP400(exc) etag_string = '"%s:%s"' % (kept_entities.hexdigest(), sha(mime_type).hexdigest()) check_incoming_etag(environ, etag_string) try: output = serializer_list(kept_entities) except NoSerializationError: raise HTTP415('Content type not supported: %s' % mime_type) start_response("200 OK", [('Content-Type', mime_type), ('Vary', 'Accept'), ('Cache-Control', 'no-cache'), ('Etag', etag_string)]) if isinstance(output, basestring): return [output] else: return output
def get_serialize_type(environ, collection=False, accept_type=False): """ Look in the ``environ`` to determine which :py:class:`serializer <tiddlyweb.serializer.Serializer>` should be used for this request. If ``collection`` is ``True``, then the presence of an extension on the URI which does not match any serializer should lead to a ``415``. """ config = environ['tiddlyweb.config'] accept = environ.get('tiddlyweb.type', [])[:] ext = environ.get('tiddlyweb.extension') extension_types = config['extension_types'] serializers = config['serializers'] serialize_type, mime_type, candidate_type = None, None, None if collection and ext and ext not in extension_types: accept = [None] if type(accept) == unicode or type(accept) == str: accept = [accept] while len(accept) and serialize_type is None: candidate_type = accept.pop(0) try: serialize_type, mime_type = serializers[candidate_type] except KeyError: pass if not serialize_type: if ext: raise HTTP415('%s type unsupported' % ext) # If we are a PUT and we haven't found a serializer, don't # state a default as that makes no sense. if environ.get('REQUEST_METHOD') == 'GET': default_serializer = config['default_serializer'] serialize_type, mime_type = serializers[default_serializer] if accept_type: return serialize_type, mime_type, candidate_type else: return serialize_type, mime_type