Code-Input was updated to 1.2.2 and currently works! I have the upload functionality working and have begun working on an undo button. It currently saves all the files to a dir everytime you access /templates but I haven't implemented a full undo yet.

This commit is contained in:
Norm Rasmussen
2023-03-15 17:07:34 -04:00
parent 8afdec139b
commit dae2eb440e
20 changed files with 817 additions and 96 deletions

View File

@ -1,10 +1,55 @@
from flask_wtf import FlaskForm
from wtforms.fields import SubmitField
from wtforms.fields import (SubmitField,
PasswordField,
StringField,
TextAreaField,
IntegerField,
BooleanField,
RadioField)
from wtforms.validators import InputRequired, Length
from flask_wtf.file import FileField, FileRequired
from werkzeug.utils import secure_filename
from flask_codemirror.fields import CodeMirrorField
class TemplateForm(FlaskForm):
template_code = CodeMirrorField(
language='htmlembedded',
config={'lineNumbers': 'true'})
submit = SubmitField('Submit')
class ApiKey(FlaskForm):
api_key = PasswordField('Api Key',
validators=[InputRequired(),
Length(min=20, max=25)]
)
class TemplateForm(FlaskForm):
name = StringField("Template File Name",
validators=[InputRequired()])
body = TextAreaField("Template Code",
validators=[InputRequired()])
submit = SubmitField('Upload Templates')
# template_code = CodeMirrorField( language='htmlembedded',
# config={'lineNumbers': 'true'})
class CsvForm(FlaskForm):
file = FileField(validators=[FileRequired()])
all_or_some = RadioField("All or Some",
choices=['All learners in all groups',
'Learners only in adjacent groups'],
validators=[InputRequired()])
class CourseForm(FlaskForm):
title = StringField('Title',
validators=[InputRequired(),
Length(min=10, max=100)])
description = TextAreaField('Course Description',
validators=[InputRequired(),
Length(max=200)])
price = IntegerField('Price', validators=[InputRequired()])
level = RadioField('Level',
choices=['Beginner', 'Intermediate', 'Advanced'],
validators=[InputRequired()])
available = BooleanField('Available', default='checked')

View File

