def update_pages(secret):
    params = Params.query.one()
    if secret != params.git_hook_url or 'git' not in syllabus.get_config(
    )['pages']:
        return seeother("/")
    syllabus.utils.pages.init_and_sync_repo(force_sync=True)
    return "done"
Esempio n. 2
0
def _render_rst_to_jinja_templating(course, page_path, content):
    cache_pages = syllabus.get_config()["caching"]["cache_pages"]
    toc = syllabus.get_toc(course)
    print_mode = session.get("print_mode", False)
    # look if we have a cached version of this content
    if cache_pages and toc.has_cached_content(content, print_mode):
        with open(
                safe_join(syllabus.get_pages_path(course),
                          content.cached_path(print_mode)), "r") as f:
            rendered = f.read()
    else:
        # render the content
        with open(safe_join(syllabus.get_pages_path(course), page_path),
                  "r") as f:
            rendered = publish_string(f.read(),
                                      writer_name='html',
                                      settings_overrides=default_rst_opts)
        if cache_pages:  # cache the content if needed
            if type(content) is Page:
                parent = toc.get_parent_of(content)
                os.makedirs(safe_join(
                    toc.cached_path(print_mode),
                    parent.path if parent is not None else ""),
                            exist_ok=True)
            else:
                os.makedirs(safe_join(toc.cached_path(print_mode),
                                      content.path),
                            exist_ok=True)
            with open(
                    safe_join(syllabus.get_pages_path(course),
                              content.cached_path(print_mode)),
                    "w") as cached_content:
                cached_content.write(rendered)
    return rendered
Esempio n. 3
0
def get_lti_url(user_id, task_id):
    config = syllabus.get_config()
    consumer = ToolConsumer(
        consumer_key=config['inginious']['lti']['consumer_key'],
        consumer_secret=config['inginious']['lti']['consumer_secret'],
        launch_url='%s/lti/%s/%s' % (config['inginious']['url'], config['inginious']['course_id'], task_id),
        params={
            'lti_message_type': 'basic-lti-launch-request',
            'lti_version': "1.1",
            'resource_link_id': "syllabus_%s" % task_id,
            'user_id': user_id,
        }
    )

    d = consumer.generate_launch_data()
    data = parse.urlencode(d).encode()

    req = urllib_request.Request('%s/lti/%s/%s' % (config['inginious']['url'], config['inginious']['course_id'], task_id), data=data)
    resp = urllib_request.urlopen(req)

    task_url = resp.geturl()

    if not lti_url_regex.match(task_url):
        pass
        #raise Exception("INGInious returned the wrong url: %s vs %s" % (task_url, str(lti_url_regex)))
    return task_url
Esempio n. 4
0
def init_and_sync_repo(course, force_sync=False):
    """
    Initializes a git repository in the pages folder if no repository already exists, then
    synchronizes it with the remote specified in the configuration file if the
    origin didn't exist before or if force_sync is True.
    Warning: the local changes will be overwritten.
    :return:
    """
    path = os.path.join(syllabus.get_root_path(),
                        syllabus.get_pages_path(course))
    git_config = syllabus.get_config()['courses'][course]['pages']['git']
    try:
        if not os.path.exists(path):
            os.makedirs(path)
        repo = Repo(path)
    except InvalidGitRepositoryError:
        # this is currently not a git repo
        repo = Repo.init(path)
    try:
        origin = repo.remote("origin").set_url(git_config['remote'])
    except:
        origin = repo.create_remote("origin", git_config['remote'])
        # sync the repo if the origin wasn't already there
        force_sync = True
    if force_sync:
        git_force_sync(course, origin, repo)
        syllabus.get_toc(course, True)
