def can_edit_institution(shortname, new): if not allowed_shortname( shortname) or len(shortname) < 2 or len(shortname) > 32: flash_error( "The identifier must be 2 to 32 characters in length and can include only letters, numbers, hyphens and underscores." ) return redirect(url_for("list_institutions"), 302), None institution = db.institutions.lookup(shortname) # Check if institution exists if new != (institution is None): flash_error("Identifier %s %s" % (shortname, "already exists" if new else "does not exist")) return redirect(url_for(".index"), 302), None if not new and not current_user.is_admin: # Make sure user has permission to edit if institution["admin"].lower() != current_user.email.lower(): rec = userdb.lookup(institution["admin"], "full_name") link = rec["homepage"] if rec[ "homepage"] else "mailto:%s" % rec["email"] owner = "%s (%s)" % (rec['name'], link) flash_error( "You do not have permission to edit %s. Contact the institution admin, %s, and ask them to fix any errors." % (institution["name"], owner)) return redirect(url_for(".index"), 302), None institution = WebInstitution(shortname, data=None, editing=new) return None, institution
def can_edit_institution(shortname, new): if not current_user.phd: flash_error("You must have a PhD to create an institution") return redirect(url_for(".index"), 301), None if not allowed_shortname(shortname): flash_error( "The institution identifier must be nonempty and can only include letters, numbers, hyphens and underscores." ) return redirect(url_for(".index"), 301), None institution = db.institutions.lookup(shortname) # Check if institution exists if new != (institution is None): flash_error("Identifier %s %s" % (shortname, "already exists" if new else "does not exist")) return redirect(url_for(".index"), 301), None if not new and not current_user.is_admin(): # Make sure user has permission to edit if institution.admin != current_user.email: admin_name = db.users.lucky({'email': institution.admin}, 'full_name') owner = "<%s>" % (owner_name, institution.admin) if owner_name: owner = owner_name + " " + owner flash_error( "You do not have permssion to edit %s. Contact the institution admin, %s, and ask them to fix any errors." % (institution.name, owner)) return redirect(url_for(".index"), 301), None if institution is None: institution = WebInstitution(shortname, data=None, editing=True) return None, institution
def can_edit_seminar(shortname, new): """ INPUT: - ``shortname`` -- the identifier of the seminar - ``new`` -- a boolean, whether the seminar is supposedly newly created OUTPUT: - ``resp`` -- a response to return to the user (indicating an error) or None (editing allowed) - ``seminar`` -- a WebSeminar object, as returned by ``seminars_lookup(shortname)``, or ``None`` (if error or seminar does not exist) """ errmsgs = [] if not allowed_shortname( shortname) or len(shortname) < 3 or len(shortname) > 32: errmsgs.append( format_errmsg( "Series identifier '%s' must be 3 to 32 characters in length and can include only letters, numbers, hyphens and underscores.", shortname)) return show_input_errors(errmsgs), None seminar = seminars_lookup(shortname, include_deleted=True) # Check if seminar exists if new != (seminar is None): if seminar is not None and seminar.deleted: errmsgs.append( format_errmsg( "Identifier %s is reserved by a series that has been deleted", shortname)) else: if not seminar: flash_error("No series with identifier %s exists" % shortname) return redirect(url_for("create.index"), 302), None else: errmsgs.append( format_errmsg( "Identifier %s is already in use by another series", shortname)) return show_input_errors(errmsgs), None if seminar is not None and seminar.deleted: return redirect(url_for("create.delete_seminar", shortname=shortname), 302), None # can happen via talks, which don't check for logged in in order to support tokens if current_user.is_anonymous: flash_error( "You do not have permission to edit series %s. Please create an account and contact the seminar organizers.", shortname) return redirect(url_for("show_seminar", shortname=shortname), 302), None # Make sure user has permission to edit if not new and not seminar.user_can_edit(): flash_error( "You do not have permission to edit series %s. Please contact the seminar organizers.", shortname) return redirect(url_for("show_seminar", shortname=shortname), 302), None if seminar is None: seminar = WebSeminar(shortname, data=None, editing=True) return None, seminar
def can_edit_seminar(shortname, new): """ INPUT: - ``shortname`` -- the identifier of the seminar - ``new`` -- a boolean, whether the seminar is supposedly newly created OUTPUT: - ``resp`` -- a response to return to the user (indicating an error) or None (editing allowed) - ``seminar`` -- a WebSeminar object, as returned by ``seminars_lookup(shortname)``, or ``None`` (if error or seminar does not exist) """ if not allowed_shortname(shortname): flash_error( "The identifier must be nonempty and can include only letters, numbers, hyphens and underscores." ) return redirect(url_for(".index"), 301), None seminar = seminars_lookup(shortname) # Check if seminar exists if new != (seminar is None): flash_error("Identifier %s %s" % (shortname, "already exists" if new else "does not exist")) return redirect(url_for(".index"), 301), None if ( current_user.is_anonymous ): # can happen via talks, which don't check for logged in in order to support tokens flash_error( "You do not have permission to edit seminar %s. Please create an account and contact the seminar organizers." % shortname) return redirect(url_for("show_seminar", shortname=shortname), 301), None if not new and not current_user.is_admin: # Make sure user has permission to edit organizer_data = db.seminar_organizers.lucky({ "seminar_id": shortname, "email": current_user.email }) if organizer_data is None: owner = seminar.owner owner_name = db.users.lucky({"email": owner}, "name") if owner_name: owner = "%s (%s)" % (owner_name, owner) flash_error( "You do not have permission to edit seminar %s. Contact the seminar owner, %s, and ask them to grant you permission." % (shortname, owner)) return redirect(url_for(".index"), 301), None if seminar is None: seminar = WebSeminar(shortname, data=None, editing=True) return None, seminar
def save_series(version=0, user=None): if version != 0: raise version_error(version) try: raw_data = get_request_json() except Exception: raw_data = None if not isinstance(raw_data, dict): raise APIError({ "code": "invalid_json", "description": "request must contain a json dictionary" }) # Temporary measure while we rename shortname series_id = raw_data.pop("series_id", None) raw_data["shortname"] = series_id if series_id is None: raise APIError({ "code": "unspecified_series_id", "description": "You must specify series_id when saving a series" }) series = seminars_lookup(series_id, include_deleted=True) if series is None: # Creating new series if not allowed_shortname( series_id) or len(series_id) < 3 or len(series_id) > 32: raise APIError({ "code": "invalid_series_id", "description": "The identifier must be 3 to 32 characters in length and can include only letters, numbers, hyphens and underscores." }) if "organizers" not in raw_data: raise APIError({ "code": "organizers_required", "description": "You must specify organizers when creating new series" }) update_organizers = True series = WebSeminar(series_id, data=None, editing=True, user=user) else: # Make sure user has permission to edit if not series.user_can_edit(user): raise APIError( { "code": "unauthorized_user", "description": "You do not have permission to edit %s." % series_id }, 401) if series.deleted: raise APIError({ "code": "norevive", "description": "You cannot revive a series through the API" }) if "organizers" in raw_data: raise APIError({ "code": "organizers_prohibited", "description": "You may not specify organizers when editing series" }) update_organizers = False # Check that there aren't extraneous keys (which might be misspelled) extra_keys = [ key for key in raw_data if key not in db.seminars.search_cols + ["slots", "organizers"] ] if extra_keys: raise APIError({ "code": "extra_keys", "description": "Unrecognized keys", "errors": extra_keys }) # Time slots/weekdays and organizers are handled differently by the processing code # We want to allow them to be unspecified (in which case we fall back on the current value) # and for an API call it's also more convenient to specify them using a list if "slots" in raw_data: slots = raw_data["slots"] else: slots = [ short_weekdays[day] + " " + slot for (day, slot) in zip(series.weekdays, series.time_slots) ] if not isinstance(slots, list) or len(slots) > MAX_SLOTS or not all( isinstance(slot, str) for slot in slots): raise APIError({ "code": "processing_error", "description": "Error in processing slots", "errors": [ "slots must be a list of strings of length at most %s" % MAX_SLOTS ] }) for i, slot in enumerate(slots): try: day, time = slot.split(None, 1) day = short_weekdays.index(day) except ValueError: raise APIError({ "code": "processing_error", "description": "Error in processing slots", "errors": [ "slots must be a three letter day-of-week followed by a time range after a space" ] }) raw_data["weekday%s" % i] = str(day) raw_data["time_slot%s" % i] = time for i in range(len(slots), MAX_SLOTS): raw_data["weekday%s" % i] = raw_data["time_slot%s" % i] = "" raw_data["num_slots"] = len(slots) if update_organizers: # We require specifying the organizers of a new seminar and don't allow updates, # so we don't need to get anything from the seminar object organizers = raw_data.get("organizers", []) fixed_cols = list(db.seminar_organizers.search_cols) i = fixed_cols.index("curator") fixed_cols[i] = "organizer" if not (isinstance(organizers, list) and len(organizers) <= MAX_ORGANIZERS and all( isinstance(OD, dict) and all(key in fixed_cols for key in OD) for OD in organizers)): raise APIError({ "code": "processing_error", "description": "Error in processing organizers", "errors": [ "organizers must be a list of dictionaries (max length %s) with keys %s" % (MAX_ORGANIZERS, ", ".join(fixed_cols)) ] }) for i, OD in enumerate(organizers): for col in db.seminar_organizers.search_cols: default = True if col == "display" else "" raw_data["org_%s%s" % (col, i)] = OD.get(col, default) # We store curator in the database but ask for organizer from the API raw_data["org_curator%s" % i] = not OD.get("organizer", True) warnings = [] def warn(msg, *args): warnings.append(msg % args) new_version, errmsgs = process_save_seminar(series, raw_data, warn, format_error, format_input_error, update_organizers, incremental_update=True, user=user) if new_version is None: raise APIError({ "code": "processing_error", "description": "Error in processing input", "errors": errmsgs }) if series.new or new_version != series: # Series saved by the API are not displayed until user approves new_version.display = False new_version.by_api = True new_version.save(user) else: raise APIError({ "code": "no_changes", "description": "No changes detected" }) if series.new: new_version.save_organizers() edittype = "created" if series.new else "edited" if warnings: response = jsonify({ "code": "warning", "description": "series successfully %s, but..." % edittype, "warnings": warnings }) else: response = jsonify({ "code": "success", "description": "series successfully %s" % edittype }) return response