@ -13,6 +13,7 @@ from flask import (
session,
make_response,
url_for,
g,
)
from werkzeug.utils import secure_filename
from app import app, forms
@ -26,6 +27,7 @@ ALLOWED_EXTENSIONS = {"csv"}
# Global Variables
url = "https://api.northpass.com/"
def download_csv():
if request.method == "GET":
download = make_response(session["dfcsv"])
@ -57,11 +59,13 @@ def allowed_file(filename):
def key_required(check):
@wraps(check)
def decorated_function(*args, **kwargs):
if session.get('key') is None:
return redirect('/',code=302)
if session.get("key") is None:
return redirect("/", code=302)
return check(*args, **kwargs)
return decorated_function
@app.route("/", methods=["GET", "POST"])
def ask_key():
"""This is the main function that asks for the API Key.
@ -71,8 +75,9 @@ def ask_key():
specials = '"!@#$%^&*()-+?_=,<>/"'
if request.method == "POST":
session["key"] = request.form.get("apikey")
if (any(char in specials for char in session["key"]) or
re.search(r"[\s]", session["key"])):
if any(char in specials for char in session["key"]) or re.search(
r"[\s]", session["key"]
):
error = "Invalid Key."
session.clear()
return render_template("index.html", title="Home", error=error)
@ -100,7 +105,7 @@ def render_home():
def clear_session():
if session.get("key"):
session.clear()
error="Session Cleared!"
error = "Session Cleared!"
return render_template("index.html", error=error, title="Home, New session")
return render_template("index.html", title="Home, New session")
@ -109,6 +114,7 @@ def clear_session():
def table():
return render_template("table.html", tables=[session["table"]], titles=["Table"])
@app.route("/upload_file", methods=["GET", "POST"])
@key_required
def upload_file():
@ -133,11 +139,12 @@ def upload_file():
return divide_values(file)
return render_template("home.html", title="Bulk Add")
def divide_values(file):
emails = []
groups = []
selection = request.form.get('learner-groups')
if request.form['submit']:
selection = request.form.get("learner-groups")
if request.form["submit"]:
if selection == "all-groups":
for item in file[1:]:
emails.append(item[0])
@ -160,13 +167,12 @@ def divide_values(file):
return api_csv_parse(emails, groups)
return emails
if request.form['preview']:
error="Preview Button Still Under Construction. Try again later."
if request.form["preview"]:
error = "Preview Button Still Under Construction. Try again later."
return render_template("bulk_add.html", error=error, title="Preview Not Yet")
return render_template(
"bulk_add.html", title="Uploaded File"
)
return render_template("bulk_add.html", title="Uploaded File")
def api_csv_parse(emails, groups):
if emails and groups:
@ -175,13 +181,15 @@ def api_csv_parse(emails, groups):
return api_add_ppl(emails)
elif groups:
return api_add_groups(groups)
return render_template("bulk_add.html", table=htmlcsv, title="CSV Submission")
return render_template("bulk_add.html", title="CSV Submission")
@app.route("/bulk_add_opts", methods=["GET", "POST"])
@key_required
def bulk_add_opts():
return render_template("bulk_add.html", titles="Bulk Add Options")
@app.route("/bulk_add", methods=["GET", "POST"])
@key_required
def bulk_add():
@ -223,8 +231,8 @@ def api_add_ppl(emails):
payload2 = []
endpoint = "v2/bulk/people"
for email in emails:
payload2.append({"email": email })
payload = {"data": {"attributes": {"people": payload2 }}}
payload2.append({"email": email})
payload = {"data": {"attributes": {"people": payload2}}}
headers = {
"accept": "application/json",
"content-type": "application/json",
@ -239,8 +247,8 @@ def api_add_groups(groups):
payload2 = []
endpoint = "v2/bulk/people"
for group in groups:
payload2.append({"groups" : group })
payload = {"data": {"attributes": {"people": payload2 }}}
payload2.append({"groups": group})
payload = {"data": {"attributes": {"people": payload2}}}
headers = {
"accept": "application/json",
"content-type": "application/json",
@ -257,9 +265,7 @@ def api_add_ppl_groups(emails, groups):
combinations = list(itertools.product(emails, groups))
for combo in combinations:
payload2.append({"email": combo[0], "groups": combo[1]})
payload = {
"data": {"attributes": {"people": payload2 }}
}
payload = {"data": {"attributes": {"people": payload2}}}
headers = {
"accept": "application/json",
@ -267,8 +273,8 @@ def api_add_ppl_groups(emails, groups):
"X-Api-Key": session["key"],
}
return payload
#response = requests.post(url + endpoint, json=payload, headers=headers)
#return check_response(response)
# response = requests.post(url + endpoint, json=payload, headers=headers)
# return check_response(response)
def check_response(response):
@ -286,11 +292,12 @@ def check_response(response):
error = "Something went wrong, but I'm not sure what."
return render_template("bulk_add.html", title="Shrug", error=error)
@app.route("/load_templates", methods=["GET", "POST"])
@key_required
def load_templates():
count = 0
templates= []
templates = []
while True:
count += 1
@ -300,33 +307,44 @@ def load_templates():
"content-type": "application/json",
"X-Api-Key": session["key"],
}
response = requests.get(url+endpoint, headers=headers)
response = requests.get(url + endpoint, headers=headers)
data = response.json()
nextlink = data["links"]
for response in data["data"]:
name, body = (
response["attributes"]["name"], response["attributes"]["body"])
templates.append((name,body))
last_updated = response["attributes"]["updated_at"].split("T")
last_updated = last_updated[0]
name, body, last_updated = (
response["attributes"]["name"],
response["attributes"]["body"],
last_updated,
)
templates.append((name, body, last_updated))
if "next" not in nextlink:
break
return render_template("templates.html",
title="Templates",
templates=templates,
)
save_templates_backup(templates)
return render_template(
"templates.html",
title="Templates",
templates=templates,
)
return render_template("options.html")
@app.route("/templates", methods=["GET", "POST"])
@key_required
def templates():
if request.method == "POST":
if request.form['submit-template']:
name = request.form.get('template_name')
body = request.form.get("loaded-content")
if request.form["submit-template"]:
name = request.form.get("template_name")
body = request.form.get("body")
g.last_template = name
if body == "":
error = "Ooph. Looks like you didn't load the changes before submitting."
error = (
"Ooph. Looks like you didn't load the changes before submitting."
)
return render_template("templates.html", error=error)
else:
endpoint = "v2/custom_templates"
@ -335,22 +353,21 @@ def templates():
"content-type": "application/json",
"X-Api-Key": session["key"],
}
payload = {"custom_template": {
"name":name,
"body":body
}}
response = requests.post(url+endpoint, json=payload, headers=headers)
payload = {"custom_template": {"name": name, "body": body}}
response = requests.post(url + endpoint, json=payload, headers=headers)
return check_templates(response)
return load_templates()
def check_templates(response):
print(response)
response = str(response)
if "201" in response:
error = "Success! Templates Uploaded."
return render_template("templates.html",
title="Templates Added",
error=error)
button = "Undo"
return render_template(
"templates.html", title="Templates Added", error=error, button=button
)
elif "403" in response:
error = "Uh oh. Looks like you don't have appropriate privileges."
return render_template("templates.html", error=error)
@ -362,28 +379,44 @@ def check_templates(response):
return render_template("templates.html", title="Shrug", error=error)
@app.route("/bulk_courses_to_groups", methods=["GET", "POST"])
def save_templates_backup(templates):
g.client_path = os.path.join(UPLOAD_FOLDER, session["school"])
if os.path.exists(g.client_path):
pass
else:
os.mkdir(g.client_path)
for tupe in templates:
file_name = tupe[0] + ".liquid"
file_body = tupe[1]
complete_path = os.path.join(g.client_path, file_name)
with open(complete_path, "w+") as temp:
temp.write(file_body)
temp.close
@app.route("/undo_template", methods=["POST"])
@key_required
def bulk_courses_to_groups():
pass
def undo_template():
print(g.client_path)
template_path = os.path.join(g.client_path, g.last_template)
if request.method == "POST":
if request.form["undo_templates"]:
if os.path.exists(template_path):
print(template_path)
@app.route("/bulk_invite_ppl", methods=["GET", "POST"])
@key_required
def bulk_invite_ppl():
pass
@app.route('/cmtest', methods = ['GET', 'POST'])
@app.route("/cmtest", methods=["GET", "POST"])
def cmtest():
form = TemplateForm()
if form.validate_on_submit():
text = form.template_code.data
return render_template('templates.html', form=form)
return render_template("templates.html", form=form)
app.secret_key = "@&I\x1a?\xce\x94\xbb0w\x17\xbf&Y\xa2\xc2(A\xf5\xf2\x97\xba\xeb\xfa"
#if __name__ == "__main__":
# if __name__ == "__main__":
# ask_key()

View File

@ -0,0 +1,49 @@
<a class="np-card" href="{% route course_enrollment, code: course.enrollment_code %}">
<div class="np-card-container">
{% if course.ribbon %}
<div class="np-card-ribbon">
{{ course.ribbon }}
</div>
{% endif %}
<video class='left-video' autoplay muted loop>
<source src="{{course.promo_video_embed_url}}" type="video/mp4" alt="{{ course.name }}">
</video>
<div class="np-card-content-bottom">
<h3 class="np-card-content-title np-color-white">
{{ course.name }}
</h3>
<div class='np-card-content-description'>
{{ course.full_description }}
</div>
<div class="np-card-content-progress np-button-color">
{% t shared.progress, count: course.progress %}
</div>
<div class="np-card-content-category">
{{course.categories.first.name}}
</div>
</div>
</div>
</a>
<style>
.np-card {
text-decoration: none;
color: white;
}
.np-card-content-category {
border-top: 1px solid lightgray;
padding: 10px;
}
.np-card-content-description {
display: block;
color: lightgray;
}
.np-card-content-bottom {
padding: 15px;
}
.np-button-color {
color: lightslategray;
font-size: 15px;
padding-bottom: 10px;
}
</style>

View File

@ -0,0 +1,44 @@
<div class="np-card np-no-horizontal-padding">
<div class="np-card-container">
<div class="np-learning-path np-featured-course">
<div class="np-featured-course-img-container" style="overflow: hidden">
<video class='featured-video' autoplay muted loop style="margin-left: -60px;">
<source id="featured_video" src="{{course.promo_video_embed_url}}" type="video/mp4" alt="{{ course.name }}" />
</video>
<div class="np-card-image-content-top">
</div>
</div>
<div class="np-card-text-wrapper">
<div class="np-card-content np-card-padding np-card-content-vertical">
<h3 class="np-card-content-title">
{{ course.name }}
</h3>
<div class="np-card-content-description">
{{ course.full_description }}
</div>
<div class="np-card-content-footer" style='display: flex; flex-direction: row; justify-content: space-between;'>
<a
class="np-top-button np-button-font-color np-button" href="{% route course_enrollment, code: course.enrollment_code %}"
>
{% if course.enrolled? == false %}
{% t shared.enroll %}
{% elsif course.started? == false %}
{% t shared.start, key: current_school.course_vocabulary %}
{% elsif course.completed? %}
{% t shared.course.view, key: current_school.course_vocabulary %}
{% else %}
{% t shared.continue %}
{% endif %}
</a>
<div class="np-card-image-content-bottom" style="position: unset;">
<div class="np-card-content-progress np-button-color">
{% t shared.progress, count: course.progress %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,80 @@
<footer class="np-footer">
<div class="np-footer-top">
{% if website_footer.show_navigation_links? %}
<div class="np-footer-navigation">
<ul class="np-footer-navigation-list">
{% for website_navigation in navigations.footer_navigations %}
{% if website_navigation.external? %}
<li class="np-footer-navigation-item">
<a
class="np-footer-navigation-link np-button-color"
href="{{ website_navigation.path }}"
{% if website_navigation.external? %} target="_blank" {% endif %}
>
{{ website_navigation.name }}
</a>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endif %}
{% if current_school.logo_url %}
<h2 class="np-footer-logo">
<a href="{% route home %}">
<img
alt="{{ current_school.name }}"
class="np-footer-logo-image"
src="{{ current_school.logo_url }}"
/>
</a>
</h2>
{% else %}
<div class="np-school-name np-header-font-color">
{{ current_school.name }}
</div>
{% endif %}
</div>
<div class="np-footer-bottom">
<nav class="np-footer-social-links">
{% if website_footer.show_social_media_links? %}
<ul class="np-footer-social-links-list">
{% for social_media_link in website_footer.social_media_links %}
<li class="np-footer-social-links-item">
<a
class="np-footer-social-links-link np-button-color"
href="{{ social_media_link.link }}"
target="_blank" title="{{ social_media_link.name }}"
>
<i class="np-footer-social-links-icon
np-button-color
fab fa-{{ social_media_link.name }}"
></i>
</a>
</li>
{% endfor %}
</ul>
{% endif %}
</nav>
{% if website_footer.show_customer_service_email? and
website_footer.school_customer_service_email
%}
<div class="np-footer-support">
<div class="np-footer-support-item np-footer-support-help np-fc-white np-opacity-50">
{% t .need_help %}
</div>
<div class="np-footer-support-item np-footer-support-email np-fc-white np-opacity-50">
{% t .email %}
</div>
<a
class="np-footer-support-item np-footer-support-link np-button-color"
href="mailto:{{ website_footer.school_customer_service_email }}"
>
support@menlosecurity.com
</a>
</div>
{% endif %}
</div>
</footer>

View File

@ -0,0 +1,11 @@
{% styles default %}
{% styles colors %}
{% styles custom %}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Raleway:wght@200;300;400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.css" integrity="sha512-wR4oNhLBHf7smjy0K4oqzdWumd+r5/+6QO/vDda76MW5iug4PT7v86FoEkySIJft3XA0Ae6axhIvHrqwm793Nw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.js" type="text/javascript"></script>

View File

@ -0,0 +1,183 @@
<header class="np-header np-header-color" >
<div class="np-header-content">
<div class="np-hidden-desktop np-header-mobile-menu-nav">
{% if current_person.signed_in? %}
<button
data-toggle-class="np-hidden"
class="np-header-mobile-menu-nav-button fal fa-times np-hidden np-header-font-color"
data-toggle-target=".np-header-mobile-avatar-menu,
.np-header-mobile-menu-content, .np-main, .np-footer"
></button>
<button
data-test="open-mobile-menu"
data-toggle-class="np-hidden"
class="np-header-mobile-menu-nav-button np-header-mobile-avatar-menu"
data-toggle-target=".fa-times, .np-header-mobile-menu-content, .np-main, .np-footer"
>
<img
alt="{{ current_person.name }}"
class="np-header-avatar-image"
src="{{ current_person.avatar_url }}"
/>
</button>
{% endif %}
</div>
{% if current_school.logo_url %}
<h1 class="np-header-logo">
<a href="{% route home %}">
<img
alt="{{ current_school.name }}"
class="np-header-logo-image"
src="{{ current_school.logo_url }}"
/>
</a>
</h1>
{% else %}
<a href="{% route home %}" class="np-school-name np-header-font-color">
{{ current_school.name }}
</a>
{% endif %}
<div class="np-hidden-mobile np-header-desktop-nav">
<ul class="np-header-desktop-nav-list">
{% for website_navigation in navigations.header_navigations_external %}
<li class= "np-header-desktop-nav-item">
<a
href="{{ website_navigation.path }}"
class="np-header-desktop-nav-link np-header-font-color"
target="_blank"
>
{{ website_navigation.name }}
</a>
</li>
{% endfor %}
</ul>
</div>
{% if current_person.signed_in? %}
<div class="np-hidden-mobile np-header-search np-header-search-expanded">
<form action="{% route search %}" method="get" data-test="desktop-search">
<input
aria-label="{% t .search %}"
class="np-header-search-input np-header-font-background-color"
type="text"
name="q"
placeholder="{% t .search %}"
/>
<i class="np-header-search-icon far fa-search"></i>
</form>
</div>
<div class="np-hidden-mobile np-header-avatar">
<button
class="np-header-avatar-button"
data-test="open-desktop-menu"
data-toggle-class-on-target="np-hidden"
data-toggle-target=".np-header-avatar-tooltip"
data-toggle-outside
>
<img
alt="{{ current_person.name }}"
class="np-header-avatar-image"
src="{{ current_person.avatar_url }}"
>
</button>
<div class="np-header-avatar-tooltip np-hidden" role="tooltip">
<span class="np-header-avatar-tooltip-arrow-up"></span>
<div class="np-header-avatar-tooltip-learner">
<div class="np-header-avatar-tooltip-learner-name">
{{ current_person.name }}
</div>
<div class="np-header-avatar-tooltip-learner-email">
{{ current_person.email }}
</div>
</div>
<nav class="np-header-avatar-tooltip-navigation">
<a class="np-header-avatar-tooltip-navigation-link" onclick="buildURL(window.location.pathname)" style="cursor:pointer">
Default
</a>
{% unless current_school.sso_active? %}
<a
class="np-header-avatar-tooltip-navigation-link"
href="{% route account %}"
>
{% t .profile_settings %}
</a>
{% endunless %}
<a
class="np-header-avatar-tooltip-navigation-link np-danger"
href="{% route logout %}"
>
{% t .sign_out %}
</a>
</nav>
</div>
</div>
{% else %}
<a
class="np-header-sign-in np-header-desktop-nav-link np-header-font-color"
href="{% route login %}"
>
{% t shared.sign_in %}
</div>
{% endif %}
</div>
</header>
<div class="np-hidden-desktop">
<div class="np-header-mobile-menu-content np-hidden">
{% if current_person.signed_in? %}
<img
alt="{{ current_person.name }}"
class="np-header-mobile-menu-content-avatar"
src="{{ current_person.avatar_url }}"
/>
<div class="np-header-mobile-menu-content-name">
{{ current_person.name }}
</div>
{% endif %}
<div class="np-header-mobile-menu-content-nav">
<form
class="np-header-search"
data-test="mobile-search"
method="get"
action="{% route search %}"
>
<input
aria-label="{% t .search %}"
class="np-header-search-input"
type="text"
name="q"
placeholder="{% t .search %}"
/>
<i class="np-header-search-icon far fa-search"></i>
</form>
{% for website_navigation in navigations.header_navigations %}
<a
href="{{ website_navigation.path }}"
class="np-header-mobile-menu-content-button"
{% if website_navigation.external? %} target="_blank" {% endif %}
>
{{ website_navigation.name }}
</a>
{% endfor %}
<div class="np-header-mobile-menu-content-line"></div>
{% unless current_school.sso_active? %}
<a
class="np-header-mobile-menu-content-button"
href="{% route account %}"
>
{% t .profile_settings %}
</a>
{% endunless %}
<a
class="np-header-mobile-menu-content-button np-danger"
href="{% route logout %}"
>
{% t .sign_out %}
</a>
</div>
</div>
</div>
{% include "messages" %}

View File

@ -0,0 +1,109 @@
<div class="np-homepage-featured np-faq np-max-width">
<div class="np-homepage-featured-text">
<div class="np-homepage-headline">
Frequently Asked Questions
</div>
</div>
<div class="row np-faqs">
<div class="col-md-6">
<div class="np-accordion">
<div class="accordion-btn">
<i class="fal fa-plus"></i>
<div class="accordion-title"><strong>Q:</strong> I'm not in shape. Can I still go to a climbing gym?</div>
</div>
<div class="accordion-panel">
<div class="accordion-panel-content">
<p><strong>A:</strong> Absolutely. Climbing is a wonderful full-body and full-mind workout. If you're looking to get in shapre but tired of pumping iron, climbing is a fantastic option.</p>
</div>
</div>
</div>
<div class="np-accordion">
<div class="accordion-btn">
<i class="fal fa-plus"></i>
<div class="accordion-title"><strong>Q:</strong> I'm scared of heights. Should I try climbing?</div>
</div>
<div class="accordion-panel">
<div class="accordion-panel-content">
<p><strong>A:</strong> Many climbers have a fear of heights! However, it goes away when you use your own strength and technique to get to the top of a wall. Start small by climbing in a gym first.</p>
</div>
</div>
</div>
<div class="np-accordion">
<div class="accordion-btn">
<i class="fal fa-plus"></i>
<div class="accordion-title"><strong>Q:</strong> Should I hire someone to belay me at a gym?</div>
</div>
<div class="accordion-panel">
<div class="accordion-panel-content">
<p><strong>A:</strong> You absolutely can as a quick introduction. However, what would be even better is to grab a friend and take a belay class at your local gym.</p>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="np-accordion">
<div class="accordion-btn">
<i class="fal fa-plus"></i>
<div class="accordion-title"><strong>Q:</strong> What is bouldering?</div>
</div>
<div class="accordion-panel">
<div class="accordion-panel-content">
<p><strong>A:</strong> Great question! Bouldering is a form of climbing, done on shorter walls. You use a crash pad under your climb to cushion your fall. Be warned, bouldering is much more powerful and has a steeper learning curve.</p>
</div>
</div>
</div>
<div class="np-accordion">
<div class="accordion-btn">
<i class="fal fa-plus"></i>
<div class="accordion-title"><strong>Q:</strong> What are those people with all that gear hanging from their harness doing?</div>
</div>
<div class="accordion-panel">
<div class="accordion-panel-content">
<p><strong>A:</strong> Chances are you saw a trad climber! Short for traditional climbing, that gear serves as removable protection. When the climbing pair is done with the climb, nothing is left on the rock.</p>
</div>
</div>
</div>
<div class="np-accordion">
<div class="accordion-btn">
<i class="fal fa-plus"></i>
<div class="accordion-title"><strong>Q:</strong> None of my friends are interested in climbing. How can I find partners?</div>
</div>
<div class="accordion-panel">
<div class="accordion-panel-content">
<p><strong>A:</strong>The best way to meet people is to join a gym a start climbing! Climbers are social and you'll start meeting people. You can also use online resources like Mountain Project's Partner Finder.</p>
</div>
</div>
</div>
</div>
</div>
</div>
/
<style>
.fa-plus {
background-image: unset;
background: #0297FA;
}
.fa-minus {
background-image: unset;
background: #0297FA; }
</style>
<script>
let accordions = document.getElementsByClassName("accordion-btn");
for (let i = 0; i < accordions.length; i++) {
accordions[i].addEventListener("click", function() {
this.querySelector('.fal').classList.toggle("fa-plus");
this.querySelector('.fal').classList.toggle("fa-minus");
let panel = this.nextElementSibling;
panel.classList.toggle("panel-open");
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}
</script>

View File

@ -0,0 +1,91 @@
<div class="np-homepage-featured np-homepage-featured-items np-max-width">
<div class="np-homepage-featured-text">
<div class="np-homepage-headline">
Recommended Gear
</div>
</div>
<div class="featured-photography-carousel np-carousel np-carousel-bg-blue" id="featured-photography-carousel">
<div class="np-carousel-card">
<div>
<img src=https://5prq858zcb-flywheel.netdna-ssl.com/wp-content/uploads/2016/12/50823_4WhiteBG-1-600x625.png" alt="Featured Photography" class='customer-carousel-image'/>
<div class="slide-label">Misty Mountain Cadillav Harness</div>
</div>
</div>
<div class="np-carousel-card">
<div>
<img src="https://outdoorgearlab-mvnab3pwrvp3t0.stackpathdns.com/photos/18/97/311203_1848_XXXL.jpg" alt="Featured Photography" class='customer-carousel-image'/>
<div class="slide-label">Gri-Gri Belay Device</div>
</div>
</div>
<div class="np-carousel-card">
<div>
<img src="https://lcdn.sportiva.com/pub/media/catalog/product/8/0/800_yellow_katanalace_1_7_4.jpg" alt="Featured Photography" class='customer-carousel-image'/>
<div class="slide-label">La Sportiva Katana Lace Shoes</div>
</div>
</div>
<div class="np-carousel-card">
<div>
<img src="https://s3.amazonaws.com/static.northpass.com/demos/hsbc-old-logo.png" alt="Featured Photography" class='customer-carousel-image'/>
<div class="slide-label">Metolious Helmet</div>
</div>
</div>
<div class="np-carousel-card">
<div>
<img src="https://s3.amazonaws.com/static.northpass.com/demos/sandia-labs-logo.jpg" alt="Featured Photography" class='customer-carousel-image'/>
<div class="slide-label">Unknown</div>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function(){
$("#featured-photography-carousel").slick({
slidesToShow: 3.5,
prevArrow: '<i class="fal fa-chevron-left"></i>',
nextArrow: '<i class="fal fa-chevron-right"></i>',
infinite: false,
responsive: [
{
breakpoint: 1350,
settings: {
slidesToShow: 2.3,
}
},
{
breakpoint: 1024,
settings: {
slidesToShow: 2,
}
},
{
breakpoint: 768,
settings: {
slidesToShow: 1,
}
}
]
});
});
</script>
<style>
.featured-photography-carousel .fa-chevron-right::before {
right: -6px !important;
}
.customer-carousel-image {
width: 292px;
height: 292px;
object-fit: contain;
}
.featured-photography-carousel .slick-initialized .slick-slide {
margin: 10px 10px;
}
</style>

View File

@ -0,0 +1,39 @@
{% include "header" %}
{% include "course_version_outdated_alert", courses: courses.in_catalog %}
<div class="np-homepage-hero">
<div class="np-homepage-hero-image">
</div>
<div class="np-homepage-hero-content">
<div class="np-homepage-headline np-header-font-color">
Menlo Security Resrouces
</div>
<div class="np-homepage-subheadline np-header-font-color">
Resources to help you make the most of your Security Training
</div>
</div>
</div>
{% include "sub_navigation" %}
{% include "courses_catalog" %}
</main>
{% include "footer" %}
<style>
.np-card-content-description {
height: 100px;
}
.np-card-content-title {
height: 58px;
overflow: clip;
}
</style>
<script>
window.onload = function() {
[].slice.call(document.getElementsByClassName("np-card-content-description")).forEach(function(element) {
if(element.innerText.length > 150){
element.innerText = element.innerText.slice(0,150) + '...'
}
});
}
</script>

View File

@ -0,0 +1,33 @@
{% include 'header' %}
<main class="np-main np-homepage">
<div class="np-homepage-hero">
<h1 class="community-headline">Explore</h1>
</div>
{% include 'sub_navigation' %}
<div class='discussion-container' style="border: 18px solid rgb(0, 0, 0); overflow: hidden; margin-left: 100px; margin-top: 35px; width: 1065px;">
<iframe scrolling="yes" src="https://www.mountainproject.com/" style="border: 0px none; margin-left: 15px auto; height: 1954px; margin-top: -100px; width: 1060px;"></iframe>
</div>
</main>
{% include 'footer' %}
<style>
.discussion-frame {
min-width: 100%;
margin-left: -10px;
display: flex;
justify-content: center;
}
.discussion-container {
display: flex;
justify-content: center;
}
.community-headline {
font-size: 32px;
line-height: 48px;
font-family: "Raleway", "Helvetic", "Arial" sans-serif;
color: #ffffff;
}
</style>

13
app/templates/cmtest.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
{% include 'head.html' %}
{% include 'header.html' %}
{% include 'logo.html' %}
<p>&nbsp;</p>
{% if error %}
<h3>{{ error }}</h4>
{% endif %}
<form method="POST" action="/">
{{ form.name.label }} {{ form.name(size=20) }}
<input type="submit" value="Go">
</form>

View File

@ -17,8 +17,8 @@
<link rel="stylesheet" href="{{ url_for('static', filename="css/styles.css") }}" />
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/WebCoder49/code-input@1.2/code-input.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/WebCoder49/code-input@1.2/code-input.min.css">
<script src="https://cdn.jsdelivr.net/gh/WebCoder49/code-input@1.2.2/code-input.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/WebCoder49/code-input@1.2.2/code-input.min.css">
<link href="{{ url_for('static', filename='css/prism.css')}}" rel="stylesheet" />
</head>
<body>

View File

@ -11,7 +11,7 @@
<p>
</p>
<form action="{{ url_for("ask_key")}}" method="post">
<input id="fields" type="text" name="apikey">
<input id="fields" type="password" name="apikey">
<input id="fields" type="submit" value="Submit">
</form>
</div>

View File

@ -6,6 +6,17 @@
{% if error %}
<h3>{{ error }}</h4>
{% endif %}
{% if button %}
<br>
<form
id="undo_template"
method="post"
action="{{ url_for('undo_template')}}">
<input type="submit" name="undo_template" value="Undo Last Upload"></input>
</form>
{% endif %}
{% if templates %}
<h2> Here are the liquid templates for </h2>
<h2 style="color:#F05323"><strong> {{ session.school }} </strong></h2>
@ -18,10 +29,9 @@
id="templates"
action="{{ url_for('templates')}}"
method="post">
<h2>
{{ templates[0] }}
</h2>
<code-input
<h2> {{ templates[0] }}</h2>
<h3> Last Updated: {{ templates [2] }}</h3>
<code-input
lang="HTML"
value="{{ templates[1] }}"
id="editor"
@ -29,10 +39,6 @@
template="code-input">
{{ templates[1] }}
</code-input>
<textarea
name="loaded-content"
id="loaded-content"
style="width:100%;"></textarea>
<label for="template_name">
Create New Template Name (optional):
</label>
@ -42,12 +48,6 @@
value="{{ templates[0] }}">
</input>
<p>&nbsp;</p>
<div
name="load-template"
value="Load Changes"
onclick="copyToTextArea()"
style="cursor:pointer"
>Click here to load changes</div>
<input
type="submit"
name="submit-template"
@ -58,7 +58,7 @@
</div>
<p>&nbsp;</p>
{% endfor %}
<!--
{% if templates %}
<h3> Advanced users only: create new template </h3>
<p>&nbsp;</p>
<div class="html_code">
@ -69,12 +69,12 @@
<h2>
Enter the code below
</h2>
<textarea
id="body"
name="body"
rows="35"
cols="100">
</textarea>
<code-input
lang="HTML"
id="editor"
name="body"
template="code-input">
</code-input>
<p>&nbsp;</p>
<label for="template_name">
Template Name:
@ -86,7 +86,8 @@
<p>&nbsp;</p>
<input type="submit" name="submit_template" value="Submit Template"</input>
</form>
</div> -->
</div>
{% endif %}
<script>
codeInput.registerTemplate("code-input", codeInput.templates.hljs(hljs,
@ -96,13 +97,3 @@
<script>
codeInput.registerTemplate("code-input", codeInput.templates.prism(Prism, []));
</script>
<script>
function copyToTextArea() {
var code = document.getElementById("editor").value;
let textarea = document.getElementById("loaded-content");
textarea.textContent = code;
console.log(code);
}
</script>