def list_entries_query(db, req): """ Reusable function """ query = {} if "author" in req: # FIXME debug r = dbref("users", req["author"], database=app.config["DB_SYSTEM_DATABASE"]) query["revdoc.author"] = r if "class" in req: if req["class"] == "page": query["class"] = "page" elif req["class"] == "notpage": query["class"] = {"$ne": "page"} elif req["class"] == "text": query["class"] = "text" elif req["class"] == "markdown": query["class"] = "markdown" elif req["class"] == "figure": query["class"] = "figure" if "archived" in req: # three values: yes, no, all if req["archived"] == "all": pass elif req["archived"] == "yes": query["revdoc.archived"] = True elif req["archived"] == "no": query["revdoc.archived"] = False else: query["revdoc.archived"] = False limit = 100 if "limit" in req: limit = int(req["limit"]) tgt_fields = {"class": 1, "head": 1, "revdoc.title": 1, "revdoc.author": 1, "revdoc.date": 1, "revdoc.tags": 1} if query == {}: results = db.entries.find(fields=tgt_fields).sort("revdoc.date", -1).limit(limit) else: results = db.entries.find(query, fields=tgt_fields).sort("revdoc.date", -1).limit(limit) results_data = [] for r in results: # fixme add date rd = { "_id": str(r["_id"]), "class": r["class"], "head": str(r["head"].id), "title": str(r["revdoc"]["title"]), "author": str(r["revdoc"]["author"].id), "date": str(r["revdoc"]["date"].isoformat()), "tags": r["revdoc"]["tags"], } results_data.append(rd) return results_data
def api_rev_get(notebook, revid): """ get rev """ db = g.dbconn[get_nb_dbname(notebook)] rev = db.dereference(dbref('revisions', revid)) return jsonify(dm.rev_to_json[rev['class']](rev))
def home(): notebooks = g.sysdb.notebooks.find({'users': dbref('users', session['user_id']), 'archived' : { "$ne" : True}}) nblist = [] for n in notebooks: nblist.append({'name' : n['name'], 'title' : n['title']}) return render_template("home.html", nblist = nblist, notebook = None, session = session, version = app.config['VERSION_GIT_DESCRIBE'])
def api_notebookadmin_list(): """ List all the notebooks available to this user """ if request.mimetype != "application/json": return "Invalid request type, must be application/json", HTTP_ERROR_CLIENT_BADREQUEST notebooks = g.sysdb.notebooks.find({'users' : dbref('users', session['user_id']), 'archived' : { "$ne" : True}}) nblist = [] for n in notebooks: nblist.append({'name' : n['name'], 'title' : n['title']}) return jsonify({"notebooks" : nblist})
def lookup_user(userid): """ FIXME: cache this eventually Take in a userid and return doc """ if isinstance(userid, bson.dbref.DBRef): ref = userid else: ref = dbref("users", userid) doc = g.sysdb.dereference(ref) return { "_id": str(ref.id), "username": doc["username"], "name": doc.get("name", ""), "email": doc.get("email", ""), "avatar": doc.get("avatar", ""), "twitter": doc.get("twitter", ""), }
def page_new(notebook): """ """ nbdb = g.dbconn[get_nb_dbname(notebook)] title = "New Page" entries = [] page_rev = dm.page_entry_revision_create(title, entries) author = session["user_id"] page_rev.update(dm.revision_create(author, app.config['DB_SYSTEM_DATABASE'])) revid = nbdb.revisions.insert(page_rev, safe=True) page_rev["_id"] = revid ent_dict = dm.entry_create(dbref("revisions", revid), 'page', page_rev) entid = nbdb.entries.insert(ent_dict, safe=True) return redirect("/notebook/%s/page/%s" % (notebook, entid))
def settings(): if request.method == "GET": userref = dbref("users", session["user_id"]) user = g.sysdb.dereference(userref) return render_template("usersettings.html", user=user, session = session, version = app.config['VERSION_GIT_DESCRIBE']) elif request.method == "POST": # FIXME add password changing userref = dbref("users", session["user_id"]) user = g.sysdb.dereference(userref) if request.form['form'] == 'password' : pw1 = request.form['password'] pw2 = request.form['password2'] if pw1 != pw2 : return render_template("usersettings.html", user=user, session = session, action='password', success = False, version = app.config['VERSION_GIT_DESCRIBE']) else: saltpw = saltpassword(pw1, app.config['PASSWORDSALT']) user['password'] = saltpw user['apikey'] = random_api_key() g.sysdb.users.update({'_id' : user['_id'], }, user) return render_template("usersettings.html", user=user, session = session, action='password', success = True, version = app.config['VERSION_GIT_DESCRIBE']) elif request.form['form'] == 'settings' : user['name'] = request.form['name'] user['email'] = request.form['email'] user['twitter'] = request.form['twitter'] g.sysdb.users.update({'_id' : user['_id'], }, user) session['name'] = user['name'] return render_template("usersettings.html", user=user, session = session, action='settings', success = True, version = app.config['VERSION_GIT_DESCRIBE']) else: print "UNKNOWN FORM" # FIXME throw real error #user['name'] = request.form('username') else: raise "Invalid method"
def api_entry_new(notebook): """ Create a new revision and entry. You can POST a new json object to this URL and get back the JSON-ified page, it's entry, the fully-spec'd rev. Note the input json must contain class and then any of the requirements for this class, for example: all: archived: tags: text: title body page: title entries : [ {entry:, hidden:, rev:},] figure: title caption images returns : {'entry' : standard entry doc, 'revision' : standard revision doc } or 400 if an invalid request """ if request.mimetype != "application/json": return "Invalid request type, must be application/json", HTTP_ERROR_CLIENT_BADREQUEST request_data = request.json if 'class' not in request_data: return "'class' not present in request", HTTP_ERROR_CLIENT_BADREQUEST dclass = request_data['class'] if dclass not in dm.json_to_rev: return "Unknown class", HTTP_ERROR_CLIENT_BADREQUEST rev = dm.json_to_rev[dclass](request_data) archived = request_data.get("archived", False) tags = request_data.get("tags", []) rev.update(dm.revision_create(session["user_id"], app.config['DB_SYSTEM_DATABASE'], archived=archived, tags = tags)) nbdb = g.dbconn[get_nb_dbname(notebook)] revid = nbdb.revisions.insert(rev, safe=True) rev["_id"] = revid ent_dict = dm.entry_create(dbref("revisions", revid), dclass, rev) entid = nbdb.entries.insert(ent_dict, safe=True) ent_dict["_id"] = entid # tag cleanup [tagutils.inc_tag(nbdb, t) for t in tags] rev_json = dm.rev_to_json[dclass](rev) return jsonify({'entry' : {'class' : dclass, 'head' : str(revid), '_id' : str(entid)}, 'revision' : rev_json})
def api_notebookadmin_config(notebook): """ Configure notebook settings, updates the indicated fields # fields : title: admin users archived Always returns full doc """ if request.mimetype != "application/json": return "Invalid request type, must be application/json", HTTP_ERROR_CLIENT_BADREQUEST def denorm_users(ul): users = {} for u in ul: ud = g.sysdb.dereference(u) users[str(ud["_id"])] = lookup_user(ud['_id']) return users; if request.method == 'POST': # check if user is on admin list, otherwise this can't pass if not has_notebook_admin(session['user_id'], notebook): return "Don't have access", HTTP_ERROR_FORBIDDEN rd = request.json d = g.sysdb.notebooks.find({'name' : notebook}) n = d[0] raw_nb_doc = d[0] if 'title' in rd: raw_nb_doc['title'] = rd['title'] if 'archived' in rd: if rd['archived']: raw_nb_doc['archived'] = True else: raw_nb_doc['archived'] = False # check unique if 'users' in rd: if len(set(rd['users']) ) != len(rd['users']): return "Duplicate in user list, error!", HTT_ERROR_CLIENT_BADREQUEST raw_nb_doc['users'] = [] for u in rd['users'] : raw_nb_doc['users'].append(dbref('users', u)) if 'admins' in rd: if len(set(rd['admins']) ) != len(rd['admins']): return "Duplicate in user list, error!", HTT_ERROR_CLIENT_BADREQUEST raw_nb_doc['admins'] = [] for u in rd['admins'] : if dbref('users', u) not in raw_nb_doc['users']: return "Admin must also be user", HTTP_ERROR_CLIENT_BADREQUEST raw_nb_doc['admins'].append(dbref('users', u)) r = g.sysdb.notebooks.update({'_id' : raw_nb_doc['_id']}, raw_nb_doc, safe=True) d = g.sysdb.notebooks.find({'name' : notebook}) users = denorm_users(d[0]['users']) return jsonify({'notebook' : dm.notebook_to_json(d[0]), 'users' : users}); elif request.method == "GET": d = g.sysdb.notebooks.find({'name' : notebook}) # this is a hack, but whatever -- get the user info users = denorm_users(d[0]['users']) return jsonify({'notebook' : dm.notebook_to_json(d[0]), 'users' : users})
def api_entry_get_post(notebook, entryid): """ GET the latest entry and rev for an entry POST an updated rev. Note that we extract out the relevant fields from the submitted doc, and overwrite other relevant ones. Things that must be present include: class parent: Parent points to the old entry; this is also what we think the entry currently points to doc-specific entries we rewrite : author (current logged in author) date _id : with the new updated revid and update the entry If the entry is out of date, we reject, and instead return the latest entry/rev, with error 409 """ nbdb = g.dbconn[get_nb_dbname(notebook)] if request.method == 'POST': """ Perform an update, assuming that the doc is pretty well-formed """ if request.mimetype != "application/json": return "Invalid request type, must be application/json", HTTP_ERROR_CLIENT_BADREQUEST rd = request.json dclass = rd['class'] parent = rd['parent'] if dclass == 'text': rev = dm.text_entry_revision_create(rd['title'], rd['body']) elif dclass == 'figure': rev = dm.figure_entry_revision_create(rd['title'], rd['caption'], rd.get('maxsize', None), gallery = rd.get("gallery", False), images = rd['images']) elif dclass == 'markdown' : rev = dm.markdown_entry_revision_create(rd['title'], rd['body']) elif dclass == 'page': rev = dm.page_entry_revision_create(rd['title'], rd['entries']) else: raise Exception("Unknown entry class '%s'" % dclass) author = dbref("users", session["user_id"]) tags = rd.get("tags", []) pref = dbref("revisions", parent) rev.update(dm.revision_create(session["user_id"], app.config['DB_SYSTEM_DATABASE'], parent=pref, archived=rd.get('archived', False), tags = tags)) # save the revision new_rev_oid = nbdb.revisions.insert(rev, safe=True) rev["_id"] = new_rev_oid new_entry_doc = dm.entry_create(dbref('revisions', new_rev_oid), dclass, rev) res = nbdb.entries.update({'_id' : bson.objectid.ObjectId(entryid), 'head' : dbref('revisions', parent), 'class' : dclass}, new_entry_doc, safe=True) new_entry_doc["_id"] = bson.objectid.ObjectId(entryid) if res['updatedExisting'] == True: # success! # get the old revisions tags, compute the diff olddoc = nbdb.dereference(dbref('revisions', parent)) tagdeltas = tagutils.tagdelta(olddoc.get('tags', []), tags) [tagutils.inc_tag(nbdb, t) for t in tagdeltas[0]] [tagutils.dec_tag(nbdb, t) for t in tagdeltas[1]] new_rev_doc_json = dm.rev_to_json[dclass](rev) entry_doc_json = dm.entry_to_json(new_entry_doc) return jsonify({"latest_entry_doc" : entry_doc_json, "latest_revision_doc" : new_rev_doc_json}) else: # failed to update, meaning someone else updated the entry ahead of us nbdb.revisions.remove({'_id' : new_rev_oid}) entry_ref = dbref("entries", entryid) latest_entry_doc = nbdb.dereference(entry_ref) true_latest_rev_ref = latest_entry_doc['head'] latest_rev_doc = nbdb.dereference(true_latest_rev_ref) latest_rev_json = dm.page_rev_to_json(latest_rev_doc) entry_doc_json = dm.entry_to_json(latest_entry_doc) return jsonify_error({"reason" : "Incorrect latest", "latest_entry_doc": entry_doc_json, "latest_revision_doc" : latest_rev_json}, HTTP_ERROR_CLIENT_CONFLICT) elif request.method == "GET": # fixme update this to use denormalized stuff entry_ref = dbref("entries", entryid) entry_doc = nbdb.dereference(entry_ref) latest_page_ref = entry_doc['head'] latest_page_rev = nbdb.dereference(latest_page_ref) return jsonify({"entry" : dm.entry_to_json(entry_doc), "revision" : dm.rev_to_json[entry_doc['class']](latest_page_rev)})