added download button to slides with pdfs
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,5 +4,6 @@ node_modules
|
|||||||
.venv
|
.venv
|
||||||
config.py
|
config.py
|
||||||
slides
|
slides
|
||||||
|
exports
|
||||||
log
|
log
|
||||||
nohup.out
|
nohup.out
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ class Deck(db.Model):
|
|||||||
deck : Mapped[str] = mapped_column(String(60), nullable=False)
|
deck : Mapped[str] = mapped_column(String(60), nullable=False)
|
||||||
description : Mapped[str] = mapped_column(String(60))
|
description : Mapped[str] = mapped_column(String(60))
|
||||||
index_file: Mapped[str] = mapped_column(String(40), nullable=False)
|
index_file: Mapped[str] = mapped_column(String(40), nullable=False)
|
||||||
|
export_file: Mapped[Optional[str]] = mapped_column(String(40), nullable=True)
|
||||||
|
|
||||||
passwords : Mapped[List[Password]] = relationship("Password", secondary=deck_password, back_populates='decks')
|
passwords : Mapped[List[Password]] = relationship("Password", secondary=deck_password, back_populates='decks')
|
||||||
tokens : Mapped[List[Token]] = relationship("Token", secondary=deck_token, back_populates='decks')
|
tokens : Mapped[List[Token]] = relationship("Token", secondary=deck_token, back_populates='decks')
|
||||||
@@ -78,12 +79,14 @@ class Deck(db.Model):
|
|||||||
course: Mapped['Course'] = relationship("Course", back_populates='decks')
|
course: Mapped['Course'] = relationship("Course", back_populates='decks')
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, name: str, deck: str, course: Course, description: str = "", index_file: str = "index.html") -> None:
|
def __init__(self, name: str, deck: str, course: Course, description: str = "", index_file: str = "index.html", export_file: str | None = None) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.deck = deck
|
self.deck = deck
|
||||||
self.description = description
|
self.description = description
|
||||||
self.course = course
|
self.course = course
|
||||||
self.index_file = index_file
|
self.index_file = index_file
|
||||||
|
if export_file:
|
||||||
|
self.export_file = export_file
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"<Deck(name={self.name}, deck={self.deck}, course={self.course}, index={self.index_file})>"
|
return f"<Deck(name={self.name}, deck={self.deck}, course={self.course}, index={self.index_file})>"
|
||||||
|
|||||||
@@ -102,6 +102,9 @@ def index():
|
|||||||
if not token:
|
if not token:
|
||||||
return redirect(url_for("main.set_token"))
|
return redirect(url_for("main.set_token"))
|
||||||
else:
|
else:
|
||||||
|
for deck in get_unlocked_decks(token):
|
||||||
|
print(str(deck) + " | " + str(deck.export_file))
|
||||||
|
|
||||||
return render_template('index.html', active_banner=get_active_banner(), decks=get_unlocked_decks(token), user_name=token.name, active_course_name=token.active_course.name, courses=get_all_courses())
|
return render_template('index.html', active_banner=get_active_banner(), decks=get_unlocked_decks(token), user_name=token.name, active_course_name=token.active_course.name, courses=get_all_courses())
|
||||||
|
|
||||||
def set_cookie(token):
|
def set_cookie(token):
|
||||||
@@ -308,6 +311,28 @@ def serve_deck(deck, course_folder, path='index.html'):
|
|||||||
|
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
|
@main.route('/exports/<course_folder>/<deck>/<path>')
|
||||||
|
def serve_export(deck, course_folder, path):
|
||||||
|
course = db.session.execute(db.select(Course).where(Course.folder==course_folder)).scalar()
|
||||||
|
if not course:
|
||||||
|
abort(404)
|
||||||
|
assert isinstance(course, Course)
|
||||||
|
deck = db.session.execute(db.select(Deck).where(Deck.deck==deck and Deck.course_id == course.id)).scalar()
|
||||||
|
if not deck:
|
||||||
|
abort(404)
|
||||||
|
assert isinstance(deck, Deck)
|
||||||
|
|
||||||
|
if not is_deck_unlocked(deck.deck):
|
||||||
|
return redirect(url_for('main.index'))
|
||||||
|
exports_dir = os.path.join(current_app.config['EXPORTS_DIR'], course.folder)
|
||||||
|
deck_path = os.path.join(exports_dir, deck.deck)
|
||||||
|
requested_file = os.path.join(deck_path, path)
|
||||||
|
if os.path.isfile(requested_file):
|
||||||
|
return send_from_directory(deck_path, path)
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
@main.route('/admin', methods=['GET', 'POST'])
|
@main.route('/admin', methods=['GET', 'POST'])
|
||||||
@flask_login.login_required
|
@flask_login.login_required
|
||||||
def admin():
|
def admin():
|
||||||
|
|||||||
@@ -130,14 +130,31 @@
|
|||||||
<!-- Deck Cards -->
|
<!-- Deck Cards -->
|
||||||
{% if decks %}
|
{% if decks %}
|
||||||
<section>
|
<section>
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{% for deck in decks %}
|
{% for deck in decks %}
|
||||||
<a href="/slides/{{deck.course.folder}}/{{ deck.deck }}/{{ deck.index_file}}" class="block bg-surface border border-border rounded-xl p-5 shadow-sm hover:border-primary hover:bg-background transition">
|
<div class="bg-surface border border-border rounded-xl shadow-sm transition hover:shadow-lg flex flex-col">
|
||||||
<h3 class="text-lg font-semibold mb-1">{{ deck.name }}</h3>
|
|
||||||
<p class="text-muted text-sm">{{ deck.description }}</p>
|
<div class="flex items-center p-5 gap-4">
|
||||||
</a>
|
|
||||||
|
<a href="/slides/{{deck.course.folder}}/{{ deck.deck }}/{{ deck.index_file}}"
|
||||||
|
class="flex-grow block border-r border-border pr-4 hover:border-primary transition rounded-xl">
|
||||||
|
<h3 class="text-lg font-semibold mb-1">{{ deck.name }}</h3>
|
||||||
|
<p class="text-muted text-sm">{{ deck.description }}</p>
|
||||||
|
</a>
|
||||||
|
{% if deck.export_file %}
|
||||||
|
<a href="/exports/{{deck.course.folder}}/{{ deck.deck }}/{{ deck.export_file}}"
|
||||||
|
class="inline-flex items-center justify-center flex-shrink-0 h-10 w-10 text-on-primary hover:bg-primary/90 transition"
|
||||||
|
download title="Download Presentation">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p class="text-center text-muted">No decks unlocked yet.</p>
|
<p class="text-center text-muted">No decks unlocked yet.</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user