Änderungen von Dokument Home

Zuletzt geändert von Steffen Rehfuß am 14.08.2026 18:48

Von Version 1.1
bearbeitet von Steffen Rehfuß
am 14.08.2026 18:21
Änderungskommentar: Copied from xwiki:Main.WebHome
Auf Version 4.1
bearbeitet von Steffen Rehfuß
am 14.08.2026 18:47
Änderungskommentar: Es gibt keinen Kommentar für diese Version

Zusammenfassung

Details

Seiteneigenschaften
Inhalt
... ... @@ -4,25 +4,26 @@
4 4  ## Einfach in den Seiten-Editor (Quelltext/Wiki) einfuegen.
5 5  ## =====================================================================
6 6  ##
7 -## ---- KONFIG 1: Blog-Space + Hero-Kategorie ----
7 +## ---- KONFIG 1: Blog-Space + Hero-Kategorie + Laufschrift-Kategorie ----
8 8  #set ($blogSpace = 'Blog') ## technischer Space-Name des Blogs
9 9  #set ($heroCategory = 'TOP') ## Kategorie, die gross dargestellt wird
10 +#set ($breakingCategory = 'Breaking News') ## Kategorie fuer die Laufschrift oben
10 10  ##
11 11  ## ---- KONFIG 2: Die 12 App-Buttons (Text / Icon / Link / Farbe) ----
12 12  ## icon = Font-Awesome-Name (ohne "fa-"). Farbe = beliebiger Hex-Wert.
13 13  #set ($apps = [
14 - {'label':'Wissensmanagement', 'icon':'book', 'url':'https://xwiki.unternehmens-wiki.de/wiki/kbv/view/Wissensmanagement', 'color':'#c30759', 'fg':'#ffffff'},
15 - {'label':'MyPage', 'icon':'key', 'url':'https://xwiki.unternehmens-wiki.de/wiki/kbv/view/MyPage/', 'color':'#c30759', 'fg':'#ffffff'},
16 - {'label':'Blog', 'icon':'bell', 'url':'https://xwiki.unternehmens-wiki.de/wiki/kbv/view/Blog/', 'color':'#c30759', 'fg':'#ffffff'},
17 - {'label':'Chatbot', 'icon':'pencil-square-o', 'url':"javascript:document.getElementById('toggle-chat-button').click();void(0);", 'color':'#385c6b', 'fg':'#ffffff'},
18 - {'label':'Forum', 'icon':'group', 'url':'https://xwiki.unternehmens-wiki.de/wiki/kbv/view/Forums/', 'color':'#385c6b', 'fg':'#ffffff'},
19 - {'label':'Umfragen', 'icon':'question', 'url':'https://xwiki.unternehmens-wiki.de/wiki/kbv/view/XPoll/', 'color':'#385c6b', 'fg':'#ffffff'},
20 - {'label':'Gruppenräume', 'icon':'life-ring', 'url':'https://xwiki.unternehmens-wiki.de/wiki/kbv/view/Rooms/', 'color':'#4d0f26', 'fg':'#ffffff'},
21 - {'label':'Onboarding', 'icon':'graduation-cap', 'url':'', 'color':'#4d0f26', 'fg':'#ffffff'},
22 - {'label':'Meine Favoriten', 'icon':'star', 'url':'https://xwiki.unternehmens-wiki.de/wiki/kbv/view/Internes/Favoriten/', 'color':'#4d0f26', 'fg':'#ffffff'},
23 - {'label':'Organigramm', 'icon':'sitemap', 'url':'https://xwiki.unternehmens-wiki.de/wiki/kbv/view/Internes/Organigramm', 'color':'#73dddd', 'fg':'#4d0f26'},
24 - {'label':'Tag Filter', 'icon':'filter', 'url':'https://xwiki.unternehmens-wiki.de/wiki/kbv/view/KBC%20Anwendungen/Tag%20Filter/', 'color':'#73dddd', 'fg':'#4d0f26'},
25 - {'label':'Kantine', 'icon':'coffee', 'url':'https://xwiki.unternehmens-wiki.de/wiki/kbv/view/Internes/Kantine/Speiseplan%20vom%2013.07.2026%20-%2017.07.2026', 'color':'#73dddd', 'fg':'#4d0f26'}
15 + {'label':'Haupt-Wiki', 'icon':'search', 'ref':'Main.WebHome', 'color':'#16a085'},
16 + {'label':'MyPage', 'icon':'key', 'ref':'MyPage.WebHome', 'color':'#2980b9'},
17 + {'label':'Blog', 'icon':'star', 'ref':'Blog.WebHome', 'color':'#8e44ad'},
18 + {'label':'Chatbot', 'icon':'pencil-square-o', 'url':"javascript:document.getElementById('toggle-chat-button').click();void(0);", 'color':'#16a085'},
19 + {'label':'Forum', 'icon':'group', 'ref':'Forums.WebHome', 'color':'#2980b9'},
20 + {'label':'Umfragen', 'icon':'question', 'ref':'XPoll.WebHome', 'color':'#8e44ad'},
21 + {'label':'Gruppenräume', 'icon':'life-ring', 'ref':'Rooms.WebHome', 'color':'#16a085'},
22 + {'label':'Meine Favoriten', 'icon':'star', 'ref':'Internes.Favoriten.WebHome', 'color':'#2980b9'},
23 + {'label':'Kalender', 'icon':'clock-o', 'ref':'MoccaCalendar.WebHome', 'color':'#8e44ad'},
24 + {'label':'Org-Chart', 'icon':'sitemap', 'ref':'Internes.Organigramm.WebHome', 'color':'#2c3e50'},
25 + {'label':'Tag Filter', 'icon':'filter', 'ref':'KBC Anwendungen.Tag Filter.WebHome', 'color':'#2c3e50'},
26 + {'label':'Kantine', 'icon':'coffee', 'ref':'Internes.Kantine.Speiseplan vom 13\.07\.2026 - 17\.07\.2026.WebHome', 'color':'#2c3e50'}
26 26  ])
27 27  ##
28 28  ## ---- Posts laden (neueste zuerst) ----
... ... @@ -37,14 +37,17 @@
37 37  #end
38 38  #set ($postNames = $q.execute())
39 39  ##
40 -## ---- Hero (erster TOP-Post) + bis zu 4 Kachel-Posts bestimmen ----
41 +## ---- Hero (erster TOP-Post) + bis zu 4 Kachel-Posts + neuester Breaking-News-Post bestimmen ----
41 41  #set ($heroDoc = $NULL)
42 42  #set ($heroObj = $NULL)
43 43  #set ($tilePosts = [])
45 +#set ($breakingDoc = $NULL)
46 +#set ($breakingObj = $NULL)
44 44  #foreach ($pn in $postNames)
45 45   #set ($pd = $xwiki.getDocument($pn))
46 46   #set ($po = $pd.getObject('Blog.BlogPostClass'))
47 47   #set ($isTop = false)
51 + #set ($isBreaking = false)
48 48   #set ($cats = $po.getValue('category'))
49 49   #if ($cats)
50 50   #foreach ($c in $cats)
... ... @@ -53,8 +53,15 @@
53 53   #if ($cn == $heroCategory)
54 54   #set ($isTop = true)
55 55   #end
60 + #if ($cn == $breakingCategory)
61 + #set ($isBreaking = true)
62 + #end
56 56   #end
57 57   #end
65 + #if ($isBreaking && !$breakingDoc)
66 + #set ($breakingDoc = $pd)
67 + #set ($breakingObj = $po)
68 + #end
58 58   #if ($isTop && !$heroDoc)
59 59   #set ($heroDoc = $pd)
60 60   #set ($heroObj = $po)
... ... @@ -87,6 +87,14 @@
87 87   #set ($heroDate = $heroObj.getValue('publishDate'))
88 88  #end
89 89  ##
101 +## ---- Laufschrift-Daten aufbereiten ----
102 +#if ($breakingDoc)
103 + #set ($breakingTitle = $breakingDoc.getPlainTitle())
104 + #set ($breakingText = "$!breakingObj.getValue('extract')")
105 + #set ($breakingURL = $breakingDoc.getURL('view'))
106 + #set ($breakingDate = $breakingObj.getValue('publishDate'))
107 +#end
108 +##
90 90  {{html clean="false" wiki="false"}}
91 91  <style>
92 92  ## Seitenkopf (Titel + "Zuletzt geaendert" + Aktionsmenue) auf DIESER Seite ausblenden
... ... @@ -102,6 +102,34 @@
102 102  ## Fusszeile: Tags + "Gefaellt mir" + "Erstellt von / uebersetzt von" ausblenden
103 103  #xdocTags, .doc-tags, .like-container, .xdocCreation { display: none !important; }
104 104  .intra-wrap { width: 90%; max-width: 90%; margin: 0 auto; }
124 +.intra-ticker {
125 + display: flex; align-items: stretch; height: 38px;
126 + background: #c0392b; margin-bottom: 20px; overflow: hidden; border-radius: 8px;
127 +}
128 +.intra-ticker-label {
129 + flex: 0 0 auto; display: flex; align-items: center;
130 + background: #922b21; color: #fff !important; text-decoration: none;
131 + font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
132 + padding: 0 18px; white-space: nowrap;
133 +}
134 +.intra-ticker-viewport { flex: 1 1 auto; position: relative; overflow: hidden; }
135 +.intra-ticker-date {
136 + flex: 0 0 auto; display: flex; align-items: center;
137 + color: #ffd7d0 !important; font-size: 13px; font-weight: 700;
138 + padding: 0 6px 0 18px; white-space: nowrap;
139 +}
140 +.intra-ticker-track {
141 + position: absolute; top: 0; left: 100%; height: 100%;
142 + display: flex; align-items: center; white-space: nowrap;
143 + color: #fff !important; text-decoration: none; font-size: 14px; font-weight: 600;
144 + padding: 0 18px;
145 + animation: intra-ticker-scroll 22s linear infinite;
146 +}
147 +.intra-ticker-track:hover { animation-play-state: paused; text-decoration: underline; }
148 +@keyframes intra-ticker-scroll {
149 + from { left: 100%; }
150 + to { left: -100%; }
151 +}
105 105  .intra-cols { display: flex; gap: 20px; align-items: stretch; }
106 106  .intra-main { flex: 2 1 0; min-width: 0; }
107 107  .intra-side { flex: 1 1 0; display: flex; flex-direction: column; gap: 16px; }
... ... @@ -109,7 +109,13 @@
109 109   position: relative; width: 100%; min-height: 360px; border-radius: 14px;
110 110   overflow: hidden; background: #223; background-size: cover; background-position: center;
111 111   box-shadow: 0 6px 18px rgba(0,0,0,.15); text-decoration: none; display: block;
159 + transition: transform .15s ease, box-shadow .15s ease;
112 112  }
161 +.intra-hero:hover {
162 + transform: translateY(-3px);
163 + box-shadow: 0 12px 28px rgba(0,0,0,.22);
164 +}
165 +.intra-hero:hover .intra-btn { filter: brightness(1.08); }
113 113  .intra-hero::after {
114 114   content: ""; position: absolute; inset: 0;
115 115   background: linear-gradient(180deg, rgba(0,0,0,0) 30%, rgba(0,0,0,.75) 100%);
... ... @@ -127,32 +127,12 @@
127 127  }
128 128  .intra-btn:hover { filter: brightness(1.08); }
129 129  .intra-apps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
130 -
131 131  .intra-app {
132 - display: flex;
133 - flex-direction: column;
134 - justify-content: space-between;
135 - border-radius: 10px;
136 - padding: 12px 12px 10px;
137 - min-height: 84px;
138 - color: #fff !important;
139 - text-decoration: none;
140 - transition: transform .15s ease,
141 - box-shadow .15s ease,
142 - filter .15s ease;
184 + display: flex; flex-direction: column; justify-content: space-between;
185 + border-radius: 10px; padding: 12px 12px 10px; min-height: 84px; color: #fff !important;
186 + text-decoration: none; transition: transform .15s ease, box-shadow .15s ease;
143 143  }
144 -
145 -.intra-app:hover {
146 - transform: translateY(-2px);
147 - filter: brightness(.90); /* ca. 8 % dunkler */
148 - box-shadow: 0 6px 14px rgba(0,0,0,.18);
149 -}
150 -
151 -.intra-app:active {
152 - filter: brightness(.86);
153 -}
154 -
155 -
188 +.intra-app:hover { transform: translateY(-2px); box-shadow: 0 6px 14px rgba(0,0,0,.18); }
156 156  .intra-app-ic { font-size: 22px; line-height: 1; }
157 157  .intra-app-ic .fa { color: #fff; }
158 158  .intra-app-lbl { font-size: 13px; font-weight: 600; margin-top: 8px; }
... ... @@ -164,6 +164,8 @@
164 164  }
165 165  .intra-widget { background: #fff; border: 0.5px solid #e2e6ea; border-radius: 12px; padding: 16px 18px; box-shadow: 0 3px 12px rgba(0,0,0,.06); margin-top: 35px; }
166 166  .intra-widget-h { font-size: 14px; font-weight: 700; color: #16324f; margin-bottom: 10px; }
200 +.intra-cal-link { color: #16324f; text-decoration: none; transition: color .15s ease; }
201 +.intra-cal-link:hover { color: #2c6fbb; }
167 167  .intra-tagcloud { line-height: 2; }
168 168  .intra-tag { display: inline-block; margin: 0 9px 4px 0; color: #2c6fbb; text-decoration: none; }
169 169  .intra-tag:hover { text-decoration: underline; }
... ... @@ -173,6 +173,7 @@
173 173  .intra-cal-item:hover .intra-cal-title { text-decoration: underline; }
174 174  .intra-cal-date { flex: 0 0 auto; font-size: 12px; font-weight: 700; color: #2c6fbb; min-width: 52px; letter-spacing: .02em; }
175 175  .intra-cal-title { font-size: 13px; color: #16324f; }
211 +.intra-cal-dot { flex: 0 0 auto; width: 9px; height: 9px; border-radius: 50%; align-self: center; box-shadow: 0 0 0 1px rgba(0,0,0,.06) inset; }
176 176  .intra-cal-empty { color: #8a97a6; font-size: 13px; }
177 177  .intra-tile {
178 178   display: flex; background: #fff; border-radius: 12px; overflow: hidden;
... ... @@ -199,7 +199,7 @@
199 199   position: relative;
200 200   transform-style: preserve-3d;
201 201   transition: transform .6s cubic-bezier(.2,.7,.2,1);
202 - transition-delay: 0s; /* Richtungserkennung im JS verhindert Fehl-Flips */
238 + transition-delay: .12s; /* kein Flip beim blossen Vorbeiziehen */
203 203  }
204 204  .intra-flip.is-back .intra-flip-inner { transform: rotateY(180deg); }
205 205  
... ... @@ -226,6 +226,18 @@
226 226  </style>
227 227  
228 228  <div class="intra-wrap">
265 +
266 + ## ----- LAUFSCHRIFT: neuester Post mit Kategorie "Breaking News" -----
267 + #if ($breakingDoc)
268 + <div class="intra-ticker">
269 + <a class="intra-ticker-label" href="$breakingURL">$escapetool.xml($breakingCategory)</a>
270 + #if ($breakingDate)<span class="intra-ticker-date">$datetool.format('dd.MM.yyyy', $breakingDate)</span>#end
271 + <div class="intra-ticker-viewport">
272 + <a class="intra-ticker-track" href="$breakingURL">$escapetool.xml($breakingTitle)#if($breakingText != '') — $escapetool.xml($breakingText)#end</a>
273 + </div>
274 + </div>
275 + #end
276 +
229 229   <div class="intra-cols">
230 230  
231 231   ## ===== LINKE SPALTE: Hero + 4 Blog-Kacheln =====
... ... @@ -284,11 +284,17 @@
284 284   ## ----- 12 APP-BUTTONS -----
285 285   <div class="intra-apps">
286 286   #foreach ($app in $apps)
287 - <a class="intra-app" style="background: $app.color;" href="#if($app.url != '')$app.url#else#end">
288 - <span class="intra-app-ic"><span class="fa fa-$app.icon" aria-hidden="true"></span></span>
289 - <span class="intra-app-lbl">$escapetool.xml($app.label)</span>
290 - </a>
291 - #end
335 + #set ($href = '')
336 + #if ("$!app.ref" != '')
337 + #set ($href = $xwiki.getURL($app.ref))
338 + #elseif ("$!app.url" != '')
339 + #set ($href = $app.url)
340 + #end
341 + <a class="intra-app" style="background: $app.color;"#if($href != '') href="$href"#end>
342 + <span class="intra-app-ic"><span class="fa fa-$app.icon" aria-hidden="true"></span></span>
343 + <span class="intra-app-lbl">$escapetool.xml($app.label)</span>
344 + </a>
345 +#end
292 292   </div>
293 293  
294 294  ## ----- NAECHSTE TERMINE (Mocca Calendar) / RUECKSEITE: TAGCLOUD -----
... ... @@ -298,10 +298,19 @@
298 298  
299 299   <!-- VORDERSEITE: Naechste Termine -->
300 300   <div class="intra-flip-face intra-flip-front">
301 - <span class="intra-flip-hint">&#8942; Maus von unten</span>
302 - <div class="intra-widget-h">Nächste Termine</div>
355 + <div class="intra-widget-h"><a class="intra-cal-link" href="$xwiki.getURL('MoccaCalendar.WebHome', 'view')">Nächste Termine</a></div>
303 303   <div class="intra-cal-list">
304 304   #set ($now = $datetool.date)
358 + ## ---- Kalenderfarben einmalig laden: Event-Elternseite (= Kalender) -> Farbe/Titel ----
359 + #set ($calXwql = "select doc.fullName, calendar.color, calendar.title from Document doc, doc.object(MoccaCalendar.MoccaCalendarClass) calendar")
360 + #set ($calResults = $services.query.xwql($calXwql).execute())
361 + #set ($calColorMap = {})
362 + #set ($calTitleMap = {})
363 + #foreach ($cr in $calResults)
364 + #set ($discard = $calColorMap.put("$cr.get(0)", "$!cr.get(1)"))
365 + #set ($discard = $calTitleMap.put("$cr.get(0)", "$!cr.get(2)"))
366 + #end
367 + #set ($defaultCalColor = '#8a97a6')
305 305   #set ($evtXwql = "from doc.object(MoccaCalendar.MoccaCalendarEventClass) as event where event.startDate >= :now and doc.hidden = false order by event.startDate asc")
306 306   #set ($evtNames = $services.query.xwql($evtXwql).bindValue('now', $now).setLimit(6).execute())
307 307   #if ($evtNames && $evtNames.size() > 0)
... ... @@ -312,7 +312,18 @@
312 312   #set ($eAllDay = $eo.getValue('allDay'))
313 313   #set ($eTitle = $ed.getPlainTitle())
314 314   #if ($eTitle == '')#set ($eTitle = "$!eo.getValue('title')")#end
378 + ## Kalender der Elternseite ermitteln (Events liegen als Kind-Seite unter ihrem Kalender)
379 + #set ($eParent = "$!ed.getParent()")
380 + #set ($eColor = $calColorMap.get($eParent))
381 + #if (!$eColor || $eColor == '')
382 + #set ($eColor = $defaultCalColor)
383 + #end
384 + #set ($eCalName = $calTitleMap.get($eParent))
385 + #if (!$eCalName || $eCalName == '')
386 + #set ($eCalName = $xwiki.getDocument($eParent).getPlainTitle())
387 + #end
315 315   <a class="intra-cal-item" href="$ed.getURL('view')">
389 + <span class="intra-cal-dot" style="background: $escapetool.xml($eColor);" title="$escapetool.xml($eCalName)"></span>
316 316   <span class="intra-cal-date">#if($eStart)$datetool.format('dd.MM.', $eStart)#if("$!eAllDay" != '1') $datetool.format('HH:mm', $eStart)#end#end</span>
317 317   <span class="intra-cal-title">$escapetool.xml($eTitle)</span>
318 318   </a>
... ... @@ -373,21 +373,18 @@
373 373   var box = document.getElementById('intra-flip-side');
374 374   if(!box){ return; }
375 375   var hoverCapable = !!(window.matchMedia && window.matchMedia('(hover: hover)').matches);
376 -
377 - // Letzte bekannte Maus-Y-Position global mitverfolgen, um beim Betreten der Karte
378 - // die Bewegungsrichtung zu kennen (Y waechst nach unten -> "von unten" = Y wird kleiner).
450 + ## Letzte bekannte Maus-Y-Position global mitverfolgen, um beim Betreten der Karte
451 + ## die Bewegungsrichtung zu kennen (Y waechst nach unten -> "von unten" = Y wird kleiner).
379 379   var lastY = null;
380 380   document.addEventListener('mousemove', function(e){ lastY = e.clientY; }, {passive:true});
381 -
382 382   box.addEventListener('mouseenter', function(e){
383 383   if (!hoverCapable) { return; }
384 - var enteredFromBelow = (lastY !== null && (lastY - e.clientY) > 2);
456 + var enteredFromBelow = (lastY !== null && e.clientY < lastY);
385 385   if (enteredFromBelow) {
386 386   box.classList.toggle('is-back');
387 387   }
388 388   lastY = e.clientY;
389 389   });
390 -
391 391   box.addEventListener('click', function(e){
392 392   if(e.target.closest('a')){ return; }
393 393   if(hoverCapable){ return; }