Esempio n. 5
0
def git_force_sync(course, origin, repo):
    git_config = syllabus.get_config()['courses'][course]['pages']['git']
    private_key_path = git_config['repository_private_key']
    branch = git_config['branch']
    if private_key_path is not None:
        # We need to be compatible with git < 2.3 as CentOS7 uses an older version, so here is an ugly code
        # to use a deployment key that will work with old git versions

        # set the ssh executable
        ssh_executable_path = os.path.join(syllabus.get_root_path(),
                                           "ssh_executable.sh")
        with open(ssh_executable_path, "w") as f:
            f.write(
                '#!/bin/sh\nID_RSA=%s\nssh -o StrictHostKeyChecking=no -i $ID_RSA "$@"'
                % private_key_path)
        os.system("chmod +x %s" % ssh_executable_path)
        with repo.git.custom_environment(GIT_SSH=ssh_executable_path):
            origin.fetch()
            # complete synchronization (local changes will be overwritten)
            repo.git.reset('--hard', 'origin/%s' % branch)
        # Hereunder is a more pretty version, uncomment it when the git version is >= 2.3
        # with repo.git.custom_environment(GIT_SSH_COMMAND='ssh -i %s -o StrictHostKeyChecking=no' % private_key_path):
        #     origin.fetch()
        #     # complete synchronization (local changes will be overwritten)
        #     repo.git.reset('--hard', 'origin/%s' % branch)
    else:
        origin.fetch()
        # complete synchronization (local changes will be overwritten)
        repo.git.reset('--hard', 'origin/%s' % branch)
def render_web_page(course: str, content: Content, print_mode=False, display_print_all=False):
    try:
        TOC = syllabus.get_toc(course)
        session["print_mode"] = print_mode
        try:
            previous = TOC.get_previous_content(content)
        except KeyError:
            previous = None
        try:
            next = TOC.get_next_content(content)
        except KeyError:
            next = None

        config = syllabus.get_config()
        inginious_config = config['courses'][course]['inginious']
        inginious_course_url = "%s/%s" % (inginious_config['url'], inginious_config['course_id'])
        path = safe_join(inginious_config.get("simple_grader_pattern", "/"), inginious_config['course_id'])
        inginious_sandbox_url = urllib.parse.urljoin(inginious_config["url"], path)
        same_origin_proxy = inginious_config['same_origin_proxy']
        retval = render_template('rst_page.html' if not print_mode else 'print_page.html',
                                 logged_in=session.get("user", None),
                                 inginious_config = syllabus.get_config()['courses'][course]['inginious'],
                                 inginious_course_url=inginious_course_url if not same_origin_proxy else ("/postinginious/" + course),
                                 inginious_sandbox_url=inginious_sandbox_url,
                                 inginious_url=inginious_config['url'],
                                 containing_chapters=TOC.get_containing_chapters_of(content), this_content=content,
                                 render_rst=lambda content, **kwargs: syllabus.utils.pages.render_content(course, content, **kwargs),
                                 render_footer= lambda course: syllabus.utils.pages.render_footer(course),
                                 content_at_same_level=TOC.get_content_at_same_level(content),
                                 course_str=course,
                                 courses_titles={course: config["courses"][course]["title"] for course in syllabus.get_courses()},
                                 toc=TOC,
                                 direct_content=TOC.get_direct_content_of(content), next=next, previous=previous,
                                 display_print_all=display_print_all,
                                 get_lti_data=get_lti_data, get_lti_submission=get_lti_submission,
                                 render_rst_str=syllabus.utils.pages.render_rst_str,
                                 login_img="/static/login.png" if os.path.exists(os.path.join(app.static_folder, "login.png")) else None,
                                 auth_methods=syllabus.get_config()["authentication_methods"])

        session["print_mode"] = False
    except Exception:
        # ensure that the print mode is disabled
        session["print_mode"] = False
        raise
    return retval
