Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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 []
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
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 []
Ejemplo n.º 6
0
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')
Ejemplo n.º 7
0
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 []
Ejemplo n.º 8
0
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
Ejemplo n.º 9
0
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