Skip to content

Commit

Permalink
Merge pull request #1716 from UlrichB22/slideshow
Browse files Browse the repository at this point in the history
Add SlideShow macro and view
  • Loading branch information
RogerHaase authored Aug 1, 2024
2 parents 6dd1a78 + e2d4b6b commit ae51d7a
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 1 deletion.
17 changes: 17 additions & 0 deletions src/moin/apps/frontend/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,23 @@ def content_item(item_name, rev):
return render_template("content.html", item_name=item.name, data_rendered=Markup(item.content._render_data()))


@frontend.route("/+slideshow/<itemname:item_name>", defaults=dict(rev=CURRENT))
def slide_item(item_name, rev):
"""same as show_item, but we only show the content"""
fqname = split_fqname(item_name)
item_displayed.send(app, fqname=fqname)
try:
item = Item.create(item_name, rev_id=rev)
except AccessDenied:
abort(403)
if isinstance(item, NonExistent):
abort(404, item_name)
data_rendered = Markup(item.content._render_data_slide())
return render_template(
"slideshow.html", item_name=item.name, full_name=fqname.fullname, data_rendered=data_rendered
)


@presenter("get")
def get_item(item):
return item.content.do_get()
Expand Down
2 changes: 1 addition & 1 deletion src/moin/constants/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
LOCK = "lock"

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

