Skip to content

Commit ae51d7a

Browse files
authored
Merge pull request #1716 from UlrichB22/slideshow
Add SlideShow macro and view
2 parents 6dd1a78 + e2d4b6b commit ae51d7a

File tree

6 files changed

+228
-1
lines changed

6 files changed

+228
-1
lines changed

src/moin/apps/frontend/views.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,23 @@ def content_item(item_name, rev):
685685
return render_template("content.html", item_name=item.name, data_rendered=Markup(item.content._render_data()))
686686

687687

688+
@frontend.route("/+slideshow/<itemname:item_name>", defaults=dict(rev=CURRENT))
689+
def slide_item(item_name, rev):
690+
"""same as show_item, but we only show the content"""
691+
fqname = split_fqname(item_name)
692+
item_displayed.send(app, fqname=fqname)
693+
try:
694+
item = Item.create(item_name, rev_id=rev)
695+
except AccessDenied:
696+
abort(403)
697+
if isinstance(item, NonExistent):
698+
abort(404, item_name)
699+
data_rendered = Markup(item.content._render_data_slide())
700+
return render_template(
701+
"slideshow.html", item_name=item.name, full_name=fqname.fullname, data_rendered=data_rendered
702+
)
703+
704+
688705
@presenter("get")
689706
def get_item(item):
690707
return item.content.do_get()

src/moin/constants/misc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
LOCK = "lock"
7373

7474
# Valid views allowed for itemlinks
75-
VALID_ITEMLINK_VIEWS = ["+meta", "+history", "+download", "+highlight"]
75+
VALID_ITEMLINK_VIEWS = ["+meta", "+history", "+download", "+highlight", "+slideshow"]
7676

7777
# Transient attribute added/removed to/from flask session. Used when a User Settings
7878
# form creates a flash message but then redirects the page making the flash message a

