Clear session now deletes all the templates. Added socketio disconnect function too. Need to remove special characters from filepath since glob can't recognize.
This commit is contained in:
Binary file not shown.
Binary file not shown.
@ -1,8 +1,13 @@
|
|||||||
import requests
|
import requests
|
||||||
|
import shutil
|
||||||
import itertools
|
import itertools
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import csv
|
import csv
|
||||||
|
import glob
|
||||||
|
import pathlib
|
||||||
|
from flask_socketio import SocketIO
|
||||||
|
from datetime import datetime, timezone
|
||||||
from .forms import TemplateForm
|
from .forms import TemplateForm
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from flask import (
|
from flask import (
|
||||||
@ -19,12 +24,15 @@ from werkzeug.utils import secure_filename
|
|||||||
from app import app, forms
|
from app import app, forms
|
||||||
|
|
||||||
# Upload folder
|
# Upload folder
|
||||||
UPLOAD_FOLDER = "/Users/normrasmussen/Documents/Projects/CSM_webapp/app/static/files"
|
UPLOAD_FOLDER = "/Users/normrasmussen/Documents/Projects/CSM_webapp/app/static/files/csv"
|
||||||
# UPLOAD_FOLDER = 'static/files'
|
TEMPLATES_FOLDER = "/Users/normrasmussen/Documents/Projects/CSM_webapp/app/static/files/templates/"
|
||||||
app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER
|
app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER
|
||||||
|
app.config["TEMPLATES_FOLDER"] = TEMPLATES_FOLDER
|
||||||
ALLOWED_EXTENSIONS = {"csv"}
|
ALLOWED_EXTENSIONS = {"csv"}
|
||||||
|
|
||||||
# Global Variables
|
# Global Variables
|
||||||
|
socketio = SocketIO(app)
|
||||||
|
specials = '"!@#$%^&[]*()-+?_=,<>/"'
|
||||||
url = "https://api.northpass.com/"
|
url = "https://api.northpass.com/"
|
||||||
|
|
||||||
|
|
||||||
@ -72,7 +80,6 @@ def ask_key():
|
|||||||
Without this key, no other functions will work.
|
Without this key, no other functions will work.
|
||||||
It also assigns the api key to the session and clears the session upon each reload.
|
It also assigns the api key to the session and clears the session upon each reload.
|
||||||
"""
|
"""
|
||||||
specials = '"!@#$%^&*()-+?_=,<>/"'
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
session["key"] = request.form.get("apikey")
|
session["key"] = request.form.get("apikey")
|
||||||
if any(char in specials for char in session["key"]) or re.search(
|
if any(char in specials for char in session["key"]) or re.search(
|
||||||
@ -104,6 +111,19 @@ def render_home():
|
|||||||
@app.route("/clear_session", methods=["GET", "POST"])
|
@app.route("/clear_session", methods=["GET", "POST"])
|
||||||
def clear_session():
|
def clear_session():
|
||||||
if session.get("key"):
|
if session.get("key"):
|
||||||
|
if session.get("client_path"):
|
||||||
|
client = session["client_path"]
|
||||||
|
print(client)
|
||||||
|
wildcard = glob.glob(client + '_*')
|
||||||
|
print(wildcard)
|
||||||
|
for directory in wildcard:
|
||||||
|
print(directory)
|
||||||
|
try:
|
||||||
|
shutil.rmtree(directory)
|
||||||
|
print(directory)
|
||||||
|
except OSError:
|
||||||
|
print(OSError)
|
||||||
|
print("Error?")
|
||||||
session.clear()
|
session.clear()
|
||||||
error = "Session Cleared!"
|
error = "Session Cleared!"
|
||||||
return render_template("index.html", error=error, title="Home, New session")
|
return render_template("index.html", error=error, title="Home, New session")
|
||||||
@ -118,7 +138,6 @@ def table():
|
|||||||
@app.route("/upload_file", methods=["GET", "POST"])
|
@app.route("/upload_file", methods=["GET", "POST"])
|
||||||
@key_required
|
@key_required
|
||||||
def upload_file():
|
def upload_file():
|
||||||
print("Uploading CSV")
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
if "file" not in request.files:
|
if "file" not in request.files:
|
||||||
flash("No file found or uploaded")
|
flash("No file found or uploaded")
|
||||||
@ -312,6 +331,8 @@ def load_templates():
|
|||||||
nextlink = data["links"]
|
nextlink = data["links"]
|
||||||
for response in data["data"]:
|
for response in data["data"]:
|
||||||
last_updated = response["attributes"]["updated_at"].split("T")
|
last_updated = response["attributes"]["updated_at"].split("T")
|
||||||
|
full_updated = response["attributes"]["updated_at"]
|
||||||
|
g.full_updated = datetime.fromisoformat(full_updated)
|
||||||
last_updated = last_updated[0]
|
last_updated = last_updated[0]
|
||||||
name, body, last_updated = (
|
name, body, last_updated = (
|
||||||
response["attributes"]["name"],
|
response["attributes"]["name"],
|
||||||
@ -340,7 +361,6 @@ def templates():
|
|||||||
if request.form["submit-template"]:
|
if request.form["submit-template"]:
|
||||||
name = request.form.get("template_name")
|
name = request.form.get("template_name")
|
||||||
body = request.form.get("body")
|
body = request.form.get("body")
|
||||||
g.last_template = name
|
|
||||||
if body == "":
|
if body == "":
|
||||||
error = (
|
error = (
|
||||||
"Ooph. Looks like you didn't load the changes before submitting."
|
"Ooph. Looks like you didn't load the changes before submitting."
|
||||||
@ -355,15 +375,18 @@ def templates():
|
|||||||
}
|
}
|
||||||
payload = {"custom_template": {"name": name, "body": body}}
|
payload = {"custom_template": {"name": name, "body": body}}
|
||||||
response = requests.post(url + endpoint, json=payload, headers=headers)
|
response = requests.post(url + endpoint, json=payload, headers=headers)
|
||||||
return check_templates(response)
|
return check_templates(response, name)
|
||||||
return load_templates()
|
return load_templates()
|
||||||
|
|
||||||
|
|
||||||
def check_templates(response):
|
def check_templates(response, name):
|
||||||
print(response)
|
|
||||||
response = str(response)
|
response = str(response)
|
||||||
if "201" in response:
|
if "201" in response:
|
||||||
error = "Success! Templates Uploaded."
|
error = (
|
||||||
|
f"Success! The {name} template was successfully uploaded for "
|
||||||
|
+ session["school"]
|
||||||
|
+ "."
|
||||||
|
)
|
||||||
button = "Undo"
|
button = "Undo"
|
||||||
return render_template(
|
return render_template(
|
||||||
"templates.html", title="Templates Added", error=error, button=button
|
"templates.html", title="Templates Added", error=error, button=button
|
||||||
@ -380,16 +403,25 @@ def check_templates(response):
|
|||||||
|
|
||||||
|
|
||||||
def save_templates_backup(templates):
|
def save_templates_backup(templates):
|
||||||
g.client_path = os.path.join(UPLOAD_FOLDER, session["school"])
|
session["client_path"] = os.path.join(TEMPLATES_FOLDER, session["school"])
|
||||||
if os.path.exists(g.client_path):
|
if any(char in specials for char in session["client_path"]):
|
||||||
|
for char in specials:
|
||||||
|
session["sanitized_path"] = session["client_path"].replace(char,"")
|
||||||
|
print(session["sanitized_path"])
|
||||||
|
else:
|
||||||
|
session["sanitized_path"] = session["client_path"]
|
||||||
|
print(session["client_path"])
|
||||||
|
today = datetime.now(timezone.utc)
|
||||||
|
if os.path.exists(session["client_path"]):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
os.mkdir(g.client_path)
|
path = session["client_path"] + "_" + str(today)
|
||||||
|
os.mkdir(path)
|
||||||
|
|
||||||
for tupe in templates:
|
for tupe in templates:
|
||||||
file_name = tupe[0] + ".liquid"
|
file_name = tupe[0] + ".liquid"
|
||||||
file_body = tupe[1]
|
file_body = tupe[1]
|
||||||
complete_path = os.path.join(g.client_path, file_name)
|
complete_path = os.path.join(path, file_name)
|
||||||
|
|
||||||
with open(complete_path, "w+") as temp:
|
with open(complete_path, "w+") as temp:
|
||||||
temp.write(file_body)
|
temp.write(file_body)
|
||||||
@ -399,12 +431,18 @@ def save_templates_backup(templates):
|
|||||||
@app.route("/undo_template", methods=["POST"])
|
@app.route("/undo_template", methods=["POST"])
|
||||||
@key_required
|
@key_required
|
||||||
def undo_template():
|
def undo_template():
|
||||||
print(g.client_path)
|
|
||||||
template_path = os.path.join(g.client_path, g.last_template)
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
if request.form["undo_templates"]:
|
if request.form["undo_templates"]:
|
||||||
if os.path.exists(template_path):
|
pass
|
||||||
print(template_path)
|
|
||||||
|
@socketio.on("disconnect")
|
||||||
|
def test_disconnect():
|
||||||
|
print("Client disconnected")
|
||||||
|
|
||||||
|
|
||||||
|
@socketio.on("connect")
|
||||||
|
def test_connect():
|
||||||
|
print("connection established")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/cmtest", methods=["GET", "POST"])
|
@app.route("/cmtest", methods=["GET", "POST"])
|
||||||
|
|||||||
@ -1,49 +0,0 @@
|
|||||||
<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>
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
<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>
|
|
||||||
@ -1,80 +0,0 @@
|
|||||||
<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>
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
{% 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>
|
|
||||||
@ -1,183 +0,0 @@
|
|||||||
<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" %}
|
|
||||||
|
|
||||||
@ -1,109 +0,0 @@
|
|||||||
<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>
|
|
||||||
@ -1,91 +0,0 @@
|
|||||||
<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>
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
{% 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>
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
{% 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>
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user