Esempio n. 7
0
def toc_edition(course):
    if not course in syllabus.get_config()["courses"].keys():
        abort(404)
    toc = syllabus.get_toc(course)
    if toc.ignored and not has_feedback(session):
        set_feedback(
            session,
            Feedback(feedback_type="warning",
                     message="The following contents have not been found :\n" +
                     "<pre>" + "\n".join(toc.ignored) + "</pre>"))
    if request.method == "POST":
        inpt = request.form
        if "new_content" in inpt:
            try:
                # check YAML validity
                toc_dict = yaml.load(inpt["new_content"],
                                     OrderedDictYAMLLoader)
                if not TableOfContent.is_toc_dict_valid(
                        syllabus.get_pages_path(course), toc_dict):
                    set_feedback(
                        session,
                        Feedback(feedback_type="error",
                                 message="The submitted table of contents "
                                 "is not consistent with the files "
                                 "located in the pages directory."))
                    return seeother(request.path)
            except yaml.YAMLError:
                set_feedback(
                    session,
                    Feedback(feedback_type="error",
                             message="The submitted table of contents is not "
                             "written in valid YAML."))
                return seeother(request.path)
            # the YAML is valid, write it in the ToC
            with open(
                    os.path.join(syllabus.get_pages_path(course), "toc.yaml"),
                    "w") as f:
                f.write(inpt["new_content"])
            syllabus.get_toc(course, force=True)
        set_feedback(
            session,
            Feedback(feedback_type="success",
                     message="The table of contents has been modified "
                     "successfully !"))
        return seeother(request.path)
    else:
        with open(os.path.join(syllabus.get_pages_path(course), "toc.yaml"),
                  "r") as f:
            try:
                return render_template(
                    'edit_table_of_content.html',
                    active_element=sidebar['active_element'],
                    sidebar_elements=sidebar['elements'],
                    content=f.read(),
                    feedback=pop_feeback(session))
            except TemplateNotFound:
                abort(404)
Esempio n. 8
0
def config_edition():
    if request.method == "POST":
        inpt = request.form
        if "new_config" in inpt:
            try:
                # check YAML validity
                config = yaml.load(inpt["new_config"])
                # TODO: check that the configuration has the appropriate fields
                # update the config
                old_config = syllabus.get_config()
                syllabus.set_config(inpt["new_config"])
                # sync the git repo if it has changed
                try:
                    courses = set(list(old_config["courses"].keys()) + list(config["courses"].keys()))
                    for course in courses:
                        old_pages_config = old_config["courses"].get(course, {}).get("pages", {})
                        pages_config = config["courses"].get(course, {}).get("pages", {})
                        if ("git" not in old_pages_config and "git" in pages_config) or old_pages_config["git"] != pages_config["git"]:
                            syllabus.utils.pages.init_and_sync_repo(course, force_sync=True)
                except KeyError as e:
                    pass
                except AttributeError:
                    return seeother(request.path, Feedback(feedback_type="error", message="The git repository has "
                                                                                            "failed to synchronize. "
                                                                                            "Please check your "
                                                                                            "configuration."))
                return seeother(request.path, Feedback(feedback_type="success", message="The table of contents has been"
                                                                                        " modified successfully !"))
            except yaml.YAMLError:
                return seeother(request.path, feedback=Feedback(feedback_type="error",
                                                                message="The submitted configuration is not "
                                                                        "written in valid YAML."))
    else:
        params = Params.query.one()
        config = syllabus.get_config()
        hook_paths = [("/update_pages/{}/{}".format(params.git_hook_url, course)) if "git" in config["courses"][course]["pages"] and params.git_hook_url is not None else None for course in syllabus.get_courses()]
        try:
            with open(syllabus.get_config_path(), 'r') as f:
                return render_template('edit_configuration.html', active_element=sidebar['active_element'],
                                       sidebar_elements=sidebar['elements'], config=f.read(),
                                       hook_paths=hook_paths,
                                       feedback=pop_feeback(session))
        except TemplateNotFound:
            abort(404)
Esempio n. 9
0
def saml():
    if "saml" not in syllabus.get_config()['authentication_methods']:
        abort(404)
    req = prepare_request(request)
    req['request_uri'] = request.path  # hack to ensure to have the correct path and to avoid RelayState loops
    auth = init_saml_auth(req, saml_config)

    # if 'sso' in request.args:
    #     return
    if request.method == "GET":
        return redirect(auth.login())
    else:
        auth.process_response()
        errors = auth.get_errors()
        # Try and check if IdP is using several signature certificates
        # This is a limitation of python3-saml
        for cert in saml_config["idp"].get("additionalX509certs", []):
            if auth.get_last_error_reason(
            ) == "Signature validation failed. SAML Response rejected":
                import copy
                # Change used IdP certificate
                new_settings = copy.deepcopy(saml_config)
                new_settings["idp"]["x509cert"] = cert
                # Retry processing response
                auth = init_saml_auth(req, new_settings)
                auth.process_response()
                errors = auth.get_errors()
        if len(errors) == 0:
            attrs = auth.get_attributes()
            # session['samlNameId'] = auth.get_nameid()
            # session['samlSessionIndex'] = auth.get_session_index()

            username = attrs[saml_config['sp']['attrs']['username']][0]
            realname = attrs[saml_config['sp']['attrs']['realname']][0]
            email = attrs[saml_config['sp']['attrs']['email']][0]

            user = User.query.filter(User.email == email).first()

            if user is None:  # The user does not exist in our DB
                user = User(name=username,
                            full_name=realname,
                            email=email,
                            hash_password=None,
                            change_password_url=None)
                db_session.add(user)
                db_session.commit()

            session["user"] = user.to_dict()
            session["user"].update({"login_method": "saml"})

            self_url = OneLogin_Saml2_Utils.get_self_url(req)
            if 'RelayState' in request.form and self_url != request.form[
                    'RelayState']:
                return redirect(auth.redirect_to(request.form['RelayState']))

    return seeother("/")