# Transient attribute added/removed to/from flask session. Used when a User Settings
# form creates a flash message but then redirects the page making the flash message a
Expand Down
49 changes: 49 additions & 0 deletions src/moin/items/content.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,55 @@ def _get_data_diff_text(self, oldfile, newfile):
"""
return []

def _render_data_slide(self, preview=None):
try:
from moin.converters import default_registry as reg

doc = self.internal_representation(preview=preview)
doc = self._expand_document(doc)

slide_pages = []
before_first_header = True
for elem1 in doc:
single_slide = []
for element in elem1:
if element.tag.name == "h" and element.get(moin_page("outline-level")) in ["1", "2"]:
if before_first_header:
before_first_header = False # ignore everything before
else:
slide_pages.append(single_slide)
single_slide = []
single_slide.append(element)
slide_pages.append(single_slide)
print(f"{len(slide_pages)} slides found.")

flaskg.clock.start("conv_dom_html")
html_conv = reg.get(type_moin_document, Type("application/x-xhtml-moin-page"))

slide_content = []
attrib = {moin_page.class_: "moin-slides"}
for slide in slide_pages:
slide_content.append(moin_page.div(attrib=attrib, children=slide))

body = moin_page.body(children=slide_content)
root = moin_page.page(children=[body])
doc = html_conv(root)
rendered_data = conv_serialize(doc, {html.namespace: ""})
flaskg.clock.stop("conv_dom_html")

except Exception:
# we really want to make sure that invalid data or a malfunctioning
# converter does not crash the item view (otherwise a user might
# not be able to fix it from the UI).
error_id = uuid.uuid4()
logging.exception(f"An exception happened in _render_data (error_id = {error_id} ):")
rendered_data = [
render_template(
"crash.html", server_time=time.strftime("%Y-%m-%d %H:%M:%S %Z"), url=request.url, error_id=error_id
)
]
return rendered_data

def get_templates(self, contenttype=None):
"""create a list of templates (for some specific contenttype)"""
terms = [
Expand Down
18 changes: 18 additions & 0 deletions src/moin/macros/SlideShow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright: 2024 MoinMoin:UlrichB
# License: GNU GPL v2 (or any later version), see LICENSE.txt for details.

"""
Create link to start a SlideShow for the current item
"""

from moin.i18n import _
from moin.utils.tree import moin_page, xlink
from moin.macros._base import MacroInlineBase


class Macro(MacroInlineBase):
def macro(self, content, arguments, page_url, alternative):
attrib = {moin_page.class_: "fa-regular fa-circle-play"}
children = [moin_page.span(attrib=attrib), _(" Start SlideShow")]
result = moin_page.a(attrib={xlink.href: f"/+slideshow{page_url.path}"}, children=children)
return result
88 changes: 88 additions & 0 deletions src/moin/static/css/projection.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/* projection.css - MoinMoin Slide Styles
Copyright (c) 2003 by Juergen Hermann
Copyright (c) 2024 by MoinMoin project
*/

html { line-height: 1.8em; }

body, b, em, a, span, div, p, td { font-size: 18pt; }

h1 { font-size: 24pt; padding-top: 24px; color: #33F; text-align: left; }
h2 { font-size: 22pt; padding-top: 24px; color: #33F; }
h3 { font-size: 20pt; }
h4 { font-size: 18pt; }
h5 { font-size: 16pt; }
h6 { font-size: 14pt; }

tt, pre { font-size: 14pt; }
sup, sub { font-size: 10pt; }

#moin-content-data { padding-left: 50px; margin: 0 2%; }

@media print {
h1 { padding-top: 50px; }
h2 { page-break-before: always; padding-top: 50px; }
h3, h4 { page-break-after: avoid; }
pre, blockquote { page-break-inside: avoid; }
}

* {box-sizing:border-box}

/* Slideshow container */
.slideshow-container {
max-width: 1000px;
position: relative;
margin: auto;
}

/* navigation container */
.navi-container {
position: fixed;
bottom: 15px;
text-align: center;
margin: 0;
left: 50%;
}

/* Next and previous slide button */
.prev, .next {
cursor: pointer;
position: fixed;
top: 50%;
width: auto;
padding: 0 20px 40% 20px;
color: lightgray;
font-weight: bold;
font-size: 18px;
transition: 0.6s ease;
border-radius: 0 3px 3px 0;
user-select: none;
}

/* Move the "next" button to the right */
.next {
right: 0;
border-radius: 3px 0 0 3px;
}

a.prev:hover, a.next:hover {
color: darkgray;
text-decoration: none;
font-size: 24px;
}

/* navigation */
a.slide-nav, .slide-nav {
font-size: 18px;
padding: 0 7px;
color: lightgray;
text-align: center;
cursor: pointer;
display: inline-block;
transition: 0.6s ease;
}

.active, .slide-nav:hover {
color: darkgray;
}
55 changes: 55 additions & 0 deletions src/moin/templates/slideshow.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{% extends "base.html" %}
{% block layout %}

{% if data_rendered %}
<link media="all" rel="stylesheet" href="{{ url_for('static', filename='css/projection.css') }}">
<div><span id="Start"></span></div>
<div id="moin-content-data">
{{ data_rendered }}
</div>
<div><span id="End"></span></div>
{% endif %}

<!-- Next and previous buttons -->
<a class="prev" onclick="prevSlide()">&#10094;</a>
<a class="next" onclick="nextSlide()">&#10095;</a>

<!-- navigation to first or last slide or exit slideshow -->
<div class="navi-container">
<span class="slide-nav" onclick="showSlide(1)" title="First slide"><i class="fa-solid fa-backward-fast"></i></span>
<span class="slide-nav"><a class="slide-nav" title="Exit slideshow" href="/{{full_name}}">
<i class="fa-solid fa-right-from-bracket"></i></a></span>
<span class="slide-nav" onclick="lastSlide()" title="Last slide"><i class="fa-solid fa-forward-fast"></i></span>
</div>

<script>
let slideNo = 1;
let slides = document.getElementsByClassName("moin-slides");
showSlide(slideNo);

function nextSlide() {
if (slideNo < slides.length) {
showSlide(slideNo += 1);
}
}

function prevSlide() {
if (slideNo > 1) {
showSlide(slideNo -= 1);
}
}

function lastSlide() {
showSlide(slides.length);
}

function showSlide(n) {
let i;
slideNo = n;
for (i = 0; i < slides.length; i++) {
slides[i].style.display = "none";
}
slides[slideNo - 1].style.display = "block";
}
</script>
{% endblock %}

0 comments on commit ae51d7a

Please sign in to comment.