def _process_request_body(environ, tiddler): """ Read request body to set tiddler content. If a serializer exists for the content type, use it, otherwise treat the content as binary or pseudo-binary tiddler. """ length, content_type = content_length_and_type(environ) content = read_request_body(environ, length) try: try: serialize_type = get_serialize_type(environ)[0] serializer = Serializer(serialize_type, environ) # Short circuit de-serialization attempt to avoid # decoding content multiple times. if hasattr(serializer.serialization, 'as_tiddler'): serializer.object = tiddler try: serializer.from_string(content.decode('utf-8')) except TiddlerFormatError as exc: raise HTTP400('unable to put tiddler: %s' % exc) else: raise NoSerializationError() except NoSerializationError: tiddler.type = content_type if pseudo_binary(tiddler.type): tiddler.text = content.decode('utf-8') else: tiddler.text = content except UnicodeDecodeError as exc: raise HTTP400('unable to decode tiddler, utf-8 expected: %s' % exc)
def do_user_auth(environ, start_response): """ Consumer authorization for the sake of a user. If no `code` is present then we send the user to the auth uri of the selected provider. If there is a code then we use that to get an access token, then use that access token to get some information about the user at the provider. XXX: Save the access token for later use. """ query = environ['tiddlyweb.query'] config = environ['tiddlyweb.config'] code = query.get('code', [None])[0] error = query.get('error', [None])[0] server_name = query.get('server_name', [None])[0] redirect_uri = query.get('tiddlyweb_redirect', [None])[0] if not server_name: raise HTTP400('invalid request, server_name required') # initial redirect if not code and not error: raise HTTP302(get_auth_uri(config, server_name, redirect_uri)) response_map = config['oauth.servers'][server_name].get('response_map') output = [] if code: try: credentials, http = get_credentials(config, server_name, code) except OAuthError as exc: raise HTTP400('credentials failure: %s' % exc) credentials.authorize(http) response, content = http.request( config['oauth.servers'][server_name]['info_uri']) if response['status'] == '200': if response_map: return _do_login_or_register(environ, start_response, server_name, response_map, content) else: output.append('code: %s\n' % code) output.append(content) else: output.append('Unable to reach info_uri') if error: output.append('error: %s\n' % error) start_response('200 OK', [('Content-Type', 'text-plain')]) 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 forge(environ, start_response): """ Handle a post to create a new tank. """ query = environ['tiddlyweb.query'] usersign = environ['tiddlyweb.usersign'] try: tank_name = query['name'][0] tank_policy = query['policy_type'][0] except KeyError: raise HTTP400('tank_name and tank_policy required') tank_desc = query.get('desc', [''])[0] try: create_wiki(environ, tank_name, mode=tank_policy, username=usersign['name'], desc=tank_desc) except InvalidBagError: return dash(environ, start_response, message='Over quota!') uri = tank_uri(environ, tank_name) start_response('303 See Other', [ ('Location', str(uri))]) return []
def delete(environ, start_response): """ Handle ``DELETE`` on a single bag URI. Remove the :py:class:`bag <tiddlyweb.model.bag.Bag>` and the :py:class:`tiddlers <tiddlyweb.model.tiddler.Tiddler>` within from the :py:class:`store <tiddlyweb.store.Store>`. How the store chooses to handle remove and what it means is up to the store. """ bag_name = web.get_route_value(environ, 'bag_name') bag_name = web.handle_extension(environ, bag_name) usersign = environ['tiddlyweb.usersign'] bag = _get_bag(environ, bag_name) bag.policy.allows(usersign, 'manage') # reuse the store attribute that was set on the # bag when we "got" it. # we don't need to check for existence here because # the above get already did try: store = environ['tiddlyweb.store'] store.delete(bag) except StoreMethodNotImplemented: raise HTTP400('Bag DELETE not supported') start_response("204 No Content", []) return []
def extract_query(self, environ): """ Read the ``QUERY_STRING`` and body (if a POSTed form) to extract query parameters. Put the results in ``tiddlyweb.query`` in environ. The query names and values are decoded from UTF-8 to unicode. If there are file uploads in posted form data, the files are not put into ``tiddlyweb.query``. Instead the file handles are appended to ``tiddlyweb.input_files``. """ content_type = environ.get('CONTENT_TYPE', '') environ['tiddlyweb.query'] = {} environ['tiddlyweb.input_files'] = [] if _cgi_post(environ, content_type): _process_post(environ, content_type) filters, leftovers = parse_for_filters(environ.get('QUERY_STRING', ''), environ) query_data = parse_qs(leftovers, keep_blank_values=True) try: _update_tiddlyweb_query(environ, query_data, encoded=ENCODED_QUERY) except UnicodeDecodeError as exc: raise HTTP400( 'Invalid encoding in query string, utf-8 required: %s', exc) environ['tiddlyweb.filters'] = filters
def app_info(environ, start_response): """ At the provider display the stored information about a app, given its id in the query parameter `app`. Only the client/app owner can see the secret. """ query = environ['tiddlyweb.query'] current_user = environ['tiddlyweb.usersign']['name'] app_id = query.get('app', [None])[0] if not app_id: raise HTTP400('app parameter required') try: app = get_app(environ, app_id) except StoreError: raise HTTP404('no matching app found') start_response('200 OK', [( 'Content-Type', 'text/plain; charset=UTF-8')]) output = ['client id: %s' % app.title] if current_user == app.modifier: output.append('client secret: %s' % app.fields['client_secret']) return output
def create_app(environ, start_response): """ At the provider, register a client and provide them with an id, secret, etc. This is not part of the oAuth spec, but is fairly standard form for what is usually called "creating an app". On success redirects to the info page for the app. """ query = environ['tiddlyweb.query'] current_user = environ['tiddlyweb.usersign']['name'] data = {} for key in ['name', 'app_url', 'callback_url', 'logo']: if key in query: data[key] = query[key][0] data['owner'] = current_user try: app_tiddler = create(**data) except TypeError as exc: raise HTTP400('Invalid form submission: %s' % exc) # let a store error raise to a 500 (for now) app_tiddler = store_app(environ, app_tiddler) raise HTTP303(server_base_url(environ) + '/_oauth/appinfo?app=%s' % app_tiddler.title)
def _do_login_or_register(environ, start_response, server_name, response_map, content): """ We had a valid response from the oauth provider, let's see if that is a user or somebody we can register. """ store = environ['tiddlyweb.store'] config = environ['tiddlyweb.config'] userinfo = simplejson.loads(content) userdata = {} for key, value in response_map.iteritems(): userdata[key] = userinfo.get(value, '') server_login = None username = userdata['login'] if not username: raise HTTP400('extractable username data required') userdata['server_name'] = server_name if config.get('oauth.use_mapuser', False): server_login = '******' % (server_name, username) map_bag_name = config.get('magicuser.map', 'MAPUSER') tiddler = Tiddler(server_login, map_bag_name) try: tiddler = store.get(tiddler) mapped_user = tiddler.fields.get('mapped_user') store.get(User(mapped_user)) user = User(server_login) return _send_cookie(environ, start_response, user) except StoreError: try: local_user = store.get(User(username)) except StoreError: local_user = None pass # fall through to register else: try: user = store.get(User(username)) return _send_cookie(environ, start_response, user) except StoreError: local_user = None pass # fall through to register registration_template = get_template(environ, 'registration.html') start_response('200 OK', [('Content-Type', 'text/html; charset=UTF-8'), ('Cache-Control', 'no-store')]) if local_user: userdata['local_user'] = local_user.usersign userdata['server_name_sig'] = _sign(config, server_name) if server_login: userdata['server_login'] = server_login userdata['server_login_sig'] = _sign(config, server_login) return registration_template.generate(userdata)
def post_createrecipe(environ, start_response): user = get_user_object(environ) store = environ['tiddlyweb.store'] recipe_name = environ['tiddlyweb.query'].get('recipe', [''])[0] bag_name = environ['tiddlyweb.query'].get('bag', [''])[0] publicity = environ['tiddlyweb.query'].get('publicity', [''])[0] description = environ['tiddlyweb.query'].get('description', [''])[0] if not bag_name or not recipe_name: raise HTTP400('missing data') recipe = Recipe(recipe_name) bag = Bag(bag_name) try: recipe = store.get(recipe) raise HTTP400('recipe exists') except NoRecipeError: pass try: bag = store.get(bag) try: bag.policy.allows(user, 'read') except (UserRequiredError, ForbiddenError): raise HTTP400('bag not readable') except NoBagError: bag.policy.owner = user['name'] for constraint in ['read', 'write', 'create', 'delete', 'manage']: setattr(bag.policy, constraint, [user['name']]) store.put(bag) if publicity == 'private': recipe.policy.read = [user['name']] else: recipe.policy.read = [] recipe.policy.manage = [user['name']] recipe.policy.owner = user['name'] recipe.desc = description recipe.set_recipe([ ('system', ''), (bag.name, ''), ]) store.put(recipe) raise HTTP303('%s/home' % server_base_url(environ))
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 editor(environ, start_response, extant_tiddler=None, message=''): store = environ['tiddlyweb.store'] usersign = environ['tiddlyweb.usersign'] query = environ['tiddlyweb.query'] if extant_tiddler: tiddler = extant_tiddler bag = _fill_bag(store, Bag(tiddler.bag)) else: try: bag_name = query['bag'][0] tiddler_title = query['tiddler'][0] except KeyError: raise HTTP400('bad query: bag and tiddler required') if not (bag_name and tiddler_title): raise HTTP400('bad query: bag and tiddler required') bag = _fill_bag(store, Bag(bag_name)) tiddler = Tiddler(tiddler_title, bag_name) tiddler_new = False try: tiddler = store.get(tiddler) except NoTiddlerError: tiddler.text = '' tiddler.type = 'text/x-markdown' tiddler_new = True if tiddler_new: bag.policy.allows(usersign, 'create') else: bag.policy.allows(usersign, 'write') start_response('200 OK', [ ('Content-Type', 'text/html; charset=UTF-8'), ('Cache-Control', 'no-cache')]) return send_template(environ, EDIT_TEMPLATE, { 'bag': bag, 'message': message, 'tiddler': tiddler, 'etag': tiddler_etag(environ, tiddler).replace('"', '').split(':', 1)[0] })
def get_tiddlers(environ, start_response): """ Handle ``GET`` on a tiddlers-within-a-recipe URI. Get a list representation of the :py:class:`tiddlers <tiddlyweb.model.tiddler.Tiddler>` generated from a :py:class:`recipe <tiddlyweb.model.recipe.Recipe>`. The information sent is dependent on the serialization chosen via :py:mod:`tiddlyweb.web.negotiate`. """ usersign = environ['tiddlyweb.usersign'] store = environ['tiddlyweb.store'] filters = environ['tiddlyweb.filters'] recipe = _determine_recipe(environ) title = 'Tiddlers From Recipe %s' % recipe.name title = environ['tiddlyweb.query'].get('title', [title])[0] # check the recipe can be read recipe.policy.allows(usersign, 'read') # check the bags in the recipe can be read try: template = control.recipe_template(environ) for bag_name, _ in recipe.get_recipe(template): bag = Bag(bag_name) bag = store.get(bag) bag.policy.allows(usersign, 'read') except NoBagError as exc: raise HTTP404('recipe %s lists an unknown bag: %s' % (recipe.name, exc)) # from this point forward we know the tiddlers are # readable # get the tiddlers from the recipe and uniquify them try: candidate_tiddlers = control.get_tiddlers_from_recipe(recipe, environ) except NoBagError as exc: raise HTTP404('recipe %s lists an unknown bag: %s' % (recipe.name, exc)) except FilterError as exc: raise HTTP400('malformed filter: %s' % exc) tiddlers = Tiddlers(title=title) if not filters: tiddlers.store = store tiddlers.recipe = recipe.name for tiddler in candidate_tiddlers: tiddler.recipe = recipe.name tiddlers.add(tiddler) tiddlers.link = '%s/tiddlers' % web.recipe_url(environ, recipe, full=False) return send_tiddlers(environ, start_response, tiddlers=tiddlers)
def get_search_query(environ): """ Inspect :py:mod:`tiddlyweb.query <tiddlyweb.web.query>` in the environment to find the search query in a parameter named ``q``. """ try: search_query = environ['tiddlyweb.query']['q'][0] except (KeyError, IndexError): raise HTTP400('query string required') return search_query
def entity_policy(environ, start_response): publicity = environ['tiddlyweb.query'].get('publicity', [''])[0] bag_name = environ['tiddlyweb.query'].get('bag', [''])[0] recipe_name = environ['tiddlyweb.query'].get('recipe', [''])[0] if bag_name: return _bag_policy(environ, bag_name, publicity) elif recipe_name: return _recipe_policy(environ, recipe_name, publicity) else: raise HTTP400('missing form data')
def content_length_and_type(environ): """ For ``PUT`` or ``POST`` request there must be ``Content-Length`` and ``Content-Type`` headers. Raise ``400`` if not present in the request. """ try: length = environ['CONTENT_LENGTH'] content_type = environ['tiddlyweb.type'] except KeyError: raise HTTP400( 'Content-Length and content-type required to PUT or POST') return length, content_type
def _process_post(environ, content_type): """ Process posted form data. """ try: if content_type.startswith('application/x-www-form-urlencoded'): posted_data = _process_encodedform(environ) elif content_type.startswith('multipart/form-data'): posted_data = _process_multipartform(environ) _update_tiddlyweb_query(environ, posted_data, encoded=ENCODED_QUERY) except UnicodeDecodeError as exc: raise HTTP400('Invalid encoding in query data, utf-8 required: %s', exc)
def read_request_body(environ, length): """ Read the ``wsgi.input`` handle to get the request body. Length is a required parameter because it is tested for existence earlier in the process. """ try: length = int(length) input_handle = environ['wsgi.input'] return input_handle.read(length) except (KeyError, ValueError, IOError) as exc: raise HTTP400('Error reading request body: %s' % exc)
def _handle_descendant(self, req_uri, space, visible_bags): """ Process a request which is not /bags, /recipes, or /search, but a descendant of /bags or /recipes. If the URI points to things which are not in this space, 404. """ try: entity_name = urllib.unquote(req_uri.split('/')[2]).decode('utf-8') except UnicodeDecodeError, exc: raise HTTP400( 'incorrect encoding in URI, url-escaped utf-8 required: %s' % exc)
def _process_encodedform(environ): """ Read ``application/x-www-form-urlencoded`` from the request body and parse for form data and return. """ try: length = environ['CONTENT_LENGTH'] content = read_request_body(environ, length) if not ENCODED_QUERY: content = content.decode('UTF-8') except KeyError as exc: raise HTTP400('Invalid post, unable to read content: %s' % exc) return parse_qs(content, keep_blank_values=True)
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 get(environ, start_response): """ Handle ``GET`` on the search URI. Perform a search against the :py:class:`store <tiddlyweb.store.Store>`. What search means and what results are returned is dependent on the search implementation (if any) in the :py:class:`chosen store <tiddlyweb.stores.StorageInterface>`. """ store = environ['tiddlyweb.store'] filters = environ['tiddlyweb.filters'] search_query = get_search_query(environ) title = 'Search for %s' % search_query title = environ['tiddlyweb.query'].get('title', [title])[0] try: tiddlers = get_tiddlers(environ) usersign = environ['tiddlyweb.usersign'] if filters: candidate_tiddlers = Tiddlers(title=title) else: candidate_tiddlers = Tiddlers(title=title, store=store) candidate_tiddlers.is_search = True for tiddler in readable_tiddlers_by_bag(store, tiddlers, usersign): candidate_tiddlers.add(tiddler) except StoreMethodNotImplemented: raise HTTP400('Search system not implemented') except StoreError as exc: raise HTTP400('Error while processing search: %s' % exc) return send_tiddlers(environ, start_response, tiddlers=candidate_tiddlers)
def post_createbag(environ, start_response): user = get_user_object(environ) store = environ['tiddlyweb.store'] bag_name = environ['tiddlyweb.query'].get('bag', [''])[0] publicity = environ['tiddlyweb.query'].get('publicity', [''])[0] description = environ['tiddlyweb.query'].get('description', [''])[0] if not bag_name: raise HTTP400('missing data') bag = Bag(bag_name) try: bag = store.get(bag) raise HTTP400('bag exists') except NoBagError: pass if publicity == 'public': bag = ensure_public_bag(store, user['name'], desc=description, name=bag_name) elif publicity == 'protected': bag = ensure_protected_bag(store, user['name'], desc=description, name=bag_name) else: bag = ensure_private_bag(store, user['name'], desc=description, name=bag_name) # the bag has already been stored raise HTTP303('%s/tiddlers' % bag_url(environ, bag))
def _regular_tiddler(environ, bag_name, input_file, target_name): store = environ['tiddlyweb.store'] username = environ['tiddlyweb.usersign']['name'] content = input_file.file.read() tiddler = Tiddler(target_name, bag_name) try: tiddler.text = content.decode('utf-8') except UnicodeError as exc: raise HTTP400('tiddler content should be utf-8 encode: %s' % exc) tiddler.modifier = username tiddler.type = input_file.type store.put(tiddler) return tiddler
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 _store_tiddler_revisions(environ, content, tiddler): """ Given JSON revisions in content, store them as a revision history to tiddler. """ try: json_tiddlers = simplejson.loads(content) except ValueError as exc: raise HTTP409('unable to handle json: %s' % exc) store = environ['tiddlyweb.store'] serializer = Serializer('json', environ) serializer.object = tiddler try: for json_tiddler in reversed(json_tiddlers): json_string = simplejson.dumps(json_tiddler) serializer.from_string(json_string) store.put(tiddler) except NoTiddlerError as exc: raise HTTP400('Unable to store tiddler revisions: %s' % exc)
def _process_multipartform(environ): """ Read ``multipart/form-data`` using ``FieldStorage``, return a dictionary of form data and set ``tiddlyweb.input_files`` to a list of available files. """ posted_data = {} try: field_storage = FieldStorage(fp=environ['wsgi.input'], environ=environ, keep_blank_values=True) except ValueError as exc: raise HTTP400('Invalid post, bad form: %s' % exc) for key in field_storage.keys(): if (hasattr(field_storage[key], 'filename') and field_storage[key].filename): environ['tiddlyweb.input_files'].append(field_storage[key]) else: posted_data[key] = field_storage.getlist(key) return posted_data
def _filter_tiddlers(filters, store, tiddlers): """ Filter the tiddlers by filters provided by the environment. """ candidate_tiddlers = Tiddlers(store=store) try: candidate_tiddlers.title = tiddlers.title candidate_tiddlers.link = tiddlers.link candidate_tiddlers.is_search = tiddlers.is_search candidate_tiddlers.is_revisions = tiddlers.is_revisions candidate_tiddlers.bag = tiddlers.bag candidate_tiddlers.recipe = tiddlers.recipe except AttributeError: pass try: for tiddler in recursive_filter(filters, tiddlers): candidate_tiddlers.add(tiddler) except FilterError as exc: raise HTTP400('malformed filter: %s' % exc) return candidate_tiddlers
def delete(environ, start_response): """ Handle ``DELETE`` on a single recipe URI. Delete a :py:class:`recipe <tiddlyweb.model.recipe.Recipe>`. This just removes the recipe, not any associated :py:class:`bags <tiddlyweb.model.bag.Bag>` or :py:class:`tiddlers <tiddlyweb.model.tiddler.Tiddler>`. """ recipe = _determine_recipe(environ) store = environ['tiddlyweb.store'] recipe.policy.allows(environ['tiddlyweb.usersign'], 'manage') try: store.delete(recipe) except StoreMethodNotImplemented: raise HTTP400('Recipe DELETE not supported') start_response("204 No Content", []) return []
def _send_tiddler_revisions(environ, start_response, tiddler): """ Push the list of tiddler revisions out the network. """ store = environ['tiddlyweb.store'] title = 'Revisions of Tiddler %s' % tiddler.title title = environ['tiddlyweb.query'].get('title', [title])[0] container = 'recipes' if tiddler.recipe else 'bags' if environ['tiddlyweb.filters']: tiddlers = Tiddlers(title=title) else: tiddlers = Tiddlers(title=title, store=store) tiddlers.is_revisions = True tiddlers.link = '%s/revisions' % tiddler_url(environ, tiddler, container=container, full=False) # Set the container on the tiddlers. Since tiddler.recipe # defaults to None, we're "safe" here. tiddlers.recipe = tiddler.recipe tiddlers.bag = tiddler.bag try: for revision in store.list_tiddler_revisions(tiddler): tmp_tiddler = Tiddler(title=tiddler.title, bag=tiddler.bag) tmp_tiddler.revision = revision tmp_tiddler.recipe = tiddler.recipe tiddlers.add(tmp_tiddler) except NoTiddlerError as exc: # If a tiddler is not present in the store. raise HTTP404('tiddler %s not found, %s' % (tiddler.title, exc)) except NoBagError as exc: raise HTTP404('tiddler %s not found, bag %s does not exist, %s' % (tiddler.title, tiddler.bag, exc)) except StoreMethodNotImplemented: raise HTTP400('no revision support') return send_tiddlers(environ, start_response, tiddlers=tiddlers)