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
|
||||
config.py
|
||||
slides
|
||||
exports
|
||||
log
|
||||
nohup.out
|
||||
|
||||
@@ -71,6 +71,7 @@ class Deck(db.Model):
|
||||
deck : Mapped[str] = mapped_column(String(60), nullable=False)
|
||||
description : Mapped[str] = mapped_column(String(60))
|
||||
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')
|
||||
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')
|
||||
|
||||
|
||||
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.deck = deck
|
||||
self.description = description
|
||||
self.course = course
|
||||
self.index_file = index_file
|
||||
if export_file:
|
||||
self.export_file = export_file
|
||||
|
||||
def __repr__(self) -> str:
|
||||
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:
|
||||
return redirect(url_for("main.set_token"))
|
||||
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())
|
||||
|
||||
def set_cookie(token):
|
||||
@@ -308,6 +311,28 @@ def serve_deck(deck, course_folder, path='index.html'):
|
||||
|
||||
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'])
|
||||
@flask_login.login_required
|
||||
def admin():
|
||||
|
||||
@@ -130,14 +130,31 @@
|
||||
<!-- Deck Cards -->
|
||||
{% if decks %}
|
||||
<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 %}
|
||||
<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">
|
||||
<h3 class="text-lg font-semibold mb-1">{{ deck.name }}</h3>
|
||||
<p class="text-muted text-sm">{{ deck.description }}</p>
|
||||
</a>
|
||||
<div class="bg-surface border border-border rounded-xl shadow-sm transition hover:shadow-lg flex flex-col">
|
||||
|
||||
<div class="flex items-center p-5 gap-4">
|
||||
|
||||
<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 %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% else %}
|
||||
<p class="text-center text-muted">No decks unlocked yet.</p>
|
||||
|
||||
Reference in New Issue
Block a user