def add_space_member(environ, start_response): """ Add a member to a space if they are not already a member. If they are already a member, nothing happens. If the username given does not exist, raise 409. If the space does not exist raise 404. """ store = environ['tiddlyweb.store'] space_name = get_route_value(environ, 'space_name') user_name = get_route_value(environ, 'user_name') current_user = environ['tiddlyweb.usersign'] _same_space_required(environ, space_name) try: change_space_member(store, space_name, add=user_name, current_user=current_user) except (NoBagError, NoRecipeError): raise HTTP404('space %s does not exist' % space_name) except NoUserError: raise HTTP409('attempt to add non-existent user: %s' % user_name) start_response('204 No Content', []) return ['']
def delete_space_member(environ, start_response): """ Remove a member from a space. If the space does not exist raise 404. If the named member is not in the space, do nothing. """ store = environ['tiddlyweb.store'] space_name = get_route_value(environ, 'space_name') user_name = get_route_value(environ, 'user_name') current_user = environ['tiddlyweb.usersign'] _same_space_required(environ, space_name) try: change_space_member(store, space_name, remove=user_name, current_user=current_user) except (NoBagError, NoRecipeError): raise HTTP404('space %s does not exist' % space_name) except NoUserError: raise HTTP409('attempt to remove non-member user: %s' % user_name) start_response('204 No Content', []) return ['']
def _determine_tiddler(environ, bag_finder, revisions=False): """ Determine, using URL info, the target tiddler. This can be complicated because of the mechanics of recipes and bags. Set revisions to True when the current request ends in `/revisions` or `/revisions.*`. Doing so ensures that processing of extensions does not impact the name of the tiddler. """ tiddler_name = get_route_value(environ, 'tiddler_name') tiddler = _base_tiddler_object(environ, tiddler_name, revisions) # We have to deserialize the tiddler here so that # PUTs to recipes are aware of values like tags when # doing filter checks. if environ['REQUEST_METHOD'] == 'PUT': _process_request_body(environ, tiddler) try: recipe_name = get_route_value(environ, 'recipe_name') recipe = Recipe(recipe_name) try: store = environ['tiddlyweb.store'] recipe = store.get(recipe) tiddler.recipe = recipe_name except NoRecipeError, exc: raise HTTP404('%s not found via recipe, %s' % (tiddler.title, exc)) try: bag = bag_finder(recipe, tiddler, environ) except NoBagError, exc: raise HTTP404('%s not found via bag, %s' % (tiddler.title, exc))
def _get_links(environ, start_response, linktype): """ Form the links as tiddlers and then send them to send_tiddlers. This allows us to use the serialization and filtering subsystems on the lists of links. """ bag_name = get_route_value(environ, 'bag_name') tiddler_title = get_route_value(environ, 'tiddler_name') store = environ['tiddlyweb.store'] filters = environ['tiddlyweb.filters'] collection_title = '%s for %s' % (linktype, tiddler_title) link = environ['SCRIPT_NAME'] try: extension = environ['tiddlyweb.extension'] link = link.rsplit('.%s' % extension)[0] except KeyError: pass host_tiddler = Tiddler(tiddler_title, bag_name) try: host_tiddler = store.get(host_tiddler) except StoreError, exc: raise HTTP404('No such tiddler: %s:%s, %s' % (host_tiddler.bag, host_tiddler.title, exc))
def bag_tiddler_uri_keys(environ): """ We have a tiddler in a bag URI provide the tiddler key and the bag tiddler key. """ bag_name = get_route_value(environ, 'bag_name') tiddler_title = get_route_value(environ, 'tiddler_name') tiddler = Tiddler(tiddler_title, bag_name) return [tiddler_key(tiddler), bag_key(tiddler.bag)]
def post_revisions(environ, start_response): """ Take a collection of JSON tiddlers, each with a text key and value, and process them into the store. That collection is known as a TiddlerChronicle. """ tiddler_name = get_route_value(environ, 'tiddler_name') bag_name = get_route_value(environ, 'bag_name') tiddler = Tiddler(tiddler_name, bag_name) return _post_tiddler_revisions(environ, start_response, tiddler)
def definition(environ, start_response): store = environ['tiddlyweb.store'] manifesto = get_route_value(environ, 'manifesto_name') definition = get_route_value(environ, 'definition') try: recipe = Recipe(manifesto) recipe = store.get(recipe) except NoRecipeError, exc: raise HTTP404('no such manifesto: %s' % exc)
def post_revisions(environ, start_response): """ Handle a ``POST`` of a chronicle of :py:class:`tiddlers <tiddlyweb.model.tiddler.Tiddler>` at a tiddler revisions URI. Take a collection of ``JSON`` tiddlers, each with a text key and value, and process them into the store. """ tiddler_name = get_route_value(environ, 'tiddler_name') bag_name = get_route_value(environ, 'bag_name') tiddler = Tiddler(tiddler_name, bag_name) return _post_tiddler_revisions(environ, start_response, tiddler)
def recipe_tiddler_uri_keys(environ): """ We have a tiddler in a recipe URI, provide the tiddler and the bag tiddler key for all the bags in the recipe. """ store = environ['tiddlyweb.store'] recipe_name = get_route_value(environ, 'recipe_name') tiddler_title = get_route_value(environ, 'tiddler_name') recipe = store.get(Recipe(recipe_name)) tiddler = Tiddler(tiddler_title) bag = determine_bag_from_recipe(recipe, tiddler, environ) tiddler.bag = bag.name return [recipe_key(recipe_name), tiddler_key(tiddler)] + [ bag_key(bag) for bag, _ in recipe.get_recipe()]
def _get_container(self): routing_args = self.environ.get('wsgiorg.routing_args', ([], {}))[1] container_name = False container_type = 'bags' if routing_args: if 'recipe_name' in routing_args: container_name = get_route_value(self.environ, 'recipe_name') container_type = 'recipes' elif 'bag_name' in routing_args: container_name = get_route_value(self.environ, 'bag_name') if container_name: return "%s/%s" % (container_type, container_name) else: return ""
def current_uri_keys(environ): """ Return relevant keys for the current uri based on routing_args and and other factors. """ config = environ.get('tiddlyweb.config', {}) routing_keys = environ['wsgiorg.routing_args'][1] request_uri = (environ.get('SCRIPT_NAME', '') + environ.get('PATH_INFO', '')) fastly_selector = config.get('fastly.selector') if _uri_is_global(request_uri): return [DISPATCH[request_uri]()] route_keys = [route_name for route_name in routing_keys if route_name in ROUTE_NAMES] surrogate_keys = [] try: if fastly_selector: # Find the selector method for this path (if any) method = fastly_selector.select(request_uri, 'GET')[0] surrogate_keys = method(environ, None) if surrogate_keys: return surrogate_keys except KeyError: pass if len(route_keys) == 1: name = route_keys[0] if '/tiddlers' not in request_uri: # recipe or bag surrogate_keys = [DISPATCH[name](get_route_value(environ, name))] else: # recipe or bags tiddlers if name == 'recipe_name': surrogate_keys = recipe_tiddlers_uri_keys(environ) elif name == 'bag_name': surrogate_keys = [bag_key(get_route_value( environ, name))] else: # a tiddler, revisions collection or single revision is_revision = 'revision' in route_keys if 'recipe_name' in route_keys: surrogate_keys = recipe_tiddler_uri_keys(environ) elif 'bag_name' in route_keys: if not is_revision: surrogate_keys = bag_tiddler_uri_keys(environ) return surrogate_keys
def get_identities(environ, start_response): """ Get a list of the identities associated with the named user. That named user must match the current user or the current user must be an admin. """ store = environ['tiddlyweb.store'] username = get_route_value(environ, 'username') usersign = environ['tiddlyweb.usersign']['name'] roles = environ['tiddlyweb.usersign']['roles'] if username != usersign and 'ADMIN' not in roles: raise HTTP403('Bad user for action') identities = [] try: mapped_bag = store.get(Bag('MAPUSER')) tiddlers = store.list_bag_tiddlers(mapped_bag) matched_tiddlers = control.filter_tiddlers( tiddlers, 'select=mapped_user:%s' % username, environ) identities = [tiddler.title for tiddler in matched_tiddlers] except NoBagError: pass start_response('200 OK', [('Content-Type', 'application/json; charset=UTF-8')]) return [simplejson.dumps(identities)]
def destroy_key(environ, start_response): """ Remove a key. """ config = environ['tiddlyweb.config'] store = environ['tiddlyweb.store'] username = environ['tiddlyweb.usersign']['name'] bag_name = config.get('oauth.tokens_bag', 'oauth_tokens') key_name = get_route_value(environ, 'key_name') tiddler = Tiddler(key_name, bag_name) try: tiddler = store.get(tiddler) except NoTiddlerError: raise HTTP404('key does not exist') if tiddler.modifier != username: raise HTTP409('owner mismatch') store.delete(tiddler) start_response('204 No Content', [ ('Content-Type', 'text/plain')]) return []
def get_identities(environ, start_response): """ Get a list of the identities associated with the named user. That named user must match the current user or the current user must be an admin. """ store = environ['tiddlyweb.store'] username = get_route_value(environ, 'username') usersign = environ['tiddlyweb.usersign']['name'] roles = environ['tiddlyweb.usersign']['roles'] if username != usersign and 'ADMIN' not in roles: raise HTTP403('Bad user for action') identities = [] try: mapped_bag = store.get(Bag('MAPUSER')) tiddlers = store.list_bag_tiddlers(mapped_bag) matched_tiddlers = control.filter_tiddlers(tiddlers, 'select=mapped_user:%s' % username, environ) identities = [tiddler.title for tiddler in matched_tiddlers] except NoBagError: pass start_response('200 OK', [ ('Content-Type', 'application/json; charset=UTF-8')]) return [simplejson.dumps(identities)]
def record_climbs(environ, start_response): """ Record a climb that has been accomplished. """ query = environ['tiddlyweb.query'] store = environ['tiddlyweb.store'] current_user = environ['tiddlyweb.usersign']['name'] gym = get_route_value(environ, 'gym') routes = query['route'] dones = query['doneroute'] for index, value in enumerate(dones): if value != '': route_title = routes[index] tiddler = Tiddler(route_title, current_user) try: tiddler = store.get(tiddler) except StoreError: pass if value in CLEAN_CLIMB: try: tiddler.tags.remove('tickwish') except ValueError: pass tiddler.fields['climbtype'] = value tiddler.fields['gym'] = gym tiddler.tags.append('climb') store.put(tiddler) raise HTTP303(server_base_url(environ) + environ['REQUEST_URI'])
def make_ticklist(environ, start_response): """ Record routes the user would like to climb. """ store = environ['tiddlyweb.store'] current_user = environ['tiddlyweb.usersign']['name'] gym = get_route_value(environ, 'gym') tick_wishes = environ['tiddlyweb.query'].get('addroute', []) routes = _get_gym_routes(environ, gym) for route in routes: route.bag = current_user if route.title in tick_wishes: route.tags = ['tickwish'] route.fields['gym'] = gym store.put(route) else: try: tiddler = store.get(route) tiddler.tags.remove('tickwish') store.put(tiddler) except (StoreError, ValueError): pass raise HTTP303(server_base_url(environ) + '/gyms/%s/ticklist' % encode_name(gym))
def recent_changes(environ, start_response): """ List recent changes for the named tank. """ tank_name = get_route_value(environ, 'bag_name') store = environ['tiddlyweb.store'] usersign = environ['tiddlyweb.usersign'] days = environ['tiddlyweb.query'].get('d', [7])[0] try: bag = store.get(Bag(tank_name)) bag.policy.allows(usersign, 'read') except NoBagError: raise HTTP404('no tank found for %s' % tank_name) tiddlers = (store.get(tiddler) for tiddler in filter_tiddlers( store.list_bag_tiddlers(bag), 'select=modified:>%sd;sort=-modified' % days, environ)) start_response('200 OK', [('Content-Type', 'text/html; charset=UTF-8'), ('Cache-Control', 'no-cache')]) return send_template(environ, CHANGES_TEMPLATE, { 'days': days, 'tiddlers': tiddlers, 'bag': bag, })
def get_tiddlers(environ, start_response): """ Get a list representation of the tiddlers in a bag. The information sent is dependent on the serialization chosen. """ store = environ["tiddlyweb.store"] filters = environ["tiddlyweb.filters"] bag_name = web.get_route_value(environ, "bag_name") bag = _get_bag(environ, bag_name) title = "Tiddlers From Bag %s" % bag.name title = environ["tiddlyweb.query"].get("title", [title])[0] usersign = environ["tiddlyweb.usersign"] # will raise exception if there are problems bag.policy.allows(usersign, "read") tiddlers = Tiddlers(title=title) if not filters: tiddlers.store = store tiddlers.bag = bag_name # A special bag can raise NoBagError here. try: for tiddler in store.list_bag_tiddlers(bag): tiddlers.add(tiddler) except NoBagError, exc: raise HTTP404("%s not found, %s" % (bag.name, exc))
def get_tiddlers(environ, start_response): # TODO: rename config = environ['tiddlyweb.config'] tags = get_route_value(environ, 'tags') tags = csv.reader([tags]).next() start_response('200 OK', [('Content-Type', 'text/html; charset=UTF-8')]) tiddlers = [] tiddler_ids = [] for _id, tiddler in commands.get_readable_tagged_tiddlers(environ, tags): # XXX: smell tiddlers.append(tiddler) tiddler_ids.append(_id) yield '<h2 id="tags">Related Tags</h2>\n' for tag in commands.get_readable_related_tags(environ, tags, tiddler_ids): params = sorted(tags + [tag]) # sorting ensures consistency csv_out = StringIO() writer = csv.writer(csv_out) writer.writerow(params) csv_out.seek(0) params = csv_out.read().rstrip() uri = '/tags/%s' % params # XXX: server prefix & encoding yield '<a href="%s">%s</a>\n' % (uri, tag) yield '<h2 id="tiddlers">Tiddlers</h2>\n' for tiddler in tiddlers: uri = '/bags/%s/tiddlers/%s' % (tiddler.bag, tiddler.title) # XXX: server prefix & encoding yield '<a href="%s">%s</a>\n' % (uri, tiddler.title)
def get_tiddlers(environ, start_response): """ Get a list representation of the tiddlers in a bag. The information sent is dependent on the serialization chosen. """ store = environ['tiddlyweb.store'] filters = environ['tiddlyweb.filters'] bag_name = web.get_route_value(environ, 'bag_name') bag = _get_bag(environ, bag_name) title = 'Tiddlers From Bag %s' % bag.name title = environ['tiddlyweb.query'].get('title', [title])[0] usersign = environ['tiddlyweb.usersign'] # will raise exception if there are problems bag.policy.allows(usersign, 'read') if filters: tiddlers = Tiddlers(title=title) else: tiddlers = Tiddlers(title=title, store=store) for tiddler in store.list_bag_tiddlers(bag): tiddlers.add(tiddler) tiddlers.link = '%s/tiddlers' % web.bag_url(environ, bag, full=False) return send_tiddlers(environ, start_response, tiddlers=tiddlers)
def gym_routes(environ, start_response): """ Display the routes from a single gym. """ store = environ['tiddlyweb.store'] current_user = environ['tiddlyweb.usersign']['name'] gym = get_route_value(environ, 'gym') routes = _get_gym_routes(environ, gym) climbtypes = [tiddler.title for tiddler in store.list_bag_tiddlers(Bag(CLIMBTYPES))] if current_user:# != 'GUEST': search_query = 'bag:%s tag:climb gym:%s _limit:%s' % (current_user, gym, len(routes)) recent_climbs = dict([(tiddler.title, store.get(tiddler)) for tiddler in store.search(search_query)]) search_query = 'bag:%s tag:tickwish gym:%s _limit:%s' % (current_user, gym, len(routes)) wished_climbs = [tiddler.title for tiddler in store.search(search_query)] for route in routes: if route.title in recent_climbs: route.fields['climbtype'] = recent_climbs[ route.title].fields['climbtype'] if route.title in wished_climbs: route.fields['do'] = True return send_template(environ, 'gym_routes.html', { 'gym': gym, 'climbtypes': [''] + climbtypes, 'title': 'Routes @%s' % gym, 'routes': routes})
def put(environ, start_response): """ Put a bag to the server, meaning the description and policy of the bag, if 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, exc: raise HTTP400('unable to put bag: %s' % exc)
def recent_changes(environ, start_response): """ List recent changes for the named tank. """ tank_name = get_route_value(environ, 'bag_name') store = environ['tiddlyweb.store'] usersign = environ['tiddlyweb.usersign'] config = environ['tiddlyweb.config'] days = environ['tiddlyweb.query'].get('d', [7])[0] try: bag = store.get(Bag(tank_name)) bag.policy.allows(usersign, 'read') except NoBagError: raise HTTP404('no tank found for %s' % tank_name) tiddlers = filter_tiddlers(store.list_bag_tiddlers(bag), 'select=modified:>%sd;sort=-modified' % days, environ) changes_template = get_template(environ, CHANGES_TEMPLATE) start_response('200 OK', [ ('Content-Type', 'text/html; charset=UTF-8'), ('Cache-Control', 'no-cache')]) return changes_template.generate({ 'socket_link': config.get('socket.link'), 'csrf_token': get_nonce(environ), 'days': days, 'tiddlers': tiddlers, 'bag': bag, 'gravatar': gravatar(environ), 'user': usersign['name'], })
def get_container(environ): routing_args = environ.get('wsgiorg.routing_args', ([], {}))[1] container_name = False container_type = 'bags' store = environ['tiddlyweb.store'] if routing_args: if 'recipe_name' in routing_args: container_name = get_route_value(self.environ, 'recipe_name') container_type = 'recipes' elif 'bag_name' in routing_args: container_name = get_route_value(self.environ, 'bag_name') if container_name: return "%s/%s" % (container_type, container_name) else: return ""
def get_tiddlers(environ, start_response): """ Get a list representation of the tiddlers in a bag. The information sent is dependent on the serialization chosen. """ store = environ['tiddlyweb.store'] filters = environ['tiddlyweb.filters'] bag_name = web.get_route_value(environ, 'bag_name') bag = _get_bag(environ, bag_name) title = 'Tiddlers From Bag %s' % bag.name title = environ['tiddlyweb.query'].get('title', [title])[0] usersign = environ['tiddlyweb.usersign'] # will raise exception if there are problems bag.policy.allows(usersign, 'read') tiddlers = Tiddlers(title=title) if not filters: tiddlers.store = store tiddlers.bag = bag_name for tiddler in store.list_bag_tiddlers(bag): tiddlers.add(tiddler) tiddlers.link = '%s/tiddlers' % web.bag_url(environ, bag, full=False) return send_tiddlers(environ, start_response, tiddlers=tiddlers)
def delete(environ, start_response): """ Remove a bag and its tiddlers from the 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 ticklist(environ, start_response): """ Display a user's own ticklist. """ store = environ['tiddlyweb.store'] current_user = environ['tiddlyweb.usersign']['name'] gym = get_route_value(environ, 'gym') routes = _get_gym_routes(environ, gym) climbtypes = [tiddler.title for tiddler in store.list_bag_tiddlers(Bag(CLIMBTYPES))] search_query = 'bag:%s tag:tickwish gym:%s _limit:%s' % (current_user, gym, len(routes)) wished_climbs = dict([(tiddler.title, store.get(tiddler)) for tiddler in store.search(search_query)]) for route in routes: if route.title in wished_climbs: wished_climbs[route.title].fields['do'] = True return send_template(environ, 'gym_routes.html', { 'gym': gym, 'climbtypes': [''] + climbtypes, 'title': 'Ticklist for %s@%s' % (current_user, gym), 'routes': wished_climbs.values()})
def manage_gym(environ, start_response): store = environ['tiddlyweb.store'] gym = get_route_value(environ, 'gym') routes_bag = store.get(Bag('%s_climbs' % gym)) news_bag = Bag('%s_news' % gym) # Bail out if we are not allowed to manage. routes_bag.policy.allows(environ['tiddlyweb.usersign'], 'manage') gym_tiddler = store.get(Tiddler(gym, GYMS_BAG)) try: latest_news = [tiddler for tiddler in filter_tiddlers( store.list_bag_tiddlers(news_bag), 'sort=-modified;limit=1', environ)][0] latest_news = store.get(latest_news) news_html = render_wikitext(latest_news, environ) latest_news.fields['html'] = news_html except IndexError: latest_news = Tiddler('tmp') latest_news.fields['html'] = '<p>No News</p>' routes = _get_gym_routes(environ, gym) return send_template(environ, 'manage_gym.html', { 'title': 'Manage %s' % gym, 'gym_tiddler': gym_tiddler, 'latest_news': latest_news, 'routes': routes})
def put(environ, start_response): """ Put a bag to the server, meaning the description and policy of the bag, if 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 = environ['CONTENT_LENGTH'] 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 = environ['wsgi.input'].read(int(length)) serializer.from_string(content.decode('utf-8')) bag.policy.owner = usersign['name'] _validate_bag(environ, bag) store.put(bag) except BagFormatError, exc: raise HTTP400('unable to put bag: %s' % exc)
def create_space(environ, start_response): """ Create a space if it does not yet exists. If it does raise 409. """ space_name = get_route_value(environ, 'space_name') _validate_space_name(environ, space_name) return _create_space(environ, start_response, space_name)
def list_tiddlers(self, tiddlers): """ If the URL is a list of bag tiddlers, we present a bag editing interface. Otherwise we use the parent serialization. """ if (self.environ['wsgiorg.routing_args'][1].get('tiddler_name')): return HTMLSerialization.list_tiddlers(self, tiddlers) try: name = get_route_value(environ, 'bag_name') return self._bag_list(tiddlers) except KeyError: # not a bag link try: name = get_route_value(environ, 'recipe_name') return self._recipe_list(tiddlers, name) except KeyError: return self._bag_list(tiddlers)
def atom_profile(environ, start_response): """ Send the atom profile, which is actually a search for tiddlers modified by the user. """ username = get_route_value(environ, 'username') search_string = _search_string(username) environ['tiddlyweb.query']['q'] = [search_string] return search(environ, start_response)
def recipe_tiddlers_uri_keys(environ): """ All the tiddlers in the recipe, provide BT of all the bags. """ store = environ['tiddlyweb.store'] recipe_name = get_route_value(environ, 'recipe_name') recipe = store.get(Recipe(recipe_name)) return [recipe_key(recipe_name)] + [ bag_key(bag) for bag, _ in recipe.get_recipe()]
def gym_routes(environ, start_response): """ Display the routes from a single gym. """ gym = get_route_value(environ, 'gym') routes = _get_gym_routes(environ, gym) return send_template(environ, 'gym_routes.html', { 'title': 'Routes @%s' % gym, 'routes': routes})
def _get_links(environ, start_response, linktype): """ Form the links as tiddlers and then send them to send_tiddlers. This allows us to use the serialization and filtering subsystems on the lists of links. """ bag_name = get_route_value(environ, 'bag_name') tiddler_title = get_route_value(environ, 'tiddler_name') store = environ['tiddlyweb.store'] filters = environ['tiddlyweb.filters'] title = '%s for %s' % (linktype, tiddler_title) tiddler = Tiddler(tiddler_title, bag_name) try: tiddler = store.get(tiddler) except StoreError, exc: raise HTTP404('No such tiddler: %s:%s, %s' % (tiddler.bag, tiddler.title, exc))
def gym_editor(environ, start_response): store = environ['tiddlyweb.store'] gym = get_route_value(environ, 'gym') try: tiddler = Tiddler(gym, GYMS_BAG) tiddler = store.get(tiddler) except StoreError: pass # new gym return send_template(environ, 'gym_editor.html', {'tiddler': tiddler, 'title': 'Edit Gym %s' % gym})
def _tiddlers_collection_uri(self): """ Calculate the uri of the current tiddler collection. This ought to use tiddlyweb.model.collection but the code is not there yet. """ recipe_name = bag_name = None try: recipe_name = get_route_value(self.environ, 'recipe_name') except KeyError: try: bag_name = get_route_value(self.environ, 'bag_name') except KeyError: return None if recipe_name: base = recipe_url(self.environ, Recipe(recipe_name), full=True) else: base = bag_url(self.environ, Bag(bag_name), full=True) return base + '/tiddlers'
def _determine_tiddler(environ, bag_finder, revisions=False): """ Determine, using URL info, the target tiddler. This can be complicated because of the mechanics of recipes and bags. Set revisions to True when the current request ends in ``/revisions`` or ``/revisions.*``. Doing so ensures that processing of extensions does not impact the name of the tiddler. """ tiddler_name = get_route_value(environ, 'tiddler_name') tiddler = _base_tiddler_object(environ, tiddler_name, revisions) # We have to deserialize the tiddler here so that # PUTs to recipes are aware of values like tags when # doing filter checks. if environ['REQUEST_METHOD'] == 'PUT': _process_request_body(environ, tiddler) try: recipe_name = get_route_value(environ, 'recipe_name') recipe = Recipe(recipe_name) try: store = environ['tiddlyweb.store'] recipe = store.get(recipe) tiddler.recipe = recipe_name except NoRecipeError as exc: raise HTTP404('%s not found via recipe, %s' % (tiddler.title, exc)) try: bag = bag_finder(recipe, tiddler, environ) except NoBagError as exc: raise HTTP404('%s not found via bag, %s' % (tiddler.title, exc)) bag_name = bag.name except KeyError: bag_name = get_route_value(environ, 'bag_name') tiddler.bag = bag_name return tiddler
def get(environ, start_response): """ Get a representation in some serialization of a bag (the bag itself not the tiddlers within). """ bag_name = web.get_route_value(environ, 'bag_name') bag_name = web.handle_extension(environ, bag_name) bag = _get_bag(environ, bag_name) bag.policy.allows(environ['tiddlyweb.usersign'], 'manage') return send_entity(environ, start_response, bag)
def _determine_tiddler(environ, bag_finder): """ Determine, using URL info, the target tiddler. This can be complicated because of the mechanics of recipes and bags. """ tiddler_name = web.get_route_value(environ, 'tiddler_name') try: revision = web.get_route_value(environ, 'revision') revision = web.handle_extension(environ, revision) except KeyError: tiddler_name = web.handle_extension(environ, tiddler_name) revision = None tiddler = Tiddler(tiddler_name) if revision: try: revision = int(revision) tiddler.revision = revision except ValueError, exc: raise HTTP404('%s not a revision of %s: %s' % (revision, tiddler_name, exc))
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 _determine_recipe(environ): """ Interpret URL information to determine the target recipe and then get it from the store. """ 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'] try: recipe = store.get(recipe) except NoRecipeError, exc: raise HTTP404('%s not found, %s' % (recipe.name, exc))
def confirm_space(environ, start_response): """ Confirm a spaces exists. If it does, raise 204. If not, raise 404. """ store = environ['tiddlyweb.store'] space_name = get_route_value(environ, 'space_name') try: space = Space(space_name) store.get(Recipe(space.public_recipe())) store.get(Recipe(space.private_recipe())) except (NoRecipeError, ValueError): raise HTTP404('%s does not exist' % space_name) start_response('204 No Content', []) return ['']
def subscribe_space(environ, start_response): """ Subscribe and/or unsubscribe the spaces named in the JSON content of the request to the space named in the URI. The current user must be a member of the space. Raise 409 if the JSON is no good. Raise 404 if the space does not exist. Raise 409 if a space in the JSON does not exist. """ store = environ['tiddlyweb.store'] space_name = get_route_value(environ, 'space_name') current_user = environ['tiddlyweb.usersign'] try: current_space = Space(space_name) except ValueError, exc: raise HTTP409('Invalid space name: %s' % exc)
def get(environ, start_response): """ Handle ``GET`` on a single bag URI. Get a representation in some serialization determined by :py:mod:`tiddlyweb.web.negotiate` of a :py:class:`bag <tiddlyweb.model.bag.Bag>` (the bag itself, not the tiddlers within). """ bag_name = web.get_route_value(environ, 'bag_name') bag_name = web.handle_extension(environ, bag_name) bag = _get_bag(environ, bag_name) bag.policy.allows(environ['tiddlyweb.usersign'], 'manage') return send_entity(environ, start_response, bag)
def _determine_challenger(environ, challenger_name=None): """ Determine which challenger we are using and import it as necessary. """ if challenger_name is None: challenger_name = get_route_value(environ, 'challenger') # If the challenger is not in config, do a 404, we don't want # to import any old code. if challenger_name not in environ['tiddlyweb.config']['auth_systems']: raise HTTP404('Challenger Not Found') try: return _get_challenger_module(challenger_name) except ImportError as exc: raise HTTP404('Unable to import challenger %s: %s' % (challenger_name, exc))
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_space_members(environ, start_response): """ List the members of the named space. You must be a member to list the members. """ store = environ['tiddlyweb.store'] space_name = get_route_value(environ, 'space_name') current_user = environ['tiddlyweb.usersign'] try: space = Space(space_name) private_space_bag = store.get(Bag(space.private_bag())) private_space_bag.policy.allows(current_user, 'manage') members = [ member for member in private_space_bag.policy.manage if not member.startswith('R:') ] except (ValueError, NoBagError): raise HTTP404('No space for %s' % space_name) start_response('200 OK', [('Cache-Control', 'no-cache'), ('Content-Type', 'application/json; charset=UTF-8')]) return simplejson.dumps(members)
def comp(environ, start_response): usersign = environ['tiddlyweb.usersign'] store = environ['tiddlyweb.store'] recipe_name = get_route_value(environ, 'recipe_name') tiddler = Tiddler('app') recipe = Recipe(recipe_name) try: recipe = store.get(recipe) recipe.policy.allows(usersign, 'read') except NoRecipeError as exc: raise HTTP404('unable to find recipe %s' % recipe_name) try: bag = determine_bag_from_recipe(recipe, tiddler, environ) except NoBagError as exc: raise HTTP404('%s not found via bag, %s' % (tiddler.title, exc)) tiddler.bag = bag.name return _send_tiddler(environ, start_response, tiddler)
def _base_tiddler_object(environ, tiddler_name, revisions): """ Create a tiddler object, without bag or recipe, based on URI values. ``revisions`` is true when the request is for a revisions collection. When true, extension handling is not done on the tiddler_name. """ if not revisions: try: revision = get_route_value(environ, 'revision') revision = handle_extension(environ, revision) except KeyError: tiddler_name = handle_extension(environ, tiddler_name) revision = None else: revision = None tiddler = Tiddler(tiddler_name) tiddler.revision = revision # reset to default None if HEAD return tiddler
def closet(environ, start_response): """ Read file input and write it to special storage. """ store = environ['tiddlyweb.store'] usersign = environ['tiddlyweb.usersign'] bag_name = get_route_value(environ, 'bag_name') redir = environ['tiddlyweb.query'].get('redir', [False])[0] target_name = environ['tiddlyweb.query'].get('name', [None])[0] bag = store.get(Bag(bag_name)) bag.policy.allows(usersign, 'create') bag.policy.allows(usersign, 'write') files = environ['tiddlyweb.input_files'] if not files: raise HTTP400('missing file input') tiddlers = [] for input_file in files: if target_name is None: target_name = input_file.filename if pseudo_binary(input_file.type): tiddler = _regular_tiddler(environ, bag_name, input_file, target_name) else: tiddler = _binary_tiddler(environ, bag_name, input_file, target_name) tiddlers.append(tiddler) response_code = '303 See Other' if redir else '204 No Content' start_response(response_code, [('Location', tiddler_url(environ, tiddlers[-1]))]) return []
def get_tiddlers(environ, start_response): """ Handle ``GET`` on a tiddlers-within-a-bag URI. Get a list representation of the :py:class:`tiddlers <tiddlyweb.model.tiddler.Tiddler>` in a :py:class:`bag <tiddlyweb.model.bag.Bag>`. The information sent is dependent on the serialization chosen via :py:mod:`tiddlyweb.web.negotiate`. """ store = environ['tiddlyweb.store'] filters = environ['tiddlyweb.filters'] bag_name = web.get_route_value(environ, 'bag_name') bag = _get_bag(environ, bag_name) title = 'Tiddlers From Bag %s' % bag.name title = environ['tiddlyweb.query'].get('title', [title])[0] usersign = environ['tiddlyweb.usersign'] # will raise exception if there are problems bag.policy.allows(usersign, 'read') tiddlers = Tiddlers(title=title) if not filters: tiddlers.store = store tiddlers.bag = bag_name # A special bag can raise NoBagError here. try: for tiddler in store.list_bag_tiddlers(bag): tiddlers.add(tiddler) except NoBagError as exc: raise HTTP404('%s not found, %s' % (bag.name, exc)) tiddlers.link = '%s/tiddlers' % web.bag_url(environ, bag, full=False) return send_tiddlers(environ, start_response, tiddlers=tiddlers)
def put(environ, start_response): """ Put a new recipe to the server. """ 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 = environ['CONTENT_LENGTH'] 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 = environ['wsgi.input'].read(int(length)) serializer.from_string(content.decode('utf-8')) recipe.policy.owner = usersign['name'] _validate_recipe(environ, recipe) store.put(recipe) except RecipeFormatError, exc: raise HTTP400('unable to put recipe: %s' % exc)
def destroy_key(environ, start_response): """ Remove a key. """ config = environ['tiddlyweb.config'] store = environ['tiddlyweb.store'] username = environ['tiddlyweb.usersign']['name'] bag_name = config.get('oauth.tokens_bag', 'oauth_tokens') key_name = get_route_value(environ, 'key_name') tiddler = Tiddler(key_name, bag_name) try: tiddler = store.get(tiddler) except NoTiddlerError: raise HTTP404('key does not exist') if tiddler.modifier != username: raise HTTP409('owner mismatch') store.delete(tiddler) start_response('204 No Content', [('Content-Type', 'text/plain')]) return []
def html_profile(environ, start_response): """ Send the HTML profile, made up for the user's SiteIcon, their profile from their space, and their most recently modified tiddlers. """ username = get_route_value(environ, 'username') usersign = environ['tiddlyweb.usersign'] store = environ['tiddlyweb.store'] # Confirm username is a real User try: store.get(User(username)) except NoUserError: raise HTTP404('Profile not found for %s' % username) activity_feed = profile_atom_url(environ, username) profile_tiddler = Tiddler('profile', '%s_public' % username) try: profile_tiddler = store.get(profile_tiddler) except StoreError, exc: raise HTTP404('No profile for %s: %s' % (username, exc))