Esempio n. 10
0
def get_lti_submission(user_id, task_id):
    config = syllabus.get_config()
    lti_url = get_lti_url(user_id, task_id)
    match = lti_regex_match.findall(lti_url)
    if len(match) == 1:
        cookie = match[0]
        response = json.loads(urllib_request.urlopen('%s/@%s@/lti/bestsubmission' % (config['inginious']['url'], cookie)).read().decode("utf-8"))
        if response["status"] == "success" and response["submission"] is not None:
            return response["submission"]["input"]['q1']
    return None
def post_inginious(course):
    inpt = request.form
    data = parse.urlencode(inpt).encode()
    inginious_config = syllabus.get_config()['courses'][course]['inginious']
    inginious_course_url = "%s/%s" % (inginious_config['url'], inginious_config['course_id'])
    req = urllib_request.Request(inginious_course_url, data=data)
    resp = urllib_request.urlopen(req)
    response = make_response(resp.read().decode())
    response.headers['Content-Type'] = 'text/json'
    return response
def post_inginious(course):
    inpt = request.form
    data = parse.urlencode(inpt).encode()
    inginious_config = syllabus.get_config()['courses'][course]['inginious']
    path = safe_join(inginious_config.get("simple_grader_pattern", "/"), inginious_config['course_id'])
    inginious_sandbox_url = urllib.parse.urljoin(inginious_config["url"], path)
    req = urllib_request.Request(inginious_sandbox_url, data=data)
    resp = urllib_request.urlopen(req)
    response = make_response(resp.read().decode())
    response.headers['Content-Type'] = 'text/json'
    return response
def reset_password(secret):
    user = db_session.query(User).filter(User.change_password_url == secret).first()
    if user is None:
        # TODO: log
        return seeother("/")
    if request.method == "GET":
        return render_template("reset_password.html", alert_hidden=True)
    if request.method == "POST":
        inpt = request.form
        password = inpt["password"]
        password_confirm = inpt["password_confirm"]
        if password != password_confirm:
            return render_template("reset_password.html", alert_hidden=False)
        password_hash = hash_password_func(email=user.email, password=password,
                                           global_salt=syllabus.get_config().get('password_salt', None),
                                           n_iterations=syllabus.get_config().get('password_hash_iterations', 100000))
        user.hash_password = password_hash
        user.change_password_url = None
        db_session.commit()
        return seeother("/login")
def course_index(course, print_mode=False):
    if not course in syllabus.get_config()["courses"].keys():
        abort(404)
    session["course"] = course
    try:
        TOC = syllabus.get_toc(course)
        if request.args.get("edit") is not None:
            return edit_content(course, TOC.index.path, TOC)
        print_mode = print_mode or request.args.get("print") is not None
        # only display the button to print the whole syllabus in the index
        return render_web_page(course, TOC.index, print_mode=print_mode, display_print_all=True)
    except ContentNotFoundError:
        abort(404)