src/moin/items/content.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,55 @@ def _get_data_diff_text(self, oldfile, newfile):
336336
"""
337337
return []
338338

339+
def _render_data_slide(self, preview=None):
340+
try:
341+
from moin.converters import default_registry as reg
342+
343+
doc = self.internal_representation(preview=preview)
344+
doc = self._expand_document(doc)
345+
346+
slide_pages = []
347+
before_first_header = True
348+
for elem1 in doc:
349+
single_slide = []
350+
for element in elem1:
351+
if element.tag.name == "h" and element.get(moin_page("outline-level")) in ["1", "2"]:
352+
if before_first_header:
353+
before_first_header = False # ignore everything before
354+
else:
355+
slide_pages.append(single_slide)
356+
single_slide = []
357+
single_slide.append(element)
358+
slide_pages.append(single_slide)
359+
print(f"{len(slide_pages)} slides found.")
360+
361+
flaskg.clock.start("conv_dom_html")
362+
html_conv = reg.get(type_moin_document, Type("application/x-xhtml-moin-page"))
363+
364+
slide_content = []
365+
attrib = {moin_page.class_: "moin-slides"}
366+
for slide in slide_pages:
367+
slide_content.append(moin_page.div(attrib=attrib, children=slide))
368+
369+
body = moin_page.body(children=slide_content)
370+
root = moin_page.page(children=[body])
371+
doc = html_conv(root)
372+
rendered_data = conv_serialize(doc, {html.namespace: ""})
373+
flaskg.clock.stop("conv_dom_html")
374+
375+
except Exception:
376+
# we really want to make sure that invalid data or a malfunctioning
377+
# converter does not crash the item view (otherwise a user might
378+
# not be able to fix it from the UI).
379+
error_id = uuid.uuid4()
380+
logging.exception(f"An exception happened in _render_data (error_id = {error_id} ):")
381+
rendered_data = [
382+
render_template(
383+
"crash.html", server_time=time.strftime("%Y-%m-%d %H:%M:%S %Z"), url=request.url, error_id=error_id
384+
)
385+
]
386+
return rendered_data
387+
339388
def get_templates(self, contenttype=None):
340389
"""create a list of templates (for some specific contenttype)"""
341390
terms = [

src/moin/macros/SlideShow.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright: 2024 MoinMoin:UlrichB
2+
# License: GNU GPL v2 (or any later version), see LICENSE.txt for details.
3+
4+
"""
5+
Create link to start a SlideShow for the current item
6+
"""
7+
8+
from moin.i18n import _
9+
from moin.utils.tree import moin_page, xlink
10+
from moin.macros._base import MacroInlineBase
11+
12+
13+
class Macro(MacroInlineBase):
14+
def macro(self, content, arguments, page_url, alternative):
15+
attrib = {moin_page.class_: "fa-regular fa-circle-play"}
16+
children = [moin_page.span(attrib=attrib), _(" Start SlideShow")]
17+
result = moin_page.a(attrib={xlink.href: f"/+slideshow{page_url.path}"}, children=children)
18+
return result

src/moin/static/css/projection.css

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/* projection.css - MoinMoin Slide Styles
2+
3+
Copyright (c) 2003 by Juergen Hermann
4+
Copyright (c) 2024 by MoinMoin project
5+
*/
6+
7+
html { line-height: 1.8em; }
8+
9+
body, b, em, a, span, div, p, td { font-size: 18pt; }
10+
11+
h1 { font-size: 24pt; padding-top: 24px; color: #33F; text-align: left; }
12+
h2 { font-size: 22pt; padding-top: 24px; color: #33F; }
13+
h3 { font-size: 20pt; }
14+
h4 { font-size: 18pt; }
15+
h5 { font-size: 16pt; }
16+
h6 { font-size: 14pt; }
17+
18+
tt, pre { font-size: 14pt; }
19+
sup, sub { font-size: 10pt; }
20+
21+
#moin-content-data { padding-left: 50px; margin: 0 2%; }
22+
23+
@media print {
24+
h1 { padding-top: 50px; }
25+
h2 { page-break-before: always; padding-top: 50px; }
26+
h3, h4 { page-break-after: avoid; }
27+
pre, blockquote { page-break-inside: avoid; }
28+
}
29+
30+
* {box-sizing:border-box}
31+
32+
/* Slideshow container */
33+
.slideshow-container {
34+
max-width: 1000px;
35+
position: relative;
36+
margin: auto;
37+
}
38+
39+
/* navigation container */
40+
.navi-container {
41+
position: fixed;
42+
bottom: 15px;
43+
text-align: center;
44+
margin: 0;
45+
left: 50%;
46+
}
47+
48+
/* Next and previous slide button */
49+
.prev, .next {
50+
cursor: pointer;
51+
position: fixed;
52+
top: 50%;
53+
width: auto;
54+
padding: 0 20px 40% 20px;
55+
color: lightgray;
56+
font-weight: bold;
57+
font-size: 18px;
58+
transition: 0.6s ease;
59+
border-radius: 0 3px 3px 0;
60+
user-select: none;
61+
}
62+
63+
/* Move the "next" button to the right */
64+
.next {
65+
right: 0;
66+
border-radius: 3px 0 0 3px;
67+
}
68+
69+
a.prev:hover, a.next:hover {
70+
color: darkgray;
71+
text-decoration: none;
72+
font-size: 24px;
73+
}
74+
75+
/* navigation */
76+
a.slide-nav, .slide-nav {
77+
font-size: 18px;
78+
padding: 0 7px;
79+
color: lightgray;
80+
text-align: center;
81+
cursor: pointer;
82+
display: inline-block;
83+
transition: 0.6s ease;
84+
}
85+
86+
.active, .slide-nav:hover {
87+
color: darkgray;
88+
}

src/moin/templates/slideshow.html

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{% extends "base.html" %}
2+
{% block layout %}
3+
4+
{% if data_rendered %}
5+
<link media="all" rel="stylesheet" href="{{ url_for('static', filename='css/projection.css') }}">
6+
<div><span id="Start"></span></div>
7+
<div id="moin-content-data">
8+
{{ data_rendered }}
9+
</div>
10+
<div><span id="End"></span></div>
11+
{% endif %}
12+
13+
<!-- Next and previous buttons -->
14+
<a class="prev" onclick="prevSlide()">&#10094;</a>
15+
<a class="next" onclick="nextSlide()">&#10095;</a>
16+
17+
<!-- navigation to first or last slide or exit slideshow -->
18+
<div class="navi-container">
19+
<span class="slide-nav" onclick="showSlide(1)" title="First slide"><i class="fa-solid fa-backward-fast"></i></span>
20+
<span class="slide-nav"><a class="slide-nav" title="Exit slideshow" href="/{{full_name}}">
21+
<i class="fa-solid fa-right-from-bracket"></i></a></span>
22+
<span class="slide-nav" onclick="lastSlide()" title="Last slide"><i class="fa-solid fa-forward-fast"></i></span>
23+
</div>
24+
25+
<script>
26+
let slideNo = 1;
27+
let slides = document.getElementsByClassName("moin-slides");
28+
showSlide(slideNo);
29+
30+
function nextSlide() {
31+
if (slideNo < slides.length) {
32+
showSlide(slideNo += 1);
33+
}
34+
}
35+
36+
function prevSlide() {
37+
if (slideNo > 1) {
38+
showSlide(slideNo -= 1);
39+
}
40+
}
41+
42+
function lastSlide() {
43+
showSlide(slides.length);
44+
}
45+
46+
function showSlide(n) {
47+
let i;
48+
slideNo = n;
49+
for (i = 0; i < slides.length; i++) {
50+
slides[i].style.display = "none";
51+
}
52+
slides[slideNo - 1].style.display = "block";
53+
}
54+
</script>
55+
{% endblock %}

0 commit comments

Comments
 (0)