Exemplo n.º 1
0
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
Exemplo n.º 2
0
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
Exemplo n.º 3
0
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
Exemplo n.º 4
0
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
Exemplo n.º 5
0
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