Esempio n. 15
0
def render_web_page(course: str,
                    content: Content,
                    print_mode=False,
                    display_print_all=False):
    try:
        TOC = syllabus.get_toc(course)
        session["print_mode"] = print_mode
        try:
            previous = TOC.get_previous_content(content)
        except KeyError:
            previous = None
        try:
            next = TOC.get_next_content(content)
        except KeyError:
            next = None

        config = syllabus.get_config()
        inginious_config = config['courses'][course]['inginious']
        inginious_course_url = "%s/%s" % (inginious_config['url'],
                                          inginious_config['course_id'])
        same_origin_proxy = inginious_config['same_origin_proxy']
        retval = render_template(
            'rst_page.html' if not print_mode else 'print_page.html',
            logged_in=session.get("user", None),
            inginious_course_url=inginious_course_url
            if not same_origin_proxy else ("/postinginious/" + course),
            inginious_url=inginious_config['url'],
            containing_chapters=TOC.get_containing_chapters_of(content),
            this_content=content,
            render_rst=lambda content, **kwargs: syllabus.utils.pages.
            render_content(course, content, **kwargs),
            content_at_same_level=TOC.get_content_at_same_level(content),
            course_str=course,
            courses_titles={
                course: config["courses"][course]["title"]
                for course in syllabus.get_courses()
            },
            toc=TOC,
            direct_content=TOC.get_direct_content_of(content),
            next=next,
            previous=previous,
            display_print_all=display_print_all,
            get_lti_data=get_lti_data,
            get_lti_submission=get_lti_submission)

        session["print_mode"] = False
    except Exception:
        # ensure that the print mode is disabled
        session["print_mode"] = False
        raise
    return retval
def print_all_syllabus(course):
    if not course in syllabus.get_config()["courses"].keys():
        abort(404)
    session["course"] = course
    TOC = syllabus.get_toc(course)
    session["print_mode"] = True

    retval = render_template("print_multiple_contents.html", contents=TOC,
                             render_rst=lambda content, **kwargs: syllabus.utils.pages.render_content(course, content, **kwargs),
                             toc=TOC, get_lti_data=get_lti_data,
                             get_lti_submission=get_lti_submission, logged_in=session.get("user", None),
                             course_str=course,
                             render_rst_str=syllabus.utils.pages.render_rst_str)
    session["print_mode"] = False
    return retval
Esempio n. 17
0
def get_lti_data(course, user_id, task_id):
    inginious_config = syllabus.get_config()['courses'][course]['inginious']
    consumer = ToolConsumer(
        consumer_key=inginious_config['lti']['consumer_key'],
        consumer_secret=inginious_config['lti']['consumer_secret'],
        launch_url='%s/lti/%s/%s' %
        (inginious_config['url'], inginious_config['course_id'], task_id),
        params={
            'lti_message_type': 'basic-lti-launch-request',
            'lti_version': "1.1",
            'resource_link_id': "syllabus_%s" % task_id,
            'user_id': user_id,
        })

    d = consumer.generate_launch_data()
    return d, consumer.launch_url
Esempio n. 18
0
def get_lti_submission(course, user_id, task_id):
    config = syllabus.get_config()
    try:
        lti_url = get_lti_url(course, user_id, task_id)
    except HTTPError:
        return None
    match = lti_regex_match.findall(lti_url)
    if len(match) == 1:
        cookie = match[0]
        try:
            response = json.loads(urllib_request.urlopen('%s/@%s@/lti/bestsubmission' % (config['courses'][course]['inginious']['url'], cookie), timeout=5).read().decode("utf-8"))
        except (JSONDecodeError, HTTPError):
            response = {"status": "error"}
        if response["status"] == "success" and response["submission"] is not None:
            return response
    return None
def get_syllabus_asset(course, asset_path: str, content_path: str = None):
    if not course in syllabus.get_config()["courses"].keys():
        abort(404)
    session["course"] = course
    TOC = syllabus.get_toc(course)
    if content_path is None:
        return send_from_directory(TOC.get_global_asset_directory(), asset_path)
    if content_path[-1] == "/":
        content_path = content_path[:-1]
    try:
        # explicitly check that the chapter exists
        chapter = TOC.get_chapter_from_path(content_path)
        # secured way to serve a static file
        # TODO: use X-Sendfile with this method to be efficient
        return send_from_directory(TOC.get_asset_directory(chapter), asset_path)
    except ContentNotFoundError:
        abort(404)
