def get(environ, start_response): """ Using query parameters, determine the current tiddler and produce an editor for it. """ usersign = environ['tiddlyweb.usersign'] try: tiddler_name = environ['tiddlyweb.query'].get('tiddler', [''])[0] recipe_name = environ['tiddlyweb.query'].get('recipe', [''])[0] bag_name = environ['tiddlyweb.query'].get('bag', [''])[0] except (KeyError, IndexError): raise HTTP400('tiddler, recipe and bag query strings required') store = environ['tiddlyweb.store'] tiddler = Tiddler(tiddler_name) if bag_name: tiddler.bag = bag_name else: recipe = Recipe(recipe_name) try: recipe = store.get(recipe) tiddler.bag = control.determine_tiddler_bag_from_recipe( recipe, tiddler).name tiddler.recipe = recipe.name except NoRecipeError, exc: raise HTTP404('unable to edit %s, recipe %s not found: %s' % (tiddler.title, recipe_name, exc)) except NoBagError, exc: raise HTTP404('unable to edit %s: %s' % (tiddler.title, exc))
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 = environ['wsgiorg.routing_args'][1]['username'] usersign = environ['tiddlyweb.usersign'] store = environ['tiddlyweb.store'] try: _ = store.get(User(username)) except NoUserError: raise HTTP404('Profile not found for %s' % username) activity_feed = profile_atom_url(environ, username) environ['tiddlyweb.links'] = [ '<link rel="alternate" type="application/atom+xml"' 'title="Atom activity feed" href="%s" />' % activity_feed ] 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))
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, exc: raise HTTP404('Unable to import challenger %s: %s' % (challenger_name, exc))
def webfinger(environ, start_response): """ Display the webfinger information for a given user. """ http_host, host_url = determine_host(environ) if http_host != host_url: raise HTTP404('No webfinger at this host: %s' % http_host) username = environ['tiddlyweb.query'].get('q', [None])[0] if not username: raise HTTP400('No account provided to webfinger query') if username.startswith('acct:'): username = username.split(':', 1)[1] username = username.split('@', 1)[0] start_response('200 OK', [('Content-Type', 'application/xrd+xml')]) return [ WEBFINGER_TEMPLATE % { 'username': username, 'host': http_host, 'server_host': server_base_url(environ) } ]
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' % web.tiddler_url( environ, tiddler, container=container, full=False) recipe = tiddler.recipe try: for revision in store.list_tiddler_revisions(tiddler): tmp_tiddler = Tiddler(title=tiddler.title, bag=tiddler.bag) tmp_tiddler.revision = revision if recipe: tmp_tiddler.recipe = recipe tiddlers.add(tmp_tiddler) except NoTiddlerError, exc: # If a tiddler is not present in the store. raise HTTP404('tiddler %s not found, %s' % (tiddler.title, exc))
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 = environ['wsgiorg.routing_args'][1]['space_name'] user_name = environ['wsgiorg.routing_args'][1]['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 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 = environ['wsgiorg.routing_args'][1]['space_name'] user_name = environ['wsgiorg.routing_args'][1]['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 safe_mode(environ, start_response): """ Serve up a space in safe mode. Safe mode means that non-required plugins are turned off and plugins that duplicate those in the core bags (system and tiddlyspace) are deleted from the store of the space in question. """ http_host, _ = determine_host(environ) space_name = determine_space(environ, http_host) recipe_name = determine_space_recipe(environ, space_name) if recipe_name != '%s_private' % space_name: raise HTTP403('membership required for safe mode') if environ['REQUEST_METHOD'] == 'GET': return _send_safe_mode(environ, start_response) store = environ['tiddlyweb.store'] # Get the list of core plugins core_plugin_tiddler_titles = _get_core_plugins(store) # Delete those plugins in the space's recipes which # duplicate the core plugins recipe = _delete_duplicates(environ, core_plugin_tiddler_titles, recipe_name, space_name) # Process the recipe. For those tiddlers which do not have a bag # in CORE_RECIPE, remove the systemConfig tag. try: candidate_tiddlers = control.get_tiddlers_from_recipe(recipe, environ) except NoBagError, exc: raise HTTP404('recipe %s lists an unknown bag: %s' % (recipe.name, exc))
def get_tiddlers(environ, start_response): """ Get the list of tiddlers produced by this recipe. """ 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, exc: raise HTTP404('recipe %s lists an unknown bag: %s' % (recipe.name, exc))
def get_user(environ, start_response): store = environ['tiddlyweb.store'] try: usersign = environ['wsgiorg.routing_args'][1]['usersign'] user = User(usersign) user = store.get(user) except (NoUserError, KeyError), exc: raise HTTP404('Unable to load user: %s, %s' % (usersign, exc))
def static(environ, start_response): pathname = environ['tiddlyweb.config'].get('static_file_dir', 'static') filename = environ['wsgiorg.routing_args'][1]['static_file'] if '../' in filename: raise HTTP404('%s inavlid' % filename) full_path = os.path.join(pathname, filename) (mime_type, encoding) = mimetypes.guess_type(full_path) if not mime_type: mime_type = DEFAULT_MIME_TYPE if not os.path.exists(full_path): raise HTTP404('%s not found' % full_path) try: static_file = file(full_path) except IOError, exc: raise HTTP404('%s not found: %s' % (full_path, exc))
def _get_bag(environ, bag_name): """ Get the named bag out of the store. """ store = environ['tiddlyweb.store'] bag = Bag(bag_name) try: bag = store.get(bag) except NoBagError, exc: raise HTTP404('%s not found, %s' % (bag.name, exc))
def change_space_member(store, space_name, add=None, remove=None, current_user=None): """ The guts of adding a member to space. """ try: space = Space(space_name) except ValueError, exc: raise HTTP404('Space %s invalid: %s' % (space_name, exc))
def host_meta(environ, start_response): """ Send the host_meta information, so webfinger info can be found. """ http_host, host_url = determine_host(environ) if http_host != host_url: # Or should it be a 302? raise HTTP404('No host-meta at this host: %s' % http_host) start_response('200 OK', [('Content-Type', 'application/xrd+xml')]) return send_template(environ, 'hostmeta.xml', {'host': http_host})
def determine_space_recipe(environ, space_name): """ Given a space name, check if the current user is a member of that named space. If so, use the private recipe. """ store = environ['tiddlyweb.store'] usersign = environ['tiddlyweb.usersign'] try: space = Space(space_name) recipe = Recipe(space.public_recipe()) recipe = store.get(recipe) except (ValueError, StoreError), exc: raise HTTP404('Space for %s does not exist: %s' % (space_name, exc))
def _get_core_plugins(store): core_plugin_tiddler_titles = [] try: for bag in [recipe_bag for recipe_bag, _ in Space.CORE_RECIPE]: bag = store.get(Bag(bag)) for tiddler in store.list_bag_tiddlers(bag): if not tiddler.store: tiddler = store.get(tiddler) if 'systemConfig' in tiddler.tags: core_plugin_tiddler_titles.append(tiddler.title) core_plugin_tiddler_titles = set(core_plugin_tiddler_titles) except NoBagError, exc: raise HTTP404('core bag not found while trying safe mode: %s' % exc)
def _send_tiddler(environ, start_response, tiddler): """ Push a single tiddler out the network in the form of the chosen serialization. """ store = environ['tiddlyweb.store'] bag = Bag(tiddler.bag) # this will raise 403 if constraint does not pass try: _check_bag_constraint(environ, bag, 'read') except NoBagError, exc: raise HTTP404('%s not found, no bag %s, %s' % (tiddler.title, tiddler.bag, exc))
def get_user(environ, start_response): """ Get information about the user named in the request. Right now this just returns the username, acknowledging the user exists. The request must be made by an existing and logged in user. """ store = environ['tiddlyweb.store'] try: usersign = environ['wsgiorg.routing_args'][1]['usersign'] user = User(usersign) user = store.get(user) except (NoUserError, KeyError), exc: raise HTTP404('Unable to load user: %s, %s' % (usersign, exc))
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 = environ['wsgiorg.routing_args'][1]['space_name'] try: space = Space(space_name) store.get(Recipe(space.public_recipe())) store.get(Recipe(space.private_recipe())) except NoRecipeError: raise HTTP404('%s does not exist' % space_name) start_response('204 No Content', []) return ['']
def friendly_uri(environ, start_response): """ Transform a not alread mapped request at the root of a space into a request for a tiddler in the public or private recipe of the current space. """ http_host, host_url = determine_host(environ) if http_host == host_url: raise HTTP404('No resource found') else: space_name = determine_space(environ, http_host) recipe_name = determine_space_recipe(environ, space_name) # tiddler_name already in uri environ['wsgiorg.routing_args'][1]['recipe_name'] = recipe_name.encode( 'UTF-8') return get_tiddler(environ, start_response)
def post(environ, start_response): usersign = environ['tiddlyweb.usersign'] text = environ['tiddlyweb.query'].get('text', [''])[0] title = environ['tiddlyweb.query'].get('title', [''])[0] bag = environ['tiddlyweb.query'].get('bag', [''])[0] store = environ['tiddlyweb.store'] tiddler = Tiddler(title, bag) tiddler.text = text tiddler.modifier = usersign['name'] bag = Bag(bag) try: bag = store.get(bag) except NoBagError, exc: raise HTTP404('tiddler %s not found: %s' % (tiddler.title, exc))
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 host_meta(environ, start_response): """ Send the host_meta information, so webfinger info can be found. """ http_host, host_url = determine_host(environ) if http_host != host_url: # Or should it be a 302? raise HTTP404('No host-meta at this host: %s' % http_host) start_response('200 OK', [('Content-Type', 'application/xrd+xml')]) return [ HOST_META_TEMPLATE % { 'host': http_host, 'server_host': server_base_url(environ) } ]
def _determine_recipe(environ): """ Interpret URL information to determine the target recipe and then get it from the store. """ recipe_name = environ['wsgiorg.routing_args'][1]['recipe_name'] recipe_name = urllib.unquote(recipe_name) recipe_name = unicode(recipe_name, 'utf-8') 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 static(environ, start_response): pathname = environ['tiddlyweb.config']['static_dir'] filename = environ['wsgiorg.routing_args'][1]['static_file'] full_path = os.path.join(pathname, filename) (mime_type, encoding) = mimetypes.guess_type(full_path) if not mime_type: mime_type = DEFAULT_MIME_TYPE if not os.path.exists(full_path): raise HTTP404('%s not found' % full_path) static_file = file(full_path) start_response('200 OK', [('Content-Type', mime_type)]) return static_file
def _delete_duplicates(environ, titles, recipe_name, space_name): store = environ['tiddlyweb.store'] try: recipe = store.get(Recipe(recipe_name)) template = control.recipe_template(environ) recipe_list = recipe.get_recipe(template) space_bags = [ bag for bag, _ in recipe_list if bag.startswith('%s_' % space_name) ] for title in titles: for bag in space_bags: try: tiddler = Tiddler(title, bag) tiddler = store.get(tiddler) except NoTiddlerError: continue store.delete(tiddler) except NoRecipeError, exc: raise HTTP404('space recipe not found while trying safe mode: %s' % exc)
def _delete_tiddler(environ, start_response, tiddler): """ The guts of deleting a tiddler from the store. """ store = environ['tiddlyweb.store'] try: tiddler = store.get(tiddler) except NoTiddlerError: tiddler.revision = 1 _validate_tiddler_headers(environ, tiddler) bag = Bag(tiddler.bag) # this will raise 403 if constraint does not pass _check_bag_constraint(environ, bag, 'delete') try: store.delete(tiddler) except NoTiddlerError, exc: raise HTTP404('%s not found, %s' % (tiddler.title, exc))
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] tiddlers = Tiddlers(title=title, store=store) tiddlers.is_revisions = True recipe = tiddler.recipe try: for revision in store.list_tiddler_revisions(tiddler): tmp_tiddler = Tiddler(title=tiddler.title, bag=tiddler.bag) tmp_tiddler.revision = revision if recipe: tmp_tiddler.recipe = recipe tiddlers.add(tmp_tiddler) except NoTiddlerError, exc: # If a tiddler is not present in the store. raise HTTP404('tiddler %s not found, %s' % (tiddler.title, exc))
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 = environ['wsgiorg.routing_args'][1]['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)