Example #1
0
def register():
    if request.method == "POST":
        name = request.form["name"].strip()
        email = request.form["email"]
        pw1 = request.form["password1"]
        pw2 = request.form["password2"]
        if len(name) < 2:
            flash_error("Oops, name is too short.  Please enter at least 2 characters.")
            return make_response(render_template("register.html", title="Register", email=email))            
        try:
            email = validate_email(email)['email']
        except EmailNotValidError as e:
            flash_error("""Oops, email '%s' is not allowed. %s""", email, str(e))
            return make_response(render_template("register.html", title="Register", email=email))
        if pw1 != pw2:
            flash_error("Oops, passwords do not match!")
            return make_response(render_template("register.html", title="Register", email=email))

        if len(pw1) < 8:
            flash_error("Oops, password too short.  Minimum 8 characters, please!")
            return make_response(render_template("register.html", title="Register", email=email))

        password = pw1
        if userdb.user_exists(email=email):
            flash_error("The email address '%s' is already registered!", email)
            return make_response(render_template("register.html", title="Register", email=email))

        newuser = userdb.new_user(email=email, password=password,)

        send_confirmation_email(email)
        login_user(newuser, remember=True)
        flask.flash(Markup("Hello! Congratulations, you are a new user!"))
        logger.info("new user: '******' - '%s'" % (newuser.get_id(), newuser.email))
        return redirect(url_for(".info"))
    return render_template("register.html", title="Register", email="")
Example #2
0
def register_for_talk(seminar_id, talkid):
    from flask import flash
    talk = talks_lucky({"seminar_id": seminar_id, "seminar_ctr": talkid})
    if talk is None:
        return abort(404, "Talk not found")
    # If registration isn't required just send them to the talk page
    # where the user will see an appropriate livestream link
    if talk.access_control != 4:
        return redirect(
            url_for('show_talk', seminar_id=seminar_id, talkid=talkid))
    if current_user.is_anonymous or len(current_user.name) < 2:
        return redirect(
            url_for("user.info",
                    next=url_for("register_for_talk",
                                 seminar_id=seminar_id,
                                 talkid=talkid)))
    if not current_user.email_confirmed:
        flash_error("You need to confirm your email before you can register.")
        return redirect(
            url_for('show_talk', seminar_id=seminar_id, talkid=talkid))
    if not talk.live_link:
        return abort(404, "Livestream link for talk not found")
    if talk.register_user():
        flash("You have been registered; enjoy the talk!")
    else:
        flash("Previous registration confirmed; enjoy the talk!")
    if talk.is_starting_soon():
        return redirect(talk.live_link)
    else:
        return redirect(
            url_for('show_talk', seminar_id=seminar_id, talkid=talkid))
Example #3
0
def parse_daterange(info, query, time=True):
    tz = current_user.tz
    date = info.get("daterange")
    if date:
        sub_query = {}
        if "-" not in date:
            # make it into a range
            date = date + "-" + date
        start, end = date.split("-")
        if start.strip():
            try:
                start = tz.localize(parse(start))
                sub_query["$gte"] = start if time else start.date()
            except Exception as e:
                flash_error("Could not parse start date %s.  Error: " + str(e),
                            start)
        if end.strip():
            try:
                end = tz.localize(parse(end))
                end = end + timedelta(hours=23, minutes=59, seconds=59)
                sub_query["$lte"] = end if time else end.date()
            except Exception as e:
                flash_error("Could not parse end date %s.  Error: " + str(e),
                            end)
        if sub_query:
            query["start_time" if time else "start_date"] = sub_query
Example #4
0
def parse_recent_edit(info, query):
    recent = info.get("recent", "").strip()
    if recent:
        try:
            recent = float(recent)
        except Exception as e:
            flash_error(
                "Could not parse recent edit input %s.  Error: " + str(e),
                recent)
        else:
            recent = datetime.now() - timedelta(hours=recent)
            query["edited_at"] = {"$gte": recent}