def activate_account():
    email = request.args.get('email')
    mac = request.args.get('token')
    try:
        ts = int(request.args.get('ts'))
    except TypeError:
        abort(404)
        return None
    email_activation_config = syllabus.get_config()["authentication_methods"]["local"].get("email_activation", {})
    if not verify_activation_mac(email=email, secret=email_activation_config["secret"],
                                 mac_to_verify=mac, timestamp=ts):
        # the mac does not match the provided email
        abort(404)
        return None

    if User.query.filter(User.email == email).count() != 0:
        set_feedback(session, ErrorFeedback("This user is already activated."), feedback_type="login")
        return seeother("/login")

    if request.method == "GET":
        return render_template("local_register_confirmed_account.html",
                               feedback=pop_feeback(session, feedback_type="login"))

    if request.method == "POST":
        inpt = request.form
        try:
            u = handle_user_registration_infos(inpt, email, False)
        except UnicodeEncodeError:
            # TODO: log
            return seeother("/login")

        if u is None:
            return seeother("/activate?{}".format(urllib.parse.urlencode({"email": email, "token": mac, "ts": ts})))

        try:
            locally_register_new_user(u, activated=True)
            feedback_message = "You have been successfully registered."
            set_feedback(session, SuccessFeedback(feedback_message), feedback_type="login")
            return seeother("/login")
        except UserAlreadyExists:
            db_session.rollback()
            set_feedback(session, ErrorFeedback("Could not register: this user already exists."), feedback_type="login")
            return seeother("/register/{}/{}".format(email, mac))

    set_feedback(session, SuccessFeedback("Your account has been successfully activated"), feedback_type="login")
    return seeother("/login")
def refresh(course):
    TOC = syllabus.get_toc(course)
    data = request.form['content']
    config = syllabus.get_config()
    inginious_config = config['courses'][course]['inginious']
    inginious_course_url = "%s/%s" % (inginious_config['url'], inginious_config['course_id'])
    same_origin_proxy = inginious_config['same_origin_proxy']
    code_html = render_template_string(publish_string(data, writer_name='html', settings_overrides=default_rst_opts),
                                 logged_in=session.get("user", None),
                                 inginious_course_url=inginious_course_url if not same_origin_proxy else ("/postinginious/" + course),
                                 inginious_url=inginious_config['url'], this_content=data,
                                 render_rst=lambda content, **kwargs: syllabus.utils.pages.render_content(course, content, **kwargs),
                                 course_str=course,
                                 courses_titles={course: config["courses"][course]["title"] for course in syllabus.get_courses()},
                                 toc=TOC,
                                 get_lti_data=get_lti_data, get_lti_submission=get_lti_submission,
                                 render_rst_str=syllabus.utils.pages.render_rst_str)
    return "<div id=\"preview\" style=\"overflow-y: scroll\">"+code_html+"</div>"
def log_in():
    if request.method == "GET":
        return render_template("login.html", auth_methods=syllabus.get_config()['authentication_methods'])
    if request.method == "POST":
        inpt = request.form
        username = inpt["username"]
        password = inpt["password"]
        try:
            password_hash = hash_password(password.encode("utf-8"))
        except UnicodeEncodeError:
            # TODO: log
            return seeother("/login")

        user = User.query.filter(User.username == username).first()
        if user is None or user.hash_password != password_hash:
            abort(403)
        session['user'] = user.to_dict()
        return seeother('/')
def saml():
    if "saml" not in syllabus.get_config()['authentication_methods']:
        abort(404)
    req = prepare_request(request)
    req['request_uri'] = request.path  # hack to ensure to have the correct path and to avoid RelayState loops
    auth = init_saml_auth(req, saml_config)

    # if 'sso' in request.args:
    #     return
    if request.method == "GET":
        return redirect(auth.login())
    elif 'acs' in request.args:
        auth.process_response()
        errors = auth.get_errors()
        if len(errors) == 0:
            attrs = auth.get_attributes()
            # session['samlNameId'] = auth.get_nameid()
            # session['samlSessionIndex'] = auth.get_session_index()

            username = attrs[saml_config['sp']['attrs']['username']][0]
            realname = attrs[saml_config['sp']['attrs']['realname']][0]
            email = attrs[saml_config['sp']['attrs']['email']][0]

            user = User.query.filter(User.email == email).first()

            if user is None:  # The user does not exist in our DB
                user = User(name=username,
                            full_name=realname,
                            email=email,
                            hash_password=None,
                            change_password_url=None)
                db_session.add(user)
                db_session.commit()

            session["user"] = user.to_dict()
            session["user"].update({"login_method": "saml"})

            self_url = OneLogin_Saml2_Utils.get_self_url(req)
            if 'RelayState' in request.form and self_url != request.form[
                    'RelayState']:
                return redirect(auth.redirect_to(request.form['RelayState']))

    return seeother("/")
def render_web_page(content: Content,
                    print_mode=False,
                    display_print_all=False):
    try:
        TOC = syllabus.get_toc()
        session["print_mode"] = print_mode
        try:
            previous = TOC.get_previous_content(content)
        except KeyError:
            previous = None
        try:
            next = TOC.get_next_content(content)
        except KeyError:
            next = None

        inginious_config = syllabus.get_config()['inginious']
        inginious_course_url = "%s/%s" % (inginious_config['url'],
                                          inginious_config['course_id'])
        same_origin_proxy = inginious_config['same_origin_proxy']
        retval = render_template(
            'rst_page.html' if not print_mode else 'print_page.html',
            logged_in=session.get("user", None),
            inginious_course_url=inginious_course_url
            if not same_origin_proxy else "/postinginious",
            inginious_url=inginious_config['url'],
            containing_chapters=TOC.get_containing_chapters_of(content),
            this_content=content,
            render_rst=syllabus.utils.pages.render_content,
            content_at_same_level=TOC.get_content_at_same_level(content),
            toc=TOC,
            direct_content=TOC.get_direct_content_of(content),
            next=next,
            previous=previous,
            display_print_all=display_print_all)

        session["print_mode"] = False
    except Exception:
        # ensure that the print mode is disabled
        session["print_mode"] = False
        raise
    return retval
def get_syllabus_content(course, content_path: str, print_mode=False):
    if not course in syllabus.get_config()["courses"].keys():
        abort(404)
    session["course"] = course
    if content_path[-1] == "/":
        content_path = content_path[:-1]
    TOC = syllabus.get_toc(course)
    if request.args.get("edit") is not None:
        return edit_content(course, content_path, TOC)
    print_mode = print_mode or request.args.get("print") is not None
    try:
        try:
            # assume that it is an RST page
            return render_web_page(course, TOC.get_page_from_path("%s.rst" % content_path), print_mode=print_mode)
        except ContentNotFoundError:
            # it should be a chapter
            if request.args.get("print") == "all_content":
                # we want to print all the content of the chapter
                return get_chapter_printable_content(course, TOC.get_chapter_from_path(content_path), TOC)
            # we want to access the index of the chapter
            return render_web_page(course, TOC.get_chapter_from_path(content_path), print_mode=print_mode)
    except ContentNotFoundError:
        abort(404)
Esempio n. 26
0
def get_lti_data(course, user_id, task_id):
    course_config = syllabus.get_config()['courses'][course]
    inginious_config = course_config['inginious']
    lti_config = inginious_config['lti']
    consumer = ToolConsumer(
        consumer_key=inginious_config['lti']['consumer_key'],
        consumer_secret=inginious_config['lti']['consumer_secret'],
        launch_url='%s/lti/%s/%s' % (inginious_config['url'], inginious_config['course_id'], task_id),
        params={
            'lti_message_type': 'basic-lti-launch-request',
            'lti_version': "1.1",
            'resource_link_id': "syllabus_%s" % task_id,
            'tool_consumer_instance_name': 'Interactive syllabus (%s)' % course_config['title'],
            'tool_consumer_instance_url': lti_config.get('tool_url', None),
            'tool_consumer_instance_description': lti_config.get('tool_description', None),
            'context_id': lti_config.get('tool_context_id', None),
            'context_label': lti_config.get('tool_context_label', None),
            'context_title': lti_config.get('tool_context_title', None),
            'user_id': user_id,
        }
    )

    d = consumer.generate_launch_data()
    return d, consumer.launch_url
Esempio n. 27
0
def index(print=False):
    default_course = syllabus.get_config().get("default_course", None)
    if not default_course:
        return "No default course"
    return redirect(url_for("course_index", course=default_course))