Example #5
0
def reset_password_wtoken(token):
    try:
        # the users have one hour to use previous token
        email = read_timed_token(token, "reset password", 3600)
    except Exception:
        flash_error("The link is invalid or has expired.")
        return redirect(url_for(".info"))
    if not userdb.user_exists(email):
        flash_error("The link is invalid or has expired.")
        return redirect(url_for(".info"))
    if request.method == "POST":
        pw1 = request.form["password1"]
        pw2 = request.form["password2"]
        if pw1 != pw2:
            flash_error("Oops, passwords do not match!")
            return redirect(url_for(".reset_password_wtoken", token=token))

        if len(pw1) < 8:
            flash_error("Oops, password too short.  Minimum 8 characters, please!")
            return redirect(url_for(".reset_password_wtoken", token=token))

        userdb.change_password(email, pw1)
        flask.flash(Markup("Your password has been changed.  Please login with your new password."))
        return redirect(url_for(".info"))
    return render_template("reset_password_wtoken.html", title="Reset password", token=token)
def can_edit_institution(shortname, name, 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
    # We don't allow backticks so that we can use them to delimit strings in javascript
    if "`" in name:
        flash_error("The name must not include backticks")
        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("list_institutions"), 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"])
            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
Example #7
0
def endorse_wtoken(token):
    try:
        # tokens last forever
        endorser, email = read_timed_token(token, "endorser", None)
    except Exception:
        return flask.abort(404, "The link is invalid or has expired.")
        return redirect(url_for(".info"))
    if current_user.is_creator:
        flash_error("Account already has creator privileges.")
    elif current_user.email.lower() != email.lower():
        flash_error("The link is not valid for this account.")
    else:
        current_user.endorser = int(endorser)  # must set endorser first
        current_user.creator = True  # this will update the db
    return redirect(url_for(".info"))
Example #8
0
def send_confirmation_email(email):
    token = generate_confirmation_token(email)
    confirm_url = url_for(".confirm_email", token=token, _external=True, _scheme="https")
    html = render_template("confirm_email.html", confirm_url=confirm_url)
    subject = "Please confirm your email"
    try:
        send_email(email, subject, html)
        return True
    except:
        import sys

        flash_error(
            'Unable to send email confirmation link; please contact <a href="mailto:[email protected]">[email protected]</a> directly to confirm your email.'
        )
        app.logger.error("%s unable to send email to %s due to error: %s" % (timestamp(), email, sys.exc_info()[0]))
        return False
 def save(self, data):
     data = dict(data)  # copy
     email = data.pop("email", None)
     if not email:
         raise ValueError("data must contain email")
     user = self.lookup(email)
     if not user:
         raise ValueError("user does not exist")
     if not data:
         raise ValueError("no data to save")
     if "new_email" in data:
         data["email"] = data.pop("new_email")
         try:
             # standerdize email
             data["email"] = validate_email(data["email"])["email"]
         except EmailNotValidError as e:
             flash_error("""Oops, email '%s' is not allowed. %s""",
                         data["email"], str(e))
             return False
         if self.user_exists(data["email"]):
             flash_error(
                 "There is already a user registered with email = %s",
                 data["email"])
             return False
     for key in list(data):
         if key not in self.search_cols:
             if key != "id":
                 from .app import app
                 app.logger.critical(
                     "Need to update pwdmanager code to account for schema change key=%s"
                     % key)
             data.pop(key)
     with DelayCommit(db):
         if "email" in data:
             newemail = data["email"]
             db.institutions.update({"admin": ilike_query(email)},
                                    {"admin": newemail})
             db.seminars.update({"owner": ilike_query(email)},
                                {"owner": newemail})
             db.seminar_organizers.update({"email": ilike_query(email)},
                                          {"email": newemail})
             db.talks.update({"speaker_email": ilike_query(email)},
                             {"speaker_email": newemail})
         self.update({"email": ilike_query(email)}, data, restat=False)
     return True
Example #10
0
def login(**kwargs):
    # login and validate the user …
    email = request.form["email"]
    password = request.form["password"]
    if not email or not password:
        flash_error("Oops! Wrong username or password.")
        return redirect(url_for(".info"))
    # we always remember
    remember = True  # if request.form["remember"] == "on" else False
    user = SeminarsUser(email=email)
    if user.email and user.check_password(password):
        # this is where we set current_user = user
        login_user(user, remember=remember)
        if user.name:
            flask.flash(Markup("Hello %s, your login was successful!" % user.name))
        else:
            flask.flash(Markup("Hello, your login was successful!"))
        logger.info("login: '******' - '%s'" % (user.get_id(), user.name))
        return redirect(request.form.get("next") or url_for(".info"))
    flash_error("Oops! Wrong username or password.")
    return redirect(url_for(".info"))
Example #11
0
def show_seminar(shortname):
    # We need organizers to be able to see seminars with display=False
    seminar = seminars_lucky({"shortname": shortname}, include_pending=True)
    if seminar is None:
        return abort(404, "Seminar not found")
    if not seminar.visible():
        # There may be a non-API version of the seminar that can be shown
        name = seminar.name  # for flash_error
        seminar = seminars_lucky({"shortname": shortname})
        if seminar is None or not seminar.visible():
            flash_error("You do not have permission to view %s", name)
            return redirect(url_for("seminar_series_index"), 302)
    talks = seminar.talks(projection=3)
    now = get_now()
    future = []
    past = []
    for talk in talks:
        if talk.end_time >= now:
            future.append(talk)
        else:
            past.append(talk)
    future.sort(key=lambda talk: talk.start_time)
    past.sort(key=lambda talk: talk.start_time, reverse=True)
    if current_user.email in seminar.editors(
    ) or current_user.is_subject_admin(seminar):
        section = "Manage"
    else:
        section = None
    return render_template(
        "seminar.html",
        title="View series",
        future=future,
        past=past,
        seminar=seminar,
        section=section,
        subsection="view",
        bread=None,
    )
Example #12
0
def show_talk(seminar_id, talkid):
    token = request.args.get(
        "token", "")  # save the token so user can toggle between view and edit
    talk = talks_lucky({
        "seminar_id": seminar_id,
        "seminar_ctr": talkid
    },
                       include_pending=True)
    if talk is None:
        return abort(404, "Talk not found")
    if not talk.visible():
        # There may be a non-API version of the seminar that can be shown
        talk = talks_lucky({"seminar_id": seminar_id, "seminar_ctr": talkid})
        if talk is None or not talk.visible():
            flash_error("You do not have permission to view %s/%s", seminar_id,
                        talkid)
            return redirect(url_for("seminar_series_index"))
    kwds = dict(title="View talk",
                talk=talk,
                seminar=talk.seminar,
                subsection="viewtalk",
                token=token)
    if token:
        kwds["section"] = "Manage"
        # Also want to override top menu
        from seminars.utils import top_menu

        menu = top_menu()
        menu[1] = (url_for("create.index"), "", "Manage")
        kwds["top_menu"] = menu
    elif (current_user.is_subject_admin(talk)
          or current_user.email_confirmed and
          (current_user.email in talk.seminar.editors()
           or current_user.email == talk.speaker_email)):
        kwds["section"] = "Manage"
    return render_template("talk.html", **kwds)
Example #13
0
def confirm_email(token):
    try:
        # the users have 24h to confirm their email
        email = read_timed_token(token, "confirm email", 86400)
    except Exception:
        flash_error("The confirmation link is invalid or has expired.")
    else:
        if current_user.email.lower() != email.lower():
            flash_error("The link is not valid for this account.")
        elif current_user.email_confirmed:
            flash_error("Email already confirmed.")
        else:
            current_user.email_confirmed = True
            current_user.save()
            flask.flash("Thank you for confirming your email!", "success")
    return redirect(url_for(".info"))
Example #14
0
def change_password():
    email = current_user.email
    pw_old = request.form["oldpwd"]
    if not current_user.check_password(pw_old):
        flash_error("Oops, old password is wrong!")
        return redirect(url_for(".info"))

    pw1 = request.form["password1"]
    pw2 = request.form["password2"]
    if pw1 != pw2:
        flash_error("Oops, new passwords do not match!")
        return redirect(url_for(".info"))

    if len(pw1) < 8:
        flash_error("Oops, password too short.  Minimum 8 characters, please!")
        return redirect(url_for(".info"))

    userdb.change_password(email, pw1)
    flask.flash(Markup("Your password has been changed."))
    return redirect(url_for(".info"))
Example #15
0
 def decorated_view(*args, **kwargs):
     logger.info("email confirmed access attempt by %s" % current_user.get_id())
     if not current_user.email_confirmed:
         flash_error("Oops, you haven't yet confirmed your email")
         return redirect(url_for("user.info"))
     return fn(*args, **kwargs)
Example #16
0
def _talks_index(
    query={},
    sort=None,
    subsection=None,
    past=False,
    keywords="",
    limit=None,  # this is an upper bound on desired number of talks, we might filter some extra out
    limitbuffer=1000,  # the number of extra talks that we give ourselves to try to get the limit right
    asblock=False,  # the number of talks returned is based on star time blocks
    getcounters=True,  # doesn't limit the SQL search to get the full counters
    visible_counter=0,
    fully_filtered=True,
):
    # Eventually want some kind of cutoff on which talks are included.
    search_array = TalkSearchArray(past=past)
    info = to_dict(read_search_cookie(search_array), search_array=search_array)
    info.update(request.args)
    if keywords:
        info["keywords"] = keywords
    keywords = info.get("keywords", "")
    query = dict(query)
    parse_substring(info, query, "keywords", [
        "title", "abstract", "speaker", "speaker_affiliation", "seminar_id",
        "comments", "speaker_homepage", "paper_link"
    ])
    more = {
    }  # we will be selecting talks satsifying the query and recording whether they satisfy the "more" query
    # Note that talks_parser ignores the "time" field at the moment; see below for workaround
    talks_parser(info, more)
    if topdomain() == "mathseminars.org":
        query["topics"] = {"$contains": "math"}
    query["display"] = True
    query["hidden"] = {"$or": [False, {"$exists": False}]}
    query["audience"] = {"$lte": DEFAULT_AUDIENCE}
    now = datetime.now(pytz.UTC)
    if past:
        query["end_time"] = {"$lt": now}
        query["seminar_ctr"] = {"$gt": 0}  # don't show rescheduled talks
        if sort is None:
            sort = [("start_time", -1), "seminar_id"]
    else:
        query["end_time"] = {"$gte": now}
        if sort is None:
            sort = ["start_time", "seminar_id"]

    def dosearch(limit=limit, limitbuffer=limitbuffer):
        if limit and not getcounters:
            # we fetch extra talks to account for filtering
            talks = talks_search(query,
                                 sort=sort,
                                 seminar_dict=all_seminars(),
                                 more=more,
                                 limit=limit + limitbuffer)
        else:
            talks = talks_search(query,
                                 sort=sort,
                                 seminar_dict=all_seminars(),
                                 more=more)
        # Filtering on display and hidden isn't sufficient since the seminar could be private
        talks = [talk for talk in talks if talk.searchable()]
        return talks

    def truncateasblock(talks, retry=True):
        if not talks:
            return talks
        last_time = None
        # find enough talks such that the next talk has a different starting time
        for i, t in enumerate(talks):
            if last_time is None:
                last_time = t.start_time
                continue
            if t.start_time != last_time:
                if i > limit:
                    return talks[:i - 1]
                else:
                    last_time = t.start_time
        else:
            if retry and limit and not getcounters:
                # redo the search without limits
                talks = dosearch(limit=None)
                return truncateasblock(talks, retry=False)
            return talks

    def truncate(talks):
        if asblock and limit:
            return truncateasblock(talks)
        elif limit:
            return talks[:limit]
        else:
            return talks

    talks = dosearch()
    if getcounters:
        counters = _get_counters(talks)
    else:
        counters = _get_counters([])

    if getcounters and fully_filtered:
        # the initial query was not limited as getcounters = True
        # we will first filter after figuring out the more attribute
        # and then truncate
        pass
    else:
        # we are not going to filter or the query was already limited, so we can truncate
        talks = truncate(talks)
    # While we may be able to write a query specifying inequalities on the timestamp in the user's timezone, it's not easily supported by talks_search.  So we filter afterward
    timerange = info.get("timerange", "").strip()
    if timerange:
        tz = current_user.tz
        try:
            timerange = process_user_input(timerange,
                                           col="search",
                                           typ="daytimes")
        except ValueError:
            try:
                onetime = process_user_input(timerange,
                                             col="search",
                                             typ="daytime")
            except ValueError:
                flash_error("Invalid time range input: %s", timerange)
            else:
                for talk in talks:
                    if talk.more:
                        talkstart = adapt_datetime(talk.start_time, tz)
                        t = date_and_daytime_to_time(talkstart.date(), onetime,
                                                     tz)
                        talk.more = (t == talkstart)
        else:
            for talk in talks:
                if talk.more:
                    talkstart = adapt_datetime(talk.start_time, tz)
                    talkend = adapt_datetime(talk.end_time, tz)
                    t0, t1 = date_and_daytimes_to_times(
                        talkstart.date(), timerange, tz)
                    talk.more = (t0 <= talkstart) and (talkend <= t1)

    # get last_time before potential filtering
    last_time = int(talks[-1].start_time.timestamp()) if talks else None
    if fully_filtered:  # first filter then truncate
        row_attributes, talks = _get_row_attributes(talks, visible_counter,
                                                    fully_filtered)
        if getcounters:  # we have not yet truncated the results
            if limit and len(talks) > limit:
                talks = truncate(talks)
                row_attributes = row_attributes[:len(talks)]
                last_time = int(
                    talks[-1].start_time.timestamp()) if talks else None
    else:
        row_attributes = _get_row_attributes(talks, visible_counter)

    response = make_response(
        render_template("browse_talks.html",
                        title="Browse past talks" if past else "Browse talks",
                        section="Browse",
                        info=info,
                        subsection=subsection,
                        talk_row_attributes=zip(talks, row_attributes),
                        past=past,
                        last_time=last_time,
                        extraargs=urlencode({'keywords': keywords}),
                        **counters))
    if request.cookies.get("topics", ""):
        # TODO: when we move cookie data to server with ajax calls, this will need to get updated again
        # For now we set the max_age to 30 years
        response.set_cookie("topics_dict",
                            topic_dag.port_cookie(),
                            max_age=60 * 60 * 24 * 365 * 30)
        response.set_cookie("topics", "", max_age=0)

    # disable cache
    response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
    response.headers['Pragma'] = 'no-cache'
    return response
Example #17
0
def get_endorsing_link():
    email = request.form["email"].strip()
    try:
        email = validate_email(email)["email"]
    except EmailNotValidError as e:
        flash_error("""Oops, email '%s' is not allowed. %s""", email, str(e))
        return redirect(url_for(".info"))
    rec = userdb.lookup(email, ["name", "creator", "email_confirmed"])
    if rec is None or not rec["email_confirmed"]:  # No account or email unconfirmed
        if db.preendorsed_users.count({'email':email}):
            flash_infomsg("The email address %s has already been pre-endorsed.", email)
            return redirect(url_for(".info"))
        else:
            db.preendorsed_users.insert_many([{"email": email, "endorser": current_user._uid}])
            to_send = """Hello,

    I am offering you permission to add content (e.g., create a seminar)
    on the {topdomain} website.

    To accept this invitation:

    1. Register at {register} using this email address.

    2. Click on the link the system emails you, to confirm your email address.

    3. Now any content you create will be publicly viewable.


    Best,
    {name}
    """.format(
                name = current_user.name,
                topdomain = topdomain(),
                register = url_for('.register', _external=True, _scheme='https'),
            )
            data = {
                "body": to_send,
                "subject": "An invitation to collaborate on " + topdomain(),
            }
            endorsing_link = """
    <p>
    The person {email} will be able to create content after registering and confirming the email address.</br>
    <button onClick="window.open('mailto:{email}?{msg}')">
    Send email
    </button> to let them know.
    </p>
    """.format(
                email=email, msg=urlencode(data, quote_via=quote)
            )
        flash_infomsg("""
            The person %s will be able to create content after registering and confirming the email address.  Click the "Send email" button below to let them know.""",email)
        session["endorsing link"] = endorsing_link
        return redirect(url_for(".info"))
    else:
        target_name = rec["name"]
        if rec["creator"]:
            flash_infomsg("%s is already able to create content.", target_name)
            return redirect(url_for(".info"))
        else:
            welcome = "Hello" if not target_name else ("Dear " + target_name)
            to_send = """{welcome},<br>
<p>
You have been endorsed on {topdomain} and any content you create will
be publicly viewable.
</p>
<p>
Thanks for using {topdomain}!
</p>

""".format(
                welcome = welcome,
                topdomain = topdomain()
            )
            subject = "Endorsement to create content on " + topdomain()
            send_email(email, subject, to_send)
            userdb.make_creator(email, int(current_user.id))
            flash_infomsg("%s is now able to create content.", target_name if target_name else email)
            return redirect(url_for(".info"))
    raise Exception("The function get_endorsing_link did not return a value")