def feedback_page():
    return render_template("feedback_page.html", auth_methods=syllabus.get_config()['authentication_methods'],
                           feedback=pop_feeback(session, feedback_type="login"))
def register():
    if "local" not in syllabus.get_config()['authentication_methods']:
        abort(404)
    timestamp = int(datetime.datetime.now(datetime.timezone.utc).timestamp())
    email_activation_config = syllabus.get_config()["authentication_methods"]["local"].get("email_activation", {})
    activation_required = email_activation_config.get("required", True)
    if request.method == "GET":
        return render_template("register.html", auth_methods=syllabus.get_config()['authentication_methods'],
                               feedback=pop_feeback(session, feedback_type="login"),
                               activation_required=activation_required)
    if request.method == "POST":
        inpt = request.form
        if activation_required:
            # send the email confirmation
            email = inpt["email"]
            auth_config = email_activation_config.get("authentication", {})
            parsed_url = urllib.parse.urlparse(urllib.parse.urljoin(request.host_url, "activate"))
            parsed_url = parsed_url._replace(query=urllib.parse.urlencode({
                "email": email,
                "token": get_activation_mac(email=email, secret=email_activation_config["secret"], timestamp=timestamp),
                "ts": timestamp
            }))
            url = urllib.parse.urlunparse(parsed_url)
            if auth_config.get("required", False):
                send_authenticated_confirmation_mail(email_activation_config["sender_email_address"], email,
                                                     url,
                                                     email_activation_config["smtp_server"],
                                                     username=auth_config["username"],
                                                     password=auth_config["password"],
                                                     smtp_port=email_activation_config["smtp_server_port"])
            else:
                send_confirmation_mail(email_activation_config["sender_email_address"], email,
                                       url,
                                       email_activation_config["smtp_server"],
                                       use_ssl=email_activation_config["use_ssl"],
                                       smtp_port=email_activation_config["smtp_server_port"])
            feedback_message = "Registration successful. Please activate your account using the activation link you received by e-mail." \
                               "Click <a href=\"/login\">here</a> to log in."
            set_feedback(session, SuccessFeedback(feedback_message), feedback_type="login")
            return seeother("/activation_needed")

        # here, the activation is not required

        try:
            u = handle_user_registration_infos(inpt, inpt["email"], False)
        except UnicodeEncodeError:
            # TODO: log
            return seeother("/login")

        if u is None:
            return seeother("/register")

        try:
            locally_register_new_user(u, not activation_required)
            feedback_message = "You have been successfully registered."
            set_feedback(session, SuccessFeedback(feedback_message), feedback_type="login")
            return seeother("/login")
        except UserAlreadyExists as e:
            set_feedback(session, ErrorFeedback("Could not register: this user already exists{}.".format(
                                                (": %s" % e.reason) if e.reason is not None else "")),
                         feedback_type="login")
            db_session.rollback()
            return seeother("/register")
Esempio n. 30
0
from syllabus.database import init_db, db_session, update_database
from syllabus.models.params import Params
from syllabus.models.user import hash_password, User
from syllabus.saml import prepare_request, init_saml_auth
from syllabus.utils.inginious_lti import get_lti_data, get_lti_submission
from syllabus.utils.pages import seeother, get_content_data, permission_admin
from syllabus.utils.toc import Content, Chapter, TableOfContent, ContentNotFoundError, Page

app = Flask(__name__,
            template_folder=os.path.join(syllabus.get_root_path(),
                                         'templates'),
            static_folder=os.path.join(syllabus.get_root_path(), 'static'))
app.register_blueprint(admin_blueprint, url_prefix='/admin')
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
session_sk = syllabus.get_config().get("sessions_secret_key", None)
if session_sk is None or session_sk == "":
    raise Exception(
        "You must give a session secret key to use the application")
app.secret_key = session_sk
directives.register_directive('inginious',
                              syllabus.utils.directives.InginiousDirective)
directives.register_directive(
    'inginious-sandbox', syllabus.utils.directives.InginiousSandboxDirective)
directives.register_directive('table-of-contents',
                              syllabus.utils.directives.ToCDirective)
directives.register_directive('author',
                              syllabus.utils.directives.AuthorDirective)
directives.register_directive('teacher',
                              syllabus.utils.directives.TeacherDirective)
directives.register_directive('framed',