Compare commits

..
14 Commits
Author SHA1 Message Date
0qln a421546df3 5.9.5: IconFile-Werte, Rueckfallverhalten und Symbolverweise ergaenzt 2026-09-01 12:33:54 +02:00
0qln 71c55cb9e8 5.8.4: Freigabe-Endpunkt kommt ohne Speicherzugriff aus 2026-09-01 12:32:30 +02:00
0qln 43feca99a9 5.8: Verweise auf Screenshots der Vorschau und Freigabelinks 2026-09-01 12:30:54 +02:00
0qln 8d251c73a1 5.4.1: Symbolsatz im Anhang, Urheberschaft der Entwuerfe ergaenzt 2026-09-01 12:30:13 +02:00
0qln b38bdca078 4.5.2: Urheberschaft der Loesungsansaetze klargestellt 2026-09-01 12:27:22 +02:00
0qln cdcb38f6d0 Anhang: Screenshots des Dokumentenbereichs; Verweise aus 4.4 2026-09-01 12:26:42 +02:00
0qln f7bff8d238 4.4.2: Zeilendarstellung als Entscheidung von Hanna Ebner ausgewiesen 2026-09-01 12:24:10 +02:00
0qln ffe2483f44 4.4.1: Clickdummy auf die Typfilterung eingegrenzt, Urheberschaft klargestellt 2026-09-01 12:23:36 +02:00
0qln 8ef18a03b2 4.3.3: Verhalten bei unbekannten Typ-Ordnern gegen Code praezisiert 2026-09-01 12:22:39 +02:00
0qln a4452b70b1 Abb. 3.2: Infrastruktur-Chronologie als Flussdiagramm von links nach rechts 2026-09-01 12:06:37 +02:00
0qln b949d1926a 3.4.2: PBI- und Anforderungstabelle einleitend verweisen 2026-09-01 12:04:49 +02:00
0qln 5bc07bd1a9 3.4.1: Unicorn-Softwareprozess statt eigenem Zustandsdiagramm 2026-09-01 12:04:02 +02:00
0qln b6526e76c6 1.3: Systemkontext-Abbildung aus PidI 2 uebernommen 2026-09-01 12:03:03 +02:00
0qln d09434f72f 5.1: Newtype-Pattern und parse-dont-validate als Entwurfsgrundlage 2026-09-01 11:51:10 +02:00
42 changed files with 335 additions and 63 deletions
+14 -14
View File
@@ -9,27 +9,27 @@ screenshots habe ich in figures/screenshots gelegt.
-> "Figure 3.2: Zeitplanung des Projekts" aktualisieren -> "Figure 3.2: Zeitplanung des Projekts" aktualisieren
-> "5.10.5 Stand bei Abgabe" aktualisieren -> "5.10.5 Stand bei Abgabe" aktualisieren
-> allgemein ueber alle chapter nochmal drueber gehen und schauen ob das den aktuellen stand reflektiert -> allgemein ueber alle chapter nochmal drueber gehen und schauen ob das den aktuellen stand reflektiert
- [ ] architektur der implementierung: - [x] architektur der implementierung:
- ich habe in meiner freizeit sehr viel rust programmiert und dort auch das NewType pattern kennen gelernt, an dem ich mich hier orientierte (auch wenn es C# ist), da ich die vorteile besonders hier wo man eigentlich mit den nur mit ganz vielen string keys rum hantiert, sie aus einander baut, und wieder zusammenbaut, erkannte. id input string -> s3 gibt keys zurueck -> parsing -> domain model -> verbarbeitung -> wenn weitere requests gebraucht werden, vom domain model wieder einen key zusammenbauen - ich habe in meiner freizeit sehr viel rust programmiert und dort auch das NewType pattern kennen gelernt, an dem ich mich hier orientierte (auch wenn es C# ist), da ich die vorteile besonders hier wo man eigentlich mit den nur mit ganz vielen string keys rum hantiert, sie aus einander baut, und wieder zusammenbaut, erkannte. id input string -> s3 gibt keys zurueck -> parsing -> domain model -> verbarbeitung -> wenn weitere requests gebraucht werden, vom domain model wieder einen key zusammenbauen
- "parse, don't validate" philosophie - "parse, don't validate" philosophie
- [ ] mit den vom pidi 2 noch relevanten abbildungen ergaenzen - [x] mit den vom pidi 2 noch relevanten abbildungen ergaenzen
- houston architektur - houston architektur
- unicorn prozess - unicorn prozess
- etc. - etc.
- [ ] "Figure 3.1: Zustände eines Work Items in Azure DevOps" sollte mit dem eigentlichem unicorn prozess bild ersaetzt werden. ich arbeite naehmlich nach dem ganz normalen prozess - [x] "Figure 3.1: Zustände eines Work Items in Azure DevOps" sollte mit dem eigentlichem unicorn prozess bild ersaetzt werden. ich arbeite naehmlich nach dem ganz normalen prozess
- "3.4.1 Entwicklungsprozess" sollte dahingehend auch aktualisiert werden, und nicht *Linus-speziefisch* sein. - "3.4.1 Entwicklungsprozess" sollte dahingehend auch aktualisiert werden, und nicht *Linus-speziefisch* sein.
- [ ] "3.4.2 Schnitt der Product Backlog Items": ein abbild aus dem devops oder eine tabelle mit den PBIs ergaenzen verweisen - [x] "3.4.2 Schnitt der Product Backlog Items": ein abbild aus dem devops oder eine tabelle mit den PBIs ergaenzen verweisen
- [ ] "Figure 3.3: Chronologie der Infrastrukturbeschaffung" sieht komisch aus, die pfeile gehen nach unten Chronologie geht aber nach rechts. sollte gefixed werden. - [x] "Figure 3.3: Chronologie der Infrastrukturbeschaffung" sieht komisch aus, die pfeile gehen nach unten Chronologie geht aber nach rechts. sollte gefixed werden.
- [ ] "4.3.3 Verhalten bei unbekannten Ordnern" (todo fuer mich) ist das richtig? oder werden unbakannte typ-order einfach nicht angezeigt? bin mir da gerade nichtmal sicher - [x] "4.3.3 Verhalten bei unbekannten Ordnern" (todo fuer mich) ist das richtig? oder werden unbakannte typ-order einfach nicht angezeigt? bin mir da gerade nichtmal sicher
- [ ] "4.4.1 Zuarbeit über einen Clickdummy" bezieht sich rein ueber das UI der Typ-Filterung. alles andere stammt von mir. Hanna Ebner hat in Houston die fuehrende Entscheidungskraft, was das UI angeht, und aus dem Grund habe ich mich mit ihr abgestimmt wie das aussehen soll, und sie hat als Referenz den Clickdummy branch erstellt. Die implementierung liegt immer noch in meiner hand. (das ist wichtig fuer das PIDI, da die Arbeit eigentlich von mir kommen soll). - [x] "4.4.1 Zuarbeit über einen Clickdummy" bezieht sich rein ueber das UI der Typ-Filterung. alles andere stammt von mir. Hanna Ebner hat in Houston die fuehrende Entscheidungskraft, was das UI angeht, und aus dem Grund habe ich mich mit ihr abgestimmt wie das aussehen soll, und sie hat als Referenz den Clickdummy branch erstellt. Die implementierung liegt immer noch in meiner hand. (das ist wichtig fuer das PIDI, da die Arbeit eigentlich von mir kommen soll).
- [ ] in "4.4.2 Flache Liste statt navigierbarer Hierarchie": "Die ursprüngliche Beschreibung sah Dokumentkacheln vor, wurde jedoch auf Zeilen-darstellung geändert" auch das ist eine Entscheidung von Hanna Ebner, die meine überwichtete. - [x] in "4.4.2 Flache Liste statt navigierbarer Hierarchie": "Die ursprüngliche Beschreibung sah Dokumentkacheln vor, wurde jedoch auf Zeilen-darstellung geändert" auch das ist eine Entscheidung von Hanna Ebner, die meine überwichtete.
- [ ] "4.4.3 Aufbau der Seite": screenshots im anhang einfügen und verweisen - [x] "4.4.3 Aufbau der Seite": screenshots im anhang einfügen und verweisen
- [ ] "4.5.2 Untersuchte Lösungsansätze" Lösungsansätze wurden von allein *mir* erarbeitet und mit Timo und Bianco nur abgesprochen und reviewed. - [x] "4.5.2 Untersuchte Lösungsansätze" Lösungsansätze wurden von allein *mir* erarbeitet und mit Timo und Bianco nur abgesprochen und reviewed.
- [ ] "5.4.1 Eigene Symbole statt Symbolschrift": icons im anhang einfuegen, darauf hinweisen, dass die Designs von mir entworfen und in Zusammenarbeit mit Hanna Ebner ausgearbeitet wurden. - [x] "5.4.1 Eigene Symbole statt Symbolschrift": icons im anhang einfuegen, darauf hinweisen, dass die Designs von mir entworfen und in Zusammenarbeit mit Hanna Ebner ausgearbeitet wurden.
- [x] "Nicht umgesetzt ist eine Filterung durch den Speicher. Houston listet die Objekte auf und filtert die Namen anschließend selbst. Im Review stellte Hanna Ebner die Frage, ob sich das nicht direkt über die Schnittstelle lösen lasse, und musste verneint werden.": auf den abschnitt verweisen wo die AU die Search Integration abgelehnt hat - [x] "Nicht umgesetzt ist eine Filterung durch den Speicher. Houston listet die Objekte auf und filtert die Namen anschließend selbst. Im Review stellte Hanna Ebner die Frage, ob sich das nicht direkt über die Schnittstelle lösen lasse, und musste verneint werden.": auf den abschnitt verweisen wo die AU die Search Integration abgelehnt hat
- [ ] "5.8.3 Vorschau in Messengern": auf screenshots verweisen - [x] "5.8.3 Vorschau in Messengern": auf screenshots verweisen
- [ ] "5.8.4 Sichtbarkeit der Vorschaubilder": darauf verweisen, dass es eigentlich ziemlich elegant ist das gesamte vorschau bild von der base64 encoded id in der url auf dem öffentlichem `/share`-endpunkt zu lesen. somit wird auf diesem öffentlichem endpunkt niemals auch nur ein aufruf nach s3 gemacht. - [x] "5.8.4 Sichtbarkeit der Vorschaubilder": darauf verweisen, dass es eigentlich ziemlich elegant ist das gesamte vorschau bild von der base64 encoded id in der url auf dem öffentlichem `/share`-endpunkt zu lesen. somit wird auf diesem öffentlichem endpunkt niemals auch nur ein aufruf nach s3 gemacht.
- [ ] "5.9.5 Symbole für Verknüpfungen": screenshots von symbolen verweisen, maybe mehr info zu den icons (siehe resources/xwiki/urlfile_docs.xwiki) - [x] "5.9.5 Symbole für Verknüpfungen": screenshots von symbolen verweisen, maybe mehr info zu den icons (siehe resources/xwiki/urlfile_docs.xwiki)
# Strukturelle TODOs # Strukturelle TODOs
+117 -1
View File
@@ -1 +1,117 @@
% TODO: Screenshots der fertigen Documents-Seite (Liste, Filter, Suche, PDF-Modal, ZIP-Auswahl) Die folgenden Aufnahmen zeigen den Dokumentenbereich auf dem Testsystem. Die
Dokumentnamen stammen aus Testdaten und sind bewusst frei erfunden.
\begin{figure}[H]
\centering
\includegraphics[width=\textwidth]{figures/screenshots/explorer_general.png}
\caption{Der Dokumentenbereich in Houston: Suchleiste, Typfilter, Dokumentenliste mit
Mehrfachauswahl und Blätterelemente}
\label{fig:shot-explorer}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=0.85\textwidth]{figures/screenshots/explorer_type-filters.png}
\caption{Typfilterung: Nur die hervorgehobenen Typen werden angezeigt}
\label{fig:shot-type-filters}
\end{figure}
\clearpage
\begin{figure}[H]
\centering
\includegraphics[width=0.85\textwidth]{figures/screenshots/explorer_search-query.png}
\caption{Suche nach einem Namensbestandteil bei gleichzeitig aktiven Typfiltern}
\label{fig:shot-search}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=0.8\textwidth]{figures/screenshots/pagination_page-2.png}
\caption{Zweite Seite der Blätterung; die Adresszeile trägt den Fortsetzungs-Token
\texttt{pageToken} statt einer Seitennummer}
\label{fig:shot-pagination}
\end{figure}
\clearpage
\begin{figure}[H]
\centering
\includegraphics[width=0.8\textwidth]{figures/screenshots/zip-downloads_in-houston.png}
\caption{Auswahl mehrerer Dokumente für den ZIP-Download}
\label{fig:shot-zip-selection}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\textwidth]{figures/screenshots/zip-downloads_after-download.png}
\caption{Das erzeugte Archiv nach dem Entpacken: Die Typordner sind erhalten,
typlose Dokumente liegen auf oberster Ebene}
\label{fig:shot-zip-result}
\end{figure}
\clearpage
\begin{figure}[H]
\centering
\includegraphics[width=0.85\textwidth]{figures/screenshots/pdf_previewer.png}
\caption{PDF-Vorschau im Modal ohne Verlassen der Seite}
\label{fig:shot-pdf-preview}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=0.8\textwidth]{figures/screenshots/explorer_opened-share-link.png}
\caption{Ergebnis eines geöffneten Freigabelinks: Suche, Typfilter und Seite sind so
gesetzt, dass das verwiesene Dokument sichtbar und hervorgehoben ist}
\label{fig:shot-share-target}
\end{figure}
\clearpage
\begin{figure}[H]
\centering
\includegraphics[width=0.85\textwidth]{figures/screenshots/sharelink-preview_in-teams.png}
\caption{Vorschau eines Freigabelinks in Microsoft Teams mit Dokumentname und Typbild}
\label{fig:shot-share-teams}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=0.85\textwidth]{figures/screenshots/url-file-icons.png}
\caption{Symbole für \texttt{.url}-Dateien: mit ausdrücklichem \texttt{IconFile},
ohne \texttt{IconFile} (Typsymbol des Ordners) sowie bekannte Zielsysteme}
\label{fig:shot-url-icons}
\end{figure}
\begin{figure}[H]
\centering
\newcommand{\typeicon}[1]{\raisebox{-0.35\height}{\includegraphics[height=9mm]{figures/icons/#1.pdf}}}
\begin{tabularx}{\textwidth}{@{} c X c X @{}}
\toprule
\multicolumn{2}{@{}l}{\textbf{Dokumententyp}} & \multicolumn{2}{l@{}}{\textbf{Dokumententyp}} \\
\midrule
\typeicon{service-protokoll} & Service-Protokoll
& \typeicon{security-assessments} & Security Assessments \\
\addlinespace
\typeicon{abnahme-dokumente} & Abnahme-Dokumente
& \typeicon{abrechnungsdaten} & Abrechnungsdaten \\
\addlinespace
\typeicon{sla-reports} & SLA-Reports Ticketbearbeitung
& \typeicon{vertragsunterlagen} & Vertragsunterlagen \\
\addlinespace
\typeicon{monitoring-reports} & Monitoring-Reports
& \typeicon{default} & Sonstige Dokumente (Standardsymbol) \\
\addlinespace
\midrule
\multicolumn{4}{@{}l}{\textbf{Symbole für Verknüpfungen auf bekannte Zielsysteme}} \\
\midrule
\typeicon{ms-teams} & Microsoft Teams
& \typeicon{ms-sharepoint} & Microsoft SharePoint \\
\addlinespace
\typeicon{ms-onedrive} & Microsoft OneDrive & & \\
\bottomrule
\end{tabularx}
\caption{Die für den Dokumentenbereich entworfenen Symbole}
\label{fig:type-icons}
\end{figure}
+10 -1
View File
@@ -3,6 +3,15 @@
WorkSimple positioniert sich als Full-Service-IT-Dienstleister, der Unternehmen jeder Größe unterstützt. Die Lösungen sind darauf ausgerichtet, die Komplexität der IT zu reduzieren und gleichzeitig die Effizienz und Sicherheit zu erhöhen. Durch die Nutzung moderner Technologien und agiler Methoden ist WorkSimple in der Lage, schnell und flexibel auf die Anforderungen der Kunden einzugehen. WorkSimple positioniert sich als Full-Service-IT-Dienstleister, der Unternehmen jeder Größe unterstützt. Die Lösungen sind darauf ausgerichtet, die Komplexität der IT zu reduzieren und gleichzeitig die Effizienz und Sicherheit zu erhöhen. Durch die Nutzung moderner Technologien und agiler Methoden ist WorkSimple in der Lage, schnell und flexibel auf die Anforderungen der Kunden einzugehen.
Die intern genutzten Tools wie Kimai (Zeiterfassung) und Efecte (ITSM) sind Beispiele für die praktische Anwendung von IT-Lösungen, die auch Kunden angeboten werden. Dies unterstreicht den praxisnahen Ansatz von WorkSimple. Das Kundenportal Houston, dessen Erweiterung Gegenstand dieses Projekts ist, verbindet diese Systeme gegenüber dem Kunden zu einer einheitlichen Oberfläche — das genaue Zusammenspiel der Kernsysteme wird in Abschnitt~\ref{sec:system-landscape} erläutert. Die intern genutzten Tools wie Kimai (Zeiterfassung) und Efecte (ITSM) sind Beispiele für die praktische Anwendung von IT-Lösungen, die auch Kunden angeboten werden. Dies unterstreicht den praxisnahen Ansatz von WorkSimple. Das Kundenportal Houston, dessen Erweiterung Gegenstand dieses Projekts ist, verbindet diese Systeme gegenüber dem Kunden zu einer einheitlichen Oberfläche.
Abbildung~\ref{fig:houston-uml} zeigt dieses Zusammenspiel im Überblick: Der Kunde besucht Houston und meldet sich über den Azure-Tenant an; Houston bezieht die Sachdaten aus Efecte und Bookstack, während Odoo die Auftragsabwicklung übernimmt. Die Unicorn Development entwickelt Houston und die begleitenden Azure Functions und liefert über Azure DevOps aus. Der Dokumentenbereich ergänzt diese Landschaft um den in Abschnitt~\ref{sec:system-landscape} beschriebenen S3-Speicher.
\begin{figure}[H]
\centering
\includegraphics[width=0.85\textwidth]{figures/houston/uml.png}
\caption{Systemkontext und Zusammenspiel der Kernsysteme zwischen Kunde und WorkSimple}
\label{fig:houston-uml}
\end{figure}
Interne Prozesse und Dokumentation werden im \emph{XWiki} gepflegt, während die Softwareentwicklung nach einem strukturierten DevOps-Prozess mit Azure DevOps erfolgt. Dieser ganzheitliche Ansatz ermöglicht es WorkSimple, sowohl interne als auch kundenseitige IT-Herausforderungen effektiv zu lösen. Interne Prozesse und Dokumentation werden im \emph{XWiki} gepflegt, während die Softwareentwicklung nach einem strukturierten DevOps-Prozess mit Azure DevOps erfolgt. Dieser ganzheitliche Ansatz ermöglicht es WorkSimple, sowohl interne als auch kundenseitige IT-Herausforderungen effektiv zu lösen.
+6 -2
View File
@@ -36,9 +36,13 @@ Der Preis: Das Hinzufügen eines Typs erfordert eine Codeänderung und ein Relea
\subsection{Verhalten bei unbekannten Ordnern} \subsection{Verhalten bei unbekannten Ordnern}
Aus der festen Kodierung ergibt sich die Frage, wie mit Unterordnern umzugehen ist, die nicht im Katalog stehen — etwa einem Ordner \texttt{Sonstiges} in Filestash. Aus der festen Kodierung ergibt sich die Frage, wie mit Unterordnern umzugehen ist, die nicht im Katalog stehen — etwa einem in Filestash von Hand angelegten Ordner \texttt{Sonstiges}.
Dokumente in solchen Ordnern werden angezeigt und erhalten ein neutrales Standard-Icon; lediglich die Typzuordnung entfällt. Ausblenden wurde verworfen, weil es stilles Fehlverhalten erzeugen würde: Ein Dokument wäre unsichtbar, ohne dass der Betreuer dies bemerkt. Dokumente in solchen Ordnern werden angezeigt: Die Typableitung liefert keinen Treffer, das Dokument gilt als typlos und erhält den Anzeigetext \emph{Sonstige Dokumente} sowie ein neutrales Standard-Icon. Ausblenden wurde verworfen, weil es stilles Fehlverhalten erzeugen würde: Ein Dokument wäre unsichtbar, ohne dass der Betreuer dies bemerkt. Dieselbe Behandlung greift für Dokumente, die unmittelbar im Kundenordner liegen.
Praktisch bleibt der Fall die Ausnahme, da Houston die Ordnerstruktur selbst anlegt und pflegt (siehe Abschnitt~\ref{sec:folder-management}) und dabei ausschließlich Ordner des Katalogs erzeugt.
Zu unterscheiden ist dieser Fall vom Umgang mit unbekannten Ordnern auf der \emph{obersten} Ebene, wo ein fehlendes Marker-Metadatum zum vollständigen Ausschluss führt (siehe Abschnitt~\ref{sec:s3-layout}). Der Unterschied ist beabsichtigt: Auf oberster Ebene entscheidet die Struktur über die Mandantentrennung; innerhalb eines Kundenordners ist die Zugehörigkeit bereits geklärt.
Zu unterscheiden ist dieser Fall vom Umgang mit unbekannten Ordnern auf der \emph{obersten} Ebene, wo ein fehlendes Marker-Metadatum zum vollständigen Ausschluss führt (siehe Abschnitt~\ref{sec:s3-layout}). Der Unterschied ist beabsichtigt: Auf oberster Ebene entscheidet die Struktur über die Mandantentrennung; innerhalb eines Kundenordners ist die Zugehörigkeit bereits geklärt. Zu unterscheiden ist dieser Fall vom Umgang mit unbekannten Ordnern auf der \emph{obersten} Ebene, wo ein fehlendes Marker-Metadatum zum vollständigen Ausschluss führt (siehe Abschnitt~\ref{sec:s3-layout}). Der Unterschied ist beabsichtigt: Auf oberster Ebene entscheidet die Struktur über die Mandantentrennung; innerhalb eines Kundenordners ist die Zugehörigkeit bereits geklärt.
+1 -1
View File
@@ -13,7 +13,7 @@ Am 28.~Juli entstand daraus ein eigenes Backlog Item, bewusst als \emph{Recherch
\subsection{Untersuchte Lösungsansätze} \subsection{Untersuchte Lösungsansätze}
Sechs Ansätze wurden betrachtet und mit \emph{Timo Walter} sowie den Ansprechpartnern für die Speicherinfrastruktur diskutiert. Tabelle~\ref{tab:lookup-tradeoffs} im Anhang fasst die Bewertung zusammen. Die sechs im Folgenden dargestellten Ansätze habe ich im Rahmen des Recherche-Items selbst erarbeitet und gegeneinander abgewogen; \emph{Timo Walter} und \emph{Bianco Veigel} dienten als Gesprächspartner für die Rückversicherung und prüften das Ergebnis im Review. Wo Angaben zur Speicherinfrastruktur nötig waren, wurden diese bei den zuständigen Ansprechpartnern eingeholt. Tabelle~\ref{tab:lookup-tradeoffs} im Anhang fasst die Bewertung zusammen.
\subsubsection{In-Memory-Cache} \subsubsection{In-Memory-Cache}
+10 -7
View File
@@ -1,11 +1,13 @@
\section{UI-Konzept} \section{UI-Konzept}
\label{sec:ui-concept} \label{sec:ui-concept}
\subsection{Zuarbeit über einen Clickdummy} \subsection{Abstimmung der Typfilterung über einen Clickdummy}
Das Oberflächenkonzept wurde von \emph{Hanna Ebner} als lauffähiger Clickdummy in einem eigenen Branch des Houston-Repositories umgesetzt und am 22.~Juli 2026 am Feature verlinkt. Das Oberflächenkonzept des Dokumentenbereichs stammt bis auf einen Punkt aus der eigenen Ausarbeitung: Für die Typfilterung existierte kein Vorbild in Houston, und da \emph{Hanna Ebner} die führende Entscheidungsinstanz für die Oberfläche der Anwendung ist, wurde das Aussehen dieser Filterleiste mit ihr abgestimmt. Als Referenz setzte sie ihren Vorschlag am 22.~Juli 2026 als lauffähigen Clickdummy in einem eigenen Branch des Houston-Repositories um und verlinkte ihn am Feature.
Der Clickdummy verwendet die bestehenden Komponenten und Stile der Anwendung, wodurch Fragen zu Abständen, Schriftgrößen oder Farben durch das vorhandene Stylesheet beantwortet werden. Interaktionen wie das Ein- und Ausklappen von Filtern lassen sich ausprobieren statt aus einer statischen Abbildung erschlossen werden. Der Clickdummy diente als Referenz für das Markup und wurde in mehreren Product Backlog Items als solche benannt. Der Clickdummy verwendet die bestehenden Komponenten und Stile der Anwendung, wodurch Fragen zu Abständen, Schriftgrößen oder Farben durch das vorhandene Stylesheet beantwortet werden. Interaktionen wie das Ein- und Ausklappen von Filtern lassen sich ausprobieren statt aus einer statischen Abbildung erschlossen werden.
Er ist damit eine gestalterische Vorgabe für ein einzelnes Bedienelement, keine Zuarbeit zur Umsetzung: Die Implementierung der Filterleiste — Markup, Zustandshaltung, Abfrageparameter und serverseitige Auswertung (Abschnitt~\ref{sec:filter-pagination}) — lag wie die des übrigen Dokumentenbereichs vollständig in meiner Hand.
\subsection{Flache Liste statt navigierbarer Hierarchie} \subsection{Flache Liste statt navigierbarer Hierarchie}
@@ -13,11 +15,11 @@ Der Dokumentenbereich stellt trotz seiner Bezeichnung als „Document Explorer"
Ein Kunde sucht in der Regel ein bestimmtes Dokument. Bei einer Hierarchie müsste er zunächst die Kategorie kennen und sich dorthin durchklicken. Die flache Liste erlaubt unmittelbares Suchen und Filtern. Da die Hierarchie nur zwei Ebenen mit sieben festen Kategorien umfasst, stünde der Navigationsaufwand in keinem Verhältnis zum Nutzen. Ein Kunde sucht in der Regel ein bestimmtes Dokument. Bei einer Hierarchie müsste er zunächst die Kategorie kennen und sich dorthin durchklicken. Die flache Liste erlaubt unmittelbares Suchen und Filtern. Da die Hierarchie nur zwei Ebenen mit sieben festen Kategorien umfasst, stünde der Navigationsaufwand in keinem Verhältnis zum Nutzen.
Die ursprüngliche Beschreibung sah Dokumentkacheln vor, wurde jedoch auf Zeilendarstellung geändert. Eine Zeile bietet Platz für Icon, Name, Auswahlbox und Aktionsschaltflächen und lässt sich um Spalten erweitern. Die ursprüngliche Feature-Beschreibung sah Dokumentkacheln vor. Die Darstellung wurde auf Zeilen umgestellt — eine Entscheidung von \emph{Hanna Ebner}, die als Entscheidungsinstanz für die Oberfläche meine abweichende Einschätzung überwog. Sie ist im Ergebnis die tragfähigere: Eine Zeile bietet Platz für Icon, Name, Auswahlbox und Aktionsschaltflächen, fügt sich in die Tabellendarstellung der übrigen Houston-Seiten ein (NFA-4) und lässt sich um Spalten erweitern, ohne den Aufbau zu verändern.
\subsection{Aufbau der Seite} \subsection{Aufbau der Seite}
Die Seite gliedert sich von oben nach unten in vier Bereiche: Die Seite gliedert sich von oben nach unten in vier Bereiche (Abbildung~\ref{fig:shot-explorer} im Anhang):
\begin{enumerate} \begin{enumerate}
\item Eine \textbf{Suchleiste} am oberen Rand, über die nach dem Dokumentnamen gesucht wird. \item Eine \textbf{Suchleiste} am oberen Rand, über die nach dem Dokumentnamen gesucht wird.
@@ -26,7 +28,8 @@ Die Seite gliedert sich von oben nach unten in vier Bereiche:
\item Am unteren Rand die \textbf{Blätterelemente} zum Seitenwechsel sowie die Auswahl der Seitengröße. \item Am unteren Rand die \textbf{Blätterelemente} zum Seitenwechsel sowie die Auswahl der Seitengröße.
\end{enumerate} \end{enumerate}
Die Tabellenstruktur ist erweiterbar: Weitere Spalten können ergänzt werden, ohne den Aufbau zu verändern. Suche und Typfilter wirken zusammen und schränken die Liste gemeinsam ein
(Abbildungen~\ref{fig:shot-type-filters} und~\ref{fig:shot-search}).
\subsection{Konsistenz zur bestehenden Anwendung} \subsection{Konsistenz zur bestehenden Anwendung}
@@ -34,4 +37,4 @@ Eine durchgängige Vorgabe war, dass sich der Dokumentenbereich wie die übrigen
Wie genau diese Vorgabe zu verstehen ist, zeigte sich erst im Abnahmetest: Die Suchleiste blendete nach einer Eingabe eine Schaltfläche zum Leeren ein — eine sinnvolle Funktion, die es auf den übrigen Seiten nicht gibt. Der Unterschied wurde als Fehler gemeldet (siehe Abschnitt~\ref{sec:acceptance-testing}). Das Beispiel verdeutlicht, dass eine Konsistenzanforderung am Vergleich mit dem Bestand geprüft werden muss. Wie genau diese Vorgabe zu verstehen ist, zeigte sich erst im Abnahmetest: Die Suchleiste blendete nach einer Eingabe eine Schaltfläche zum Leeren ein — eine sinnvolle Funktion, die es auf den übrigen Seiten nicht gibt. Der Unterschied wurde als Fehler gemeldet (siehe Abschnitt~\ref{sec:acceptance-testing}). Das Beispiel verdeutlicht, dass eine Konsistenzanforderung am Vergleich mit dem Bestand geprüft werden muss.
Ein Freigabelink, der auf ein bestimmtes Dokument verweist, muss auch funktionieren, wenn dieses nicht auf der ersten Seite liegt. Der Link setzt daher beim Weiterleiten die passenden Abfrageparameter, sodass die richtige Seite geladen und an die entsprechende Stelle gesprungen wird. Ein Freigabelink, der auf ein bestimmtes Dokument verweist, muss auch funktionieren, wenn dieses nicht auf der ersten Seite liegt. Der Link setzt daher beim Weiterleiten die passenden Abfrageparameter, sodass die richtige Seite geladen und an die entsprechende Stelle gesprungen wird (Abbildung~\ref{fig:shot-share-target} im Anhang).
+9 -2
View File
@@ -17,12 +17,19 @@ Die oberste Schicht bilden drei Razor Pages: \texttt{Documents} für Liste, Such
Darunter liegt der \texttt{DocumentsService} als fachliche Schicht mit den Regeln des Ablagekonzepts: Auflösung des Kundenordners, Typableitung aus dem Pfad, Ausblendung technischer Einträge. Der \texttt{S3DocumentsClient} kapselt den technischen Speicherzugriff und ist die einzige Stelle, an der \texttt{IAmazonS3} unmittelbar verwendet wird. Darunter liegt der \texttt{DocumentsService} als fachliche Schicht mit den Regeln des Ablagekonzepts: Auflösung des Kundenordners, Typableitung aus dem Pfad, Ausblendung technischer Einträge. Der \texttt{S3DocumentsClient} kapselt den technischen Speicherzugriff und ist die einzige Stelle, an der \texttt{IAmazonS3} unmittelbar verwendet wird.
\subsection{Eigene Typen statt Zeichenketten} \subsection{Eigene Typen statt Zeichenketten}
\label{sec:newtype}
Eine Entwurfsentscheidung, die sich im Verlauf herausbildete, betrifft den Umgang mit Pfaden. Anfangs wurden Dokumentschlüssel als Zeichenketten durch die Schichten gereicht. Im Review des Downloads führte das zu wiederholten Rückfragen zur Pfadvalidierung, weil einer Zeichenkette nicht anzusehen ist, ob sie bereits geprüft wurde. Eine Entwurfsentscheidung, die sich im Verlauf herausbildete, betrifft den Umgang mit Pfaden. Anfangs wurden Dokumentschlüssel als Zeichenketten durch die Schichten gereicht. Im Review des Downloads führte das zu wiederholten Rückfragen zur Pfadvalidierung, weil einer Zeichenkette nicht anzusehen ist, ob sie bereits geprüft wurde.
Daraufhin wurden eigene Typen eingeführt: Ein \emph{Schlüssel} bezeichnet den vollständigen, validierten Pfad; ein \emph{relativer Pfad} den Anteil unterhalb des Kundenordners. Beide können nur über Konstruktionswege entstehen, die die jeweilige Prüfung durchführen. Eine vergessene Prüfung führt zu einem Übersetzungsfehler statt zu einer Sicherheitslücke. Der Ausweg war ein Muster, das ich außerhalb der Arbeit beim Programmieren in Rust kennengelernt habe: das \emph{Newtype-Pattern}. Ein primitiver Wert wird in einen eigenen, sonst inhaltsgleichen Typ verpackt, damit der Übersetzer zwei Werte unterscheiden kann, die als Zeichenkette identisch aussehen \autocite{rust-newtype}. In C\# lässt sich das mit \texttt{readonly record struct} ohne Laufzeitkosten nachbilden. Gerade hier lag der Nutzen auf der Hand, weil das Modul fast ausschließlich mit Schlüsseln arbeitet, die zerlegt, umgeformt und wieder zusammengesetzt werden.
Der zugehörige Pull Request durchlief 23 Iterationen und bestand zu einem erheblichen Teil aus diesem Refactoring — ein Aufwand, der sich auszahlte, weil ZIP-Download, PDF-Vorschau und Freigabelinks dieselben Typen wiederverwenden konnten. Nach demselben Muster entstanden \texttt{DocumentType}, \texttt{DocumentName} und \texttt{OrgSlug}. Ergänzt wird das Muster durch die Haltung „parse, don’t validate“ \autocite{king-parse}: Eine Prüfung soll nicht nur ein Ja oder Nein zurückgeben, sondern das geprüfte Ergebnis in einem Typ festhalten, der die Zusicherung trägt. Eine Funktion, die einen \texttt{DocumentKey} entgegennimmt, muss die Gültigkeit nicht erneut prüfen — sie wäre sonst gar nicht aufrufbar gewesen.
So entstanden \texttt{DocumentKey} für den vollständigen Pfad einschließlich Kundenordner (Listing~\ref{lst:document-key}) und \texttt{DocumentRelativePath} für den Anteil darunter. Beide sind nur über eine Fabrikmethode erzeugbar, die zerlegt und dabei prüft; eine vergessene Prüfung führt zu einem Übersetzungsfehler statt zu einer Sicherheitslücke. Nach demselben Muster entstanden \texttt{DocumentType}, \texttt{DocumentName}, \texttt{DocumentId} und \texttt{OrgSlug}.
Der Datenfluss folgt daraus unmittelbar: Aus der Anfrage kommt eine Zeichenkette, der Speicher liefert Schlüssel als Zeichenketten zurück, beide werden einmal am Rand in das Domänenmodell geparst. Die gesamte weitere Verarbeitung — Typableitung, Filterung, Sortierung, Blätterung — arbeitet nur noch auf Typen. Erst wenn ein weiterer Speicheraufruf nötig ist, wird aus dem Modell wieder ein Schlüssel erzeugt. Zeichenketten existieren damit ausschließlich an den Systemgrenzen.
Der zugehörige Pull Request durchlief 23 Iterationen und bestand zu einem erheblichen Teil aus diesem Refactoring — ein Aufwand, der sich auszahlte, weil ZIP-Download, PDF-Vorschau und Freigabelinks dieselben Typen wiederverwenden konnten.
\subsection{Zentrale Autorisierung} \subsection{Zentrale Autorisierung}
+8 -4
View File
@@ -3,7 +3,7 @@
\subsection{PDF-Vorschau im Modal} \subsection{PDF-Vorschau im Modal}
Die Vorschau öffnet PDF-Dokumente in einem überlagerten Fenster über dieselbe zeitlich begrenzte Zugriffs-URL, die auch dem Download zugrunde liegt. Für Nicht-PDF-Dateien wird keine Vorschau angeboten. Die Vorschau öffnet PDF-Dokumente in einem überlagerten Fenster über dieselbe zeitlich begrenzte Zugriffs-URL, die auch dem Download zugrunde liegt (Abbildung~\ref{fig:shot-pdf-preview} im Anhang). Für Nicht-PDF-Dateien wird keine Vorschau angeboten.
Eine Prüfung der Dateigröße wurde bewusst nicht umgesetzt. Diese Abgrenzung wurde im Approval-Termin festgelegt: Jede Grenze wäre willkürlich, und der Browser stellt PDF-Dokumente ohnehin fortlaufend dar. Eine Prüfung der Dateigröße wurde bewusst nicht umgesetzt. Diese Abgrenzung wurde im Approval-Termin festgelegt: Jede Grenze wäre willkürlich, und der Browser stellt PDF-Dokumente ohnehin fortlaufend dar.
@@ -11,18 +11,22 @@ Eine Prüfung der Dateigröße wurde bewusst nicht umgesetzt. Diese Abgrenzung w
Ein Freigabelink verweist auf eine eigene Seite, deren Adresse den relativen Pfad des Dokuments kodiert enthält. Kodiert wird nur der Anteil unterhalb des Kundenordners, um Pfadtrennzeichen und Sonderzeichen unbeschadet durch die Adresse zu transportieren. Ein Freigabelink verweist auf eine eigene Seite, deren Adresse den relativen Pfad des Dokuments kodiert enthält. Kodiert wird nur der Anteil unterhalb des Kundenordners, um Pfadtrennzeichen und Sonderzeichen unbeschadet durch die Adresse zu transportieren.
Dass der Kundenordner nicht Bestandteil des Links ist, hat eine Wirkung: Der Link enthält keinen Hinweis auf die Organisation und ist ohne Anmeldung nicht auflösbar. Dass der Kundenordner nicht Bestandteil des Links ist (Listing~\ref{lst:document-id}), hat eine Wirkung: Der Link enthält keinen Hinweis auf die Organisation und ist ohne Anmeldung nicht auflösbar.
Die Seite zeigt keinen Dokumentinhalt an. Sie liefert Metaangaben für die Vorschau und leitet auf die Dokumentenliste weiter, wobei Sprungmarke und passende Seite angesteuert werden. Die Seite zeigt keinen Dokumentinhalt an. Sie liefert Metaangaben für die Vorschau und leitet auf die Dokumentenliste weiter, wobei Sprungmarke und passende Seite angesteuert werden.
\subsection{Vorschau in Messengern} \subsection{Vorschau in Messengern}
Der Zweck der Freigabeseite ist, dass ein in einem Messenger geteilter Link mit Titel und Symbol dargestellt wird. Dazu werden Metaangaben ausgeliefert: Dokumentname als Titel, Typsymbol als Bild, Adresse der Dokumentenliste als Ziel. Der Zweck der Freigabeseite ist, dass ein in einem Messenger geteilter Link mit Titel und Symbol dargestellt wird. Dazu werden Metaangaben ausgeliefert: Dokumentname als Titel, Typsymbol als Bild, Adresse der Dokumentenliste als Ziel. Abbildung~\ref{fig:shot-share-teams} im Anhang zeigt das Ergebnis in Microsoft Teams; Abbildung~\ref{fig:shot-share-target} das Ziel des Links in Houston.
Die Typsymbole liegen als Vektorgrafiken vor, die von Vorschaudiensten gängiger Messenger nicht zuverlässig dargestellt werden. Sie mussten daher zusätzlich als Rastergrafiken bereitgestellt werden. Im Pull Request wurde vermerkt, dass die Rastergrafiken bei einer Gestaltungsänderung nachzuziehen sind. Die Typsymbole liegen als Vektorgrafiken vor, die von Vorschaudiensten gängiger Messenger nicht zuverlässig dargestellt werden. Sie mussten daher zusätzlich als Rastergrafiken bereitgestellt werden. Im Pull Request wurde vermerkt, dass die Rastergrafiken bei einer Gestaltungsänderung nachzuziehen sind.
\subsection{Sichtbarkeit der Vorschaubilder} \subsection{Sichtbarkeit der Vorschaubilder}
Damit ein Messenger eine Vorschau erzeugen kann, muss er das Bild ohne Anmeldung abrufen können. Bei den sieben festen Typsymbolen ist das unbedenklich, da sie keine kundenbezogene Information enthalten. Benutzerdefinierte Symbole aus URL-Dateien werden daher \emph{nicht} für die Vorschau ausgeliefert — andernfalls müsste Dateiinhalt über einen nicht authentifizierten Pfad zugänglich gemacht werden. Damit ein Messenger eine Vorschau erzeugen kann, muss er das Bild ohne Anmeldung abrufen können. Der Freigabe-Endpunkt ist deshalb anonym erreichbar — und damit die einzige Stelle des Moduls ohne Autorisierung.
Entschärft wird das durch den Aufbau der Dokumentkennung: Sie ist die Base64-Kodierung des relativen Pfades und trägt damit bereits alles, was die Seite ausgeben muss. Aus dem dekodierten Pfad ergeben sich Dokumentname als Titel, Dokumententyp und daraus der Pfad des Vorschaubildes sowie die Zieladresse in der Dokumentenliste (Listing~\ref{lst:share-page}). Der anonyme Endpunkt löst folglich keinen einzigen Aufruf an den Objektspeicher aus. Er kann weder die Existenz eines Dokuments bestätigen noch Inhalte preisgeben, und er ist auch nicht als Hebel geeignet, um über wiederholte Aufrufe Last auf dem Speicher zu erzeugen.
Bei den sieben festen Typsymbolen ist die freie Abrufbarkeit unbedenklich, da sie keine kundenbezogene Information enthalten. Benutzerdefinierte Symbole aus URL-Dateien werden daher \emph{nicht} für die Vorschau ausgeliefert — andernfalls müsste Dateiinhalt über einen nicht authentifizierten Pfad zugänglich gemacht werden, und die genannte Eigenschaft ginge verloren.
Die Entscheidung wurde im Pull Request festgehalten. Sie zeigt, dass eine harmlose Funktion in Verbindung mit einer anderen eine Sicherheitsfrage aufwirft, die keine der beiden für sich genommen aufgeworfen hätte. Die Entscheidung wurde im Pull Request festgehalten. Sie zeigt, dass eine harmlose Funktion in Verbindung mit einer anderen eine Sicherheitsfrage aufwirft, die keine der beiden für sich genommen aufgeworfen hätte.
+3 -1
View File
@@ -5,6 +5,8 @@
Houston verwendet für Symbole eine gängige Symbolbibliothek. Für die sieben Dokumententypen enthielt diese keine passenden Motive — Begriffe wie „SLA-Report Ticketbearbeitung" lassen sich mit allgemeinen Symbolen nicht unterscheiden. Es wurden daher eigene Vektorgrafiken erstellt. Houston verwendet für Symbole eine gängige Symbolbibliothek. Für die sieben Dokumententypen enthielt diese keine passenden Motive — Begriffe wie „SLA-Report Ticketbearbeitung" lassen sich mit allgemeinen Symbolen nicht unterscheiden. Es wurden daher eigene Vektorgrafiken erstellt.
Die Entwürfe stammen von mir und wurden in Abstimmung mit \emph{Hanna Ebner} ausgearbeitet, damit sie sich in Strichstärke, Abmessung und Bildsprache in die vorhandene Symbolbibliothek einfügen. Alle Symbole greifen dieselbe Dokumentkontur auf und unterscheiden sich nur im Innenmotiv, sodass die Typzugehörigkeit auf einen Blick erkennbar ist, ohne dass die Zeilen unruhig wirken. Abbildung~\ref{fig:type-icons} im Anhang zeigt den vollständigen Satz.
Eine Einbindung als gewöhnliche Grafik hätte eine feste Farbe bedeutet. Houston unterstützt jedoch ein helles und ein dunkles Erscheinungsbild, und fest eingefärbte Symbole wären im dunklen kaum sichtbar gewesen. Eine Einbindung als gewöhnliche Grafik hätte eine feste Farbe bedeutet. Houston unterstützt jedoch ein helles und ein dunkles Erscheinungsbild, und fest eingefärbte Symbole wären im dunklen kaum sichtbar gewesen.
\subsection{Einfärbung über Masken} \subsection{Einfärbung über Masken}
@@ -13,4 +15,4 @@ Gelöst wurde dies, indem die Vektorgrafiken als Maske über einer Hintergrundfl
\subsection{Standardsymbol} \subsection{Standardsymbol}
Dokumente ohne erkennbaren Typ erhalten ein neutrales Standardsymbol. Damit trägt jede Zeile ein Symbol, und an der Darstellung ist erkennbar, dass keine Typzuordnung vorliegt, ohne dass dies wie ein Fehler wirkt. Dokumente ohne erkennbaren Typ erhalten ein neutrales Standardsymbol (letzter Eintrag in Abbildung~\ref{fig:type-icons}). Damit trägt jede Zeile ein Symbol, und an der Darstellung ist erkennbar, dass keine Typzuordnung vorliegt, ohne dass dies wie ein Fehler wirkt.
+7 -1
View File
@@ -21,7 +21,13 @@ Die Endung \texttt{.url} wird für die Anzeige entfernt. Im Review wies \emph{Ro
\subsection{Symbole für Verknüpfungen} \subsection{Symbole für Verknüpfungen}
Da benutzerdefinierte Symbole aus den in Abschnitt~\ref{sec:pdf-preview} genannten Gründen nicht ausgeliefert werden, stehen stattdessen benannte Voreinstellungen zur Verfügung, die auf Symbole der in Houston vorhandenen Bibliothek verweisen. Für die häufigsten Ziele — Dateiablage, Dokumentenverwaltungssystem und Kollaborationswerkzeug — wurden eigene Symbole ergänzt. Die verfügbaren Namen wurden im internen Wiki dokumentiert und im Pull Request darauf verwiesen. Das Dateiformat sieht im Abschnitt \texttt{[InternetShortcut]} einen Eintrag \texttt{IconFile} vor, der unter Windows auf eine Symboldatei zeigt \autocite{nsis-shortcuts}. Ein Verweis auf eine Datei ist im Browser nicht verwertbar, und aus den in Abschnitt~\ref{sec:pdf-preview} genannten Gründen werden benutzerdefinierte Symbole ohnehin nicht ausgeliefert. Der Eintrag wird daher umgedeutet: Er trägt keinen Dateipfad, sondern den Namen eines Symbols, das die Anwendung bereits kennt.
Zwei Formen sind zulässig. Zum einen die Klassen der eigenen Typsymbole, etwa \texttt{doc-type-icon-vertragsunterlagen}; zum anderen eine Klasse der in Houston vorhandenen Symbolbibliothek Boxicons, etwa \texttt{bx bxl-github} \autocite{boxicons}. Für die drei häufigsten Verknüpfungsziele — Microsoft Teams, SharePoint und OneDrive — wurden im selben Stil wie die Typsymbole eigene Grafiken ergänzt (Abbildung~\ref{fig:type-icons} im Anhang).
Fehlt der Eintrag oder ist er leer, greift dieselbe Regel wie für gewöhnliche Dateien: Liegt die Verknüpfung in einem bekannten Typordner, erscheint dessen Symbol, sonst das Standardsymbol. Ein unbekannter Wert führt damit nie zu einer leeren Zelle. Abbildung~\ref{fig:shot-url-icons} im Anhang zeigt die Fälle nebeneinander.
Da die Kundenbetreuer diese Dateien von Hand anlegen, wurden Format, zulässige Werte und Rückfallverhalten im internen Wiki dokumentiert und im Pull Request darauf verwiesen.
\subsection{Ausschluss vom ZIP-Download} \subsection{Ausschluss vom ZIP-Download}
+2 -2
View File
@@ -5,7 +5,7 @@ Als \textbf{Praktikant und Entwickler} (\emph{Linus Nagel}) war ich für Anforde
\emph{Sarah Hinzmann} übernahm die \textbf{betriebliche Betreuung}, koordinierte Abstimmungen und war an Code-Reviews der Abschlussphase beteiligt. \emph{Thomas Drewermann} fungierte als \textbf{Product Owner}: Er arbeitete das Feature~484 im Juni 2026 aus, definierte den Umfang und beantwortete Rückfragen. \emph{Sarah Hinzmann} übernahm die \textbf{betriebliche Betreuung}, koordinierte Abstimmungen und war an Code-Reviews der Abschlussphase beteiligt. \emph{Thomas Drewermann} fungierte als \textbf{Product Owner}: Er arbeitete das Feature~484 im Juni 2026 aus, definierte den Umfang und beantwortete Rückfragen.
Die \textbf{technische Qualitätssicherung} lag beim Team der Unicorn Development: \emph{Timo Walter} (Code-Reviews der frühen PRs, Architektur-Rückfragen), \emph{Robin Noack} (Reviews der Schlussphase), \emph{Hanna Ebner} (UI-Konzept und Clickdummy). \emph{Maria-Lena Andersz} führte die \textbf{Abnahmetests} durch. Die \textbf{technische Qualitätssicherung} lag beim Team der Unicorn Development: \emph{Timo Walter} (Code-Reviews der frühen PRs, Architektur-Rückfragen), \emph{Robin Noack} (Reviews der Schlussphase), \emph{Hanna Ebner} (Reviews sowie Entscheidungen zur Oberfläche, Clickdummy der Typfilterung). \emph{Maria-Lena Andersz} führte die \textbf{Abnahmetests} durch.
Weitere Beteiligte: \emph{Stephan Janßen} (Schätzung, Backlog-Pflege), \emph{Christiana Sobik} (Backlog-Pflege), \emph{Bianco Veigel} (Sprint-Planung), \emph{Nicole Kimmel} (ursprüngliche Anforderung, 2023). Weitere Beteiligte: \emph{Stephan Janßen} (Schätzung, Backlog-Pflege), \emph{Christiana Sobik} (Backlog-Pflege), \emph{Bianco Veigel} (Sprint-Planung), \emph{Nicole Kimmel} (ursprüngliche Anforderung, 2023).
@@ -20,7 +20,7 @@ Thomas Drewermann & Product Owner, fachliche Ausarbeitung Feature 484 \\
Sarah Hinzmann & Betriebliche Betreuung, PR-Reviews \\ Sarah Hinzmann & Betriebliche Betreuung, PR-Reviews \\
Timo Walter & Code-Review (PRs 2154–2164), technische Rückfragen \\ Timo Walter & Code-Review (PRs 2154–2164), technische Rückfragen \\
Robin Noack & Code-Review (PRs 2180–2196) \\ Robin Noack & Code-Review (PRs 2180–2196) \\
Hanna Ebner & UX/UI, Clickdummy \\ Hanna Ebner & Oberflächenentscheidungen, Clickdummy Typfilter \\
Maria-Lena Andersz & Abnahmetest \\ Maria-Lena Andersz & Abnahmetest \\
Stephan Janßen & Schätzung, Refinement \\ Stephan Janßen & Schätzung, Refinement \\
\bottomrule \bottomrule
+11 -7
View File
@@ -3,25 +3,29 @@
\subsection{Entwicklungsprozess} \subsection{Entwicklungsprozess}
Die Unicorn Development arbeitet agil mit zweiwöchigen Sprints in Azure DevOps. Jedes Work Item durchläuft den in Abbildung~\ref{fig:ado-workflow} dargestellten Zustandsautomaten. Die Unicorn Development arbeitet agil mit zweiwöchigen Sprints in Azure DevOps. Der dabei verbindliche Softwareprozess ist im internen XWiki dokumentiert und in Abbildung~\ref{fig:unicorn-process} wiedergegeben. Er beschreibt den vollständigen Weg von der Kundenidee bis zur Abrechnung und ordnet jedem Schritt den Zustand zu, den das zugehörige Work Item in Azure DevOps annimmt.
\begin{figure}[H] \begin{figure}[H]
\centering \centering
\includegraphics[width=0.55\textwidth]{figures/diagrams/ado-workflow.pdf} \includegraphics[width=\textwidth]{figures/worksimple/softwareprozess-unicorns.jpg}
\caption{Zustände eines Work Items in Azure DevOps} \caption{Softwareprozess der Unicorn Development mit den zugehörigen Work-Item-Zuständen}
\label{fig:ado-workflow} \label{fig:unicorn-process}
\end{figure} \end{figure}
Ein neues Item steht auf \texttt{New} und wird nach vollständiger Beschreibung zur Freigabe eingereicht (\texttt{To Approve}). Im Approval-Termin prüft das Team die \emph{Definition of Ready}, stellt Rückfragen und schätzt Story Points; danach gilt es als \texttt{Approved} und kann in einen Sprint gezogen werden (\texttt{Committed}). Nach dem Merge wechselt es zu \texttt{Dev Completed}; die Abnahme überführt es nach \texttt{Test Completed}. Am Anfang steht eine Idee, die festgehalten und als Product Backlog Item ausformuliert wird; das Item steht dabei auf \texttt{New}. Nach der Prüfung der technischen Machbarkeit und der kaufmännischen Abwicklung — Angebot, Annahme, Auftragsbestätigung — geht es in die Freigabe (\texttt{To Approve}). Im Approval-Termin prüft das Team die \emph{Definition of Ready}, stellt Rückfragen und schätzt den Aufwand; danach gilt das Item als \texttt{Approved}. Mit der Sprintplanung wechselt es auf \texttt{Committed} und wird umgesetzt. Ist die Umsetzung abgeschlossen und in den Hauptbranch übernommen, steht es auf \texttt{Dev Completed}. Es folgen das Release ins Testsystem, das Review mit dem Kunden sowie die Tests durch Unicorn Development und Kunde; mit deren Abschluss erreicht das Item \texttt{Test Completed}. Erst danach erfolgt das Release ins Produktivsystem mit anschließender Abnahme; die abschließenden kaufmännischen Schritte — Rechnungsstellung, Abschluss in Kimai, Anpassung der Wartungsgebühr — überführen es nach \texttt{Done}.
Rückfragen im Approval-Termin — insbesondere durch \emph{Timo Walter} — deckten mehrfach Lücken in den Akzeptanzkriterien auf, etwa zur Häufigkeit der Ordnerstrukturprüfung oder zu Namensbeschränkungen. Das Dokumentenfeature wurde ohne Abweichung nach diesem Prozess bearbeitet. Da es sich um eine Erweiterung des eigenen Produkts Houston und nicht um einen Einzelauftrag handelt, entfielen lediglich die auftragsbezogenen Schritte; alle Freigabe-, Test- und Abnahmestufen wurden regulär durchlaufen.
Die Freigabestufe erwies sich dabei als wirksam: Rückfragen im Approval-Termin — insbesondere durch \emph{Timo Walter} — deckten mehrfach Lücken in den Akzeptanzkriterien auf, etwa zur Häufigkeit der Ordnerstrukturprüfung oder zu Namensbeschränkungen.
\subsection{Schnitt der Product Backlog Items} \subsection{Schnitt der Product Backlog Items}
Alle Arbeiten hängen als Product Backlog Items am Feature~484. Tabelle~\ref{tab:backlog} im Anhang listet sie mit Kennung, Titel, geschätztem Aufwand, Sprint und Zustand auf; Tabelle~\ref{tab:requirements} ordnet ihnen die Anforderungen aus Abschnitt~\ref{sec:requirements} zu.
Der Backlog-Schnitt erfolgte in zwei Phasen. Am 18.~Juni 2026 wurden acht Kern-Items angelegt, orientiert an der Feature-Beschreibung: Document Explorer als Basis, die \emph{Feature-Creep}-Punkte (Suche, Einzeldownload, ZIP-Download, PDF-Modal, URL-Dateien) als eigene Items. Am 7.~Juli kam die Typisierung über Icons hinzu, am 8.~Juli die automatische Ordneranlage. Der Backlog-Schnitt erfolgte in zwei Phasen. Am 18.~Juni 2026 wurden acht Kern-Items angelegt, orientiert an der Feature-Beschreibung: Document Explorer als Basis, die \emph{Feature-Creep}-Punkte (Suche, Einzeldownload, ZIP-Download, PDF-Modal, URL-Dateien) als eigene Items. Am 7.~Juli kam die Typisierung über Icons hinzu, am 8.~Juli die automatische Ordneranlage.
Eine zweite Gruppe entstand während der Umsetzung: Am 22.~Juli ergänzte die UI-Zuarbeit Paginierung und Typfilter. Am 28.~Juli wurde die Recherche zur Optimierung der S3-Abfrage angelegt, deren Ergebnis am 12.~August in das Kundenordner-Lookup-Item mündete. Am 17.~August kam das Item zu Race Conditions hinzu (siehe Abschnitt~\ref{sec:race-conditions}). Eine zweite Gruppe entstand während der Umsetzung: Am 22.~Juli ergänzte die UI-Zuarbeit Paginierung und Typfilter. Am 28.~Juli wurde die Recherche zur Optimierung der S3-Abfrage angelegt, deren Ergebnis am 12.~August in das Kundenordner-Lookup-Item mündete. Am 17.~August kam das Item zu Race Conditions hinzu (siehe Abschnitt~\ref{sec:race-conditions}).
Insgesamt hängen 14 Product Backlog Items und ein Bug am Feature (Tabelle~\ref{tab:backlog} im Anhang). Die geschätzten Aufwände summieren sich auf 46 Story Points: 35 in Sprint~15.2026, 11 in Sprint~16.2026. Insgesamt sind es 14 Product Backlog Items und ein Bug. Die geschätzten Aufwände summieren sich auf 46 Story Points: 35 in Sprint~15.2026, 11 in Sprint~16.2026.
Die vorgeschnittenen Items betreffen ausschließlich fachliche Funktionen; alle nachgeschobenen entstanden aus technischen Problemen bei der Implementierung — ein Muster, das in Abschnitt~\ref{sec:reflection} aufgegriffen wird. Die vorgeschnittenen Items betreffen ausschließlich fachliche Funktionen; alle nachgeschobenen entstanden aus technischen Problemen bei der Implementierung — ein Muster, das in Abschnitt~\ref{sec:reflection} aufgegriffen wird.
-10
View File
@@ -1,10 +0,0 @@
stateDiagram-v2
[*] --> New
New --> ToApprove : Linus reicht PBI ein
ToApprove --> Approved : Approval-Team
Approved --> Committed : Sprint-Planung
Committed --> DevCompleted : PR gemerged
DevCompleted --> TestCompleted : Abnahme bestanden
TestCompleted --> [*]
ToApprove --> Removed : Verworfen
Removed --> New : Wiederhergestellt
Binary file not shown.
+15 -10
View File
@@ -1,10 +1,15 @@
%%{init: {'theme': 'base'}}%% %%{init: {'theme': 'base', 'flowchart': {'nodeSpacing': 30, 'rankSpacing': 45}}}%%
timeline flowchart LR
title Infrastrukturbeschaffung S3-Speicher A["<b>22.07.2026</b><br/>Service Request<br/>im Maschinenraum"]
2026-07-22 : Service Request erstellt (Maschinenraum) B["<b>23.07.2026</b><br/>Zuweisung an<br/>Alexander Wagner"]
2026-07-23 : Request Alexander Wagner zugewiesen C["<b>27.07.2026</b><br/>Drei Buckets angelegt,<br/>Zugangsdaten in Passbolt"]
2026-07-27 : 3 Buckets durch AU erstellt, Credentials in Passbolt D["<b>30.07.2026</b><br/>Anfrage S3 Select und<br/>Search Integration"]
2026-07-30 : Anfrage S3 Select + Search Integration E["<b>04.08.2026</b><br/>Lennart Meinert<br/>kontaktiert AU"]
2026-08-04 : Lennart Meinert kontaktiert AU F["<b>07.08.2026</b><br/>S3 Select für alle<br/>drei Buckets aktiviert"]
2026-08-07 : S3 Select für alle 3 Buckets aktiviert G["<b>17.08.2026</b><br/>Search Integration<br/>von AU abgelehnt"]
2026-08-17 : Search Integration von AU abgelehnt
A --> B --> C --> D
E --> F --> G
classDef step fill:#eef2f7,stroke:#5b6b7f,stroke-width:1px,color:#111
class A,B,C,D,E,F,G step
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.
+6
View File
@@ -0,0 +1,6 @@
<svg width="21" height="28" viewBox="0 0 21 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.5 24C4.67157 24 4 23.3284 4 22.5V12.5C4 11.6716 4.67157 11 5.5 11H13.5C13.7761 11 14 11.2239 14 11.5C14 11.7761 13.7761 12 13.5 12H5.5C5.22386 12 5 12.2239 5 12.5V22.5C5 22.7761 5.22386 23 5.5 23H15.5C15.7761 23 16 22.7761 16 22.5V17.5C16 17.2239 16.2239 17 16.5 17C16.7761 17 17 17.2239 17 17.5V22.5C17 23.3284 16.3284 24 15.5 24H5.5Z" fill="black"/>
<path d="M10.8536 19.8536L17.8536 12.8536C18.0488 12.6583 18.0488 12.3417 17.8536 12.1464C17.6583 11.9512 17.3417 11.9512 17.1464 12.1464L10.5 18.7929L7.85355 16.1464C7.65829 15.9512 7.34171 15.9512 7.14645 16.1464C6.95118 16.3417 6.95118 16.6583 7.14645 16.8536L10.1464 19.8536C10.3417 20.0488 10.6583 20.0488 10.8536 19.8536Z" fill="black"/>
<path d="M0.5 24.0161C0.5 25.1774 1.1087 27.5 3.54348 27.5H17.4565C18.471 27.5 20.5 26.8032 20.5 24.0161V6.59677L14.413 0.5H3.54348C2.52899 0.5 0.5 1.10968 0.5 3.54839V24.0161Z" stroke="black"/>
<path d="M14.4131 4.71875V1.34375L19.6304 6.82812H16.587C15.8623 6.82812 14.4131 6.40625 14.4131 4.71875Z" fill="black" stroke="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.
+10
View File
@@ -0,0 +1,10 @@
<svg width="21" height="28" viewBox="0 0 21 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.5 24.0161C0.5 25.1774 1.1087 27.5 3.54348 27.5H17.4565C18.471 27.5 20.5 26.8032 20.5 24.0161V6.59677L14.413 0.5H3.54348C2.52899 0.5 0.5 1.10968 0.5 3.54839V24.0161Z" stroke="black"/>
<path d="M14.4131 4.71875V1.34375L19.6304 6.82812H16.587C15.8623 6.82812 14.4131 6.40625 14.4131 4.71875Z" fill="black" stroke="black"/>
<path d="M10.5 21.4571H11.4962C11.8121 24.1767 13.6102 25.75 16.4449 25.75C17.0281 25.75 17.5383 25.6868 18 25.5761V24.3586C17.5464 24.4693 17.02 24.5167 16.4449 24.5167C14.4768 24.5167 13.2052 23.3941 12.9055 21.4571H16.6717V20.5875H12.8407C12.8407 20.5717 12.8407 20.5559 12.8407 20.5401V19.7099C12.8407 19.6072 12.8407 19.5044 12.8488 19.4016H16.6717V18.532H12.9541C13.3186 16.7532 14.5659 15.7333 16.4449 15.7333C17.02 15.7333 17.5464 15.7807 18 15.8993V14.6818C17.5383 14.5632 17.0281 14.5 16.4449 14.5C13.6992 14.5 11.9255 15.9705 11.5286 18.532H10.5V19.4016H11.4476C11.4476 19.4965 11.4476 19.5993 11.4476 19.6941V20.5875H10.5V21.4571Z" fill="black"/>
<line x1="18" y1="12" x2="3" y2="12" stroke="black" stroke-linecap="round"/>
<line x1="11" y1="15" x2="3" y2="15" stroke="black" stroke-linecap="round"/>
<line x1="9" y1="18" x2="3" y2="18" stroke="black" stroke-linecap="round"/>
<line x1="9" y1="21" x2="3" y2="21" stroke="black" stroke-linecap="round"/>
<line x1="10" y1="24" x2="3" y2="24" stroke="black" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.
+4
View File
@@ -0,0 +1,4 @@
<svg width="21" height="28" viewBox="0 0 21 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.5 24.0161C0.5 25.1774 1.1087 27.5 3.54348 27.5H17.4565C18.471 27.5 20.5 26.8032 20.5 24.0161V6.59677L14.413 0.5H3.54348C2.52899 0.5 0.5 1.10968 0.5 3.54839V24.0161Z" stroke="black"/>
<path d="M14.4131 4.71875V1.34375L19.6304 6.82812H16.587C15.8623 6.82812 14.4131 6.40625 14.4131 4.71875Z" fill="black" stroke="black"/>
</svg>

After

Width:  |  Height:  |  Size: 439 B

Binary file not shown.
+5
View File
@@ -0,0 +1,5 @@
<svg width="21" height="28" viewBox="0 0 21 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.5 10.5C8.71025 10.5 8.89804 10.6315 8.9699 10.8291L12.5 20.5369L14.0301 16.3291C14.102 16.1315 14.2897 16 14.5 16H18C18.2761 16 18.5 16.2239 18.5 16.5C18.5 16.7761 18.2761 17 18 17H14.8502L12.9699 22.1709C12.898 22.3685 12.7103 22.5 12.5 22.5C12.2897 22.5 12.102 22.3685 12.0301 22.1709L8.5 12.4631L6.9699 16.6709C6.89804 16.8685 6.71025 17 6.5 17H3C2.72386 17 2.5 16.7761 2.5 16.5C2.5 16.2239 2.72386 16 3 16H6.14979L8.0301 10.8291C8.10196 10.6315 8.28975 10.5 8.5 10.5Z" fill="black"/>
<path d="M0.5 24.0161C0.5 25.1774 1.1087 27.5 3.54348 27.5H17.4565C18.471 27.5 20.5 26.8032 20.5 24.0161V6.59677L14.413 0.5H3.54348C2.52899 0.5 0.5 1.10968 0.5 3.54839V24.0161Z" stroke="black"/>
<path d="M14.413 4.71875V1.34375L19.6304 6.82812H16.5869C15.8623 6.82812 14.413 6.40625 14.413 4.71875Z" fill="black" stroke="black"/>
</svg>

After

Width:  |  Height:  |  Size: 979 B

Binary file not shown.
+12
View File
@@ -0,0 +1,12 @@
<svg width="21" height="28" viewBox="0 0 21 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5826_132)">
<path d="M0.5 24.0161C0.5 25.1774 1.1087 27.5 3.54348 27.5H17.4565C18.471 27.5 20.5 26.8032 20.5 24.0161V6.59677L14.413 0.5H3.54348C2.52899 0.5 0.5 1.10968 0.5 3.54839V24.0161Z" stroke="black"/>
<path d="M14.4131 4.71875V1.34375L19.6304 6.82812H16.587C15.8623 6.82812 14.4131 6.40625 14.4131 4.71875Z" fill="black" stroke="black"/>
<path d="M7.78762 21.0079C6.85412 20.7746 6.33409 20.0328 6.33208 18.9311C6.33141 18.5793 6.35688 18.4104 6.44467 18.1839C6.65978 17.629 7.23074 17.2102 7.98062 17.056C8.35389 16.9797 8.46914 16.8972 8.46914 16.7062C8.46914 16.6466 8.51338 16.4683 8.56767 16.3102C8.81428 15.5931 9.27131 14.9947 9.75984 14.7501C10.2712 14.4941 10.5285 14.4365 11.147 14.4398C12.0249 14.4445 12.4632 14.6348 13.0757 15.2782L13.4128 15.632L13.7143 15.5275C15.1752 15.0222 16.6314 15.8826 16.7487 17.3208L16.7809 17.7141L17.0684 17.8173C17.89 18.1115 18.276 18.7294 18.2063 19.6381C18.1607 20.2325 17.8826 20.707 17.4423 20.9422L17.2352 21.0528L12.6374 21.0615C9.10444 21.0682 7.98196 21.0561 7.78762 21.0079ZM4.34043 20.3732C3.79561 20.2439 3.21795 19.7621 2.94721 19.2119C2.79375 18.8996 2.78571 18.8534 2.78571 18.3012C2.78571 17.7758 2.79911 17.6913 2.92107 17.4307C3.17908 16.8805 3.67297 16.4831 4.29285 16.3269C4.42353 16.2941 4.54683 16.2412 4.56627 16.2103C4.5857 16.1795 4.60714 16.0086 4.61452 15.831C4.6574 14.7307 5.37981 13.761 6.3877 13.4494C6.93253 13.2812 7.61674 13.3227 8.20914 13.5593C8.39678 13.6343 8.37601 13.6504 8.7734 13.1297C9.00862 12.8214 9.48308 12.4381 9.87176 12.2431C10.2913 12.0327 10.7275 11.9355 11.2489 11.9369C12.7071 11.9402 13.9636 12.8523 14.428 14.2441C14.5768 14.6891 14.5688 14.8131 14.3939 14.8171C14.3175 14.8184 14.0983 14.8607 13.9073 14.9103L13.5595 15.0007L13.2426 14.6838C12.3479 13.7891 10.8884 13.5961 9.64793 14.208C9.15203 14.4526 8.75396 14.803 8.45306 15.2601C8.23862 15.5858 7.96521 16.1942 7.96521 16.345C7.96521 16.4522 7.87877 16.5059 7.50818 16.6272C6.36157 17.0031 5.69277 17.8716 5.69277 18.9827C5.69277 19.3875 5.79731 19.882 5.94139 20.1655C5.99567 20.2727 6.0265 20.3739 6.00908 20.3913C5.96485 20.4356 4.53812 20.4201 4.34043 20.3732Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_5826_132">
<rect width="21" height="28" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.
+12
View File
@@ -0,0 +1,12 @@
<svg width="21" height="28" viewBox="0 0 21 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5827_149)">
<path d="M0.5 24.0161C0.5 25.1774 1.1087 27.5 3.54348 27.5H17.4565C18.471 27.5 20.5 26.8032 20.5 24.0161V6.59677L14.413 0.5H3.54348C2.52899 0.5 0.5 1.10968 0.5 3.54839V24.0161Z" stroke="black"/>
<path d="M14.4131 4.71875V1.34375L19.6304 6.82812H16.587C15.8623 6.82812 14.4131 6.40625 14.4131 4.71875Z" fill="black" stroke="black"/>
<path d="M18.5 17.9375C18.5 18.4575 18.4 18.945 18.2 19.4C18.005 19.85 17.7375 20.245 17.3975 20.585C17.0575 20.925 16.66 21.195 16.205 21.395C15.75 21.59 15.265 21.6875 14.75 21.6875C14.43 21.6875 14.1125 21.6475 13.7975 21.5675C13.7525 21.9825 13.6375 22.3675 13.4525 22.7225C13.2625 23.0825 13.02 23.395 12.725 23.66C12.435 23.925 12.1025 24.13 11.7275 24.275C11.3475 24.425 10.95 24.5 10.535 24.5C10.08 24.5 9.6525 24.4125 9.2525 24.2375C8.8575 24.0675 8.51 23.835 8.21 23.54C7.915 23.245 7.6825 22.8975 7.5125 22.4975C7.3375 22.0975 7.25 21.6725 7.25 21.2225V20.9825C7.265 20.9075 7.2775 20.83 7.2875 20.75H4.1225C3.9575 20.75 3.81 20.69 3.68 20.57C3.56 20.44 3.5 20.2925 3.5 20.1275V13.8725C3.5 13.7075 3.56 13.56 3.68 13.43C3.81 13.31 3.9575 13.25 4.1225 13.25H6.335C6.395 12.72 6.5475 12.22 6.7925 11.75C7.0325 11.305 7.3425 10.915 7.7225 10.58C8.0975 10.24 8.525 9.975 9.005 9.785C9.485 9.595 9.995 9.5 10.535 9.5C11.115 9.5 11.66 9.61 12.17 9.83C12.685 10.055 13.1325 10.3575 13.5125 10.7375C13.8925 11.1175 14.195 11.565 14.42 12.08C14.64 12.59 14.75 13.1375 14.75 13.7225V13.955C14.75 14.03 14.74 14.1075 14.72 14.1875C15.24 14.1875 15.7275 14.285 16.1825 14.48C16.6425 14.675 17.04 14.9425 17.375 15.2825C17.73 15.6175 18.005 16.0125 18.2 16.4675C18.4 16.9275 18.5 17.4175 18.5 17.9375ZM10.5275 10.4375C10.1225 10.4375 9.7375 10.51 9.3725 10.655C9.0025 10.795 8.67 10.99 8.375 11.24C8.095 11.49 7.8575 11.785 7.6625 12.125C7.4725 12.475 7.3475 12.85 7.2875 13.25H10.3775C10.5425 13.25 10.69 13.3125 10.82 13.4375C10.94 13.5625 11 13.7075 11 13.8725V16.9625L11.135 16.94C11.22 16.63 11.345 16.335 11.51 16.055C11.67 15.77 11.8675 15.515 12.1025 15.29C12.3275 15.065 12.585 14.8675 12.875 14.6975C13.145 14.5325 13.4375 14.4075 13.7525 14.3225C13.7925 14.1075 13.8125 13.9075 13.8125 13.7225C13.8125 13.2675 13.725 12.84 13.55 12.44C13.38 12.045 13.145 11.7 12.845 11.405C12.55 11.11 12.205 10.875 11.81 10.7C11.41 10.525 10.9825 10.4375 10.5275 10.4375ZM7.325 19.4375C7.55 19.4375 7.775 19.415 8 19.37C8.21 19.325 8.4 19.2475 8.57 19.1375C8.74 19.0325 8.8775 18.8925 8.9825 18.7175C9.0775 18.5375 9.125 18.3175 9.125 18.0575C9.125 17.7925 9.075 17.5725 8.975 17.3975C8.865 17.2225 8.7275 17.075 8.5625 16.955C8.3975 16.84 8.22 16.745 8.03 16.67L7.49 16.4525C7.33 16.3875 7.1925 16.32 7.0775 16.25C6.9675 16.175 6.9125 16.08 6.9125 15.965C6.9125 15.885 6.9425 15.8175 7.0025 15.7625C7.0625 15.7125 7.1325 15.675 7.2125 15.65C7.2925 15.615 7.375 15.5925 7.46 15.5825C7.55 15.5775 7.625 15.575 7.685 15.575C7.93 15.575 8.15 15.605 8.345 15.665C8.535 15.73 8.7325 15.825 8.9375 15.95V14.84C8.8125 14.805 8.7025 14.775 8.6075 14.75C8.5075 14.725 8.41 14.705 8.315 14.69C8.215 14.675 8.11 14.6625 8 14.6525C7.9 14.6475 7.7875 14.645 7.6625 14.645C7.4475 14.645 7.2275 14.6675 7.0025 14.7125C6.7775 14.7625 6.5725 14.8425 6.3875 14.9525C6.2125 15.0675 6.065 15.2075 5.945 15.3725C5.83 15.5475 5.7725 15.7625 5.7725 16.0175C5.7725 16.2675 5.8275 16.47 5.9375 16.625C6.0475 16.8 6.185 16.9475 6.35 17.0675C6.515 17.1825 6.69 17.285 6.875 17.375L7.415 17.5925C7.585 17.6625 7.725 17.735 7.835 17.81C7.945 17.89 8 17.985 8 18.095C8 18.19 7.9725 18.265 7.9175 18.32C7.8675 18.375 7.8025 18.415 7.7225 18.44C7.6575 18.48 7.5775 18.5 7.4825 18.5H7.25C6.955 18.5 6.695 18.455 6.47 18.365C6.24 18.265 6.01 18.135 5.78 17.975V19.145C6.275 19.34 6.79 19.4375 7.325 19.4375ZM10.5275 23.5625C10.8425 23.5625 11.145 23.5 11.435 23.375C11.72 23.255 11.97 23.09 12.185 22.88C12.395 22.665 12.5625 22.415 12.6875 22.13C12.8125 21.845 12.875 21.5425 12.875 21.2225C12.875 20.9425 12.8275 20.675 12.7325 20.42C12.6425 20.165 12.515 19.9375 12.35 19.7375C12.18 19.5325 11.98 19.36 11.75 19.22C11.525 19.08 11.275 18.98 11 18.92V20.1275C11 20.2925 10.94 20.44 10.82 20.57C10.69 20.69 10.5425 20.75 10.3775 20.75H8.2325C8.2025 20.905 8.1875 21.0625 8.1875 21.2225C8.1875 21.5425 8.25 21.845 8.375 22.13C8.495 22.415 8.66 22.665 8.87 22.88C9.085 23.09 9.335 23.255 9.62 23.375C9.905 23.5 10.2075 23.5625 10.5275 23.5625ZM14.75 20.75C15.135 20.75 15.4975 20.6775 15.8375 20.5325C16.1825 20.3875 16.4825 20.185 16.7375 19.925C16.9925 19.67 17.195 19.3725 17.345 19.0325C17.49 18.6925 17.5625 18.3275 17.5625 17.9375C17.5625 17.5625 17.49 17.2 17.345 16.85C17.195 16.505 16.9925 16.205 16.7375 15.95C16.4825 15.695 16.1825 15.4925 15.8375 15.3425C15.4975 15.1975 15.135 15.125 14.75 15.125C14.365 15.125 14.0025 15.2 13.6625 15.35C13.3225 15.5 13.025 15.7025 12.77 15.9575C12.515 16.2125 12.3125 16.51 12.1625 16.85C12.0125 17.2 11.9375 17.5625 11.9375 17.9375V18.095L11.9525 18.26C12.1825 18.37 12.395 18.505 12.59 18.665C12.78 18.825 12.955 19.0025 13.115 19.1975C13.265 19.3975 13.395 19.6125 13.505 19.8425C13.61 20.0725 13.69 20.31 13.745 20.555C14.075 20.685 14.41 20.75 14.75 20.75Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_5827_149">
<rect width="21" height="28" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.
+16
View File
@@ -0,0 +1,16 @@
<svg width="21" height="28" viewBox="0 0 21 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5826_117)">
<path d="M0.5 24.0161C0.5 25.1774 1.1087 27.5 3.54348 27.5H17.4565C18.471 27.5 20.5 26.8032 20.5 24.0161V6.59677L14.413 0.5H3.54348C2.52899 0.5 0.5 1.10968 0.5 3.54839V24.0161Z" stroke="black"/>
<path d="M14.4131 4.71875V1.34375L19.6304 6.82812H16.587C15.8623 6.82812 14.4131 6.40625 14.4131 4.71875Z" fill="black" stroke="black"/>
<path d="M11.5378 14.6973C12.5219 14.5156 13.2674 13.653 13.2674 12.6163C13.2674 11.4475 12.3199 10.5 11.1511 10.5C10.0028 10.5 9.06803 11.4147 9.03574 12.5553H10.066C10.8788 12.5553 11.5378 13.2142 11.5378 14.0271V14.6973Z" fill="black"/>
<path d="M7.77975 21.4681H10.066C10.8788 21.4681 11.5378 20.8091 11.5378 19.9963V15.3837H13.973C14.3105 15.3921 14.5776 15.6723 14.5697 16.0098V19.7667C14.6169 21.7926 13.0141 23.4737 10.9883 23.5233C9.57264 23.4886 8.36352 22.6572 7.77975 21.4681Z" fill="black"/>
<path d="M17.1744 13.2675C17.1744 14.0766 16.5184 14.7326 15.7093 14.7326C14.9001 14.7326 14.2442 14.0766 14.2442 13.2675C14.2442 12.4583 14.9001 11.8024 15.7093 11.8024C16.5184 11.8024 17.1744 12.4583 17.1744 13.2675Z" fill="black"/>
<path d="M15.2157 21.5698C15.1806 21.5698 15.1458 21.569 15.1111 21.5674C15.3385 21.0096 15.4582 20.3973 15.4448 19.757V16.0178C15.4482 15.792 15.401 15.577 15.3136 15.3837H16.8814C17.2231 15.3837 17.5 15.6607 17.5 16.0023V19.2963C17.5 20.5519 16.4821 21.5698 15.2265 21.5698H15.2157Z" fill="black"/>
<path d="M4.09679 13.4303H10.066C10.3956 13.4303 10.6628 13.6975 10.6628 14.0271V19.9963C10.6628 20.3259 10.3956 20.5931 10.066 20.5931H4.09679C3.76719 20.5931 3.5 20.3259 3.5 19.9963V14.0271C3.5 13.6975 3.76719 13.4303 4.09679 13.4303ZM8.65199 15.7022V15.0719H5.51078V15.7022H6.6985V18.9515H7.45873V15.7022H8.65199Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_5826_117">
<rect width="21" height="28" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.
+6
View File
@@ -0,0 +1,6 @@
<svg width="21" height="28" viewBox="0 0 21 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.83794 10.0908C6.75194 10.3852 5.65214 10.7332 5.00122 10.9456C4.82213 11.0041 4.69637 11.1592 4.67276 11.3364C4.11888 15.4929 5.39864 18.5267 6.92564 20.5243C7.6923 21.5272 8.52182 22.2691 9.21336 22.7567C9.5594 23.0007 9.86532 23.1772 10.1057 23.2904C10.2261 23.347 10.3245 23.3852 10.3992 23.4084C10.4608 23.4274 10.4925 23.4319 10.5 23.433C10.5075 23.4319 10.5392 23.4274 10.6008 23.4084C10.6755 23.3852 10.7739 23.347 10.8943 23.2904C11.1347 23.1772 11.4406 23.0007 11.7866 22.7567C12.4782 22.2691 13.3077 21.5272 14.0744 20.5243C15.6014 18.5267 16.8811 15.4929 16.3272 11.3364C16.3036 11.1592 16.1779 11.0041 15.9988 10.9456C15.3479 10.7332 14.2481 10.3852 13.1621 10.0908C12.0517 9.78985 11.0309 9.56667 10.5 9.56667C9.96915 9.56667 8.9483 9.78985 7.83794 10.0908ZM7.57166 9.05965C8.65738 8.76534 9.81051 8.5 10.5 8.5C11.1895 8.5 12.3426 8.76534 13.4283 9.05965C14.5384 9.36057 15.6568 9.71461 16.3147 9.92928C16.8637 10.1084 17.279 10.5936 17.3588 11.1921C17.9554 15.6694 16.572 18.9869 14.894 21.182C14.0582 22.2754 13.1498 23.0904 12.3769 23.6354C11.9908 23.9077 11.6329 24.1165 11.329 24.2596C11.0481 24.3918 10.7477 24.5 10.5 24.5C10.2523 24.5 9.95186 24.3918 9.67105 24.2596C9.36713 24.1165 9.00924 23.9077 8.62309 23.6354C7.85024 23.0904 6.94183 22.2754 6.106 21.182C4.42804 18.9869 3.04461 15.6694 3.64123 11.1921C3.72098 10.5936 4.13625 10.1084 4.68528 9.92928C5.34316 9.71461 6.46156 9.36057 7.57166 9.05965Z" fill="black"/>
<path d="M12 15C12 15.6531 11.5826 16.2087 11 16.4146L11.3849 18.4051C11.4446 18.7136 11.2083 19 10.894 19H10.106C9.79174 19 9.55539 18.7136 9.61506 18.4051L10 16.4146C9.4174 16.2087 9 15.6531 9 15C9 14.1716 9.67157 13.5 10.5 13.5C11.3284 13.5 12 14.1716 12 15Z" fill="black"/>
<path d="M0.5 24.0161C0.5 25.1774 1.1087 27.5 3.54348 27.5H17.4565C18.471 27.5 20.5 26.8032 20.5 24.0161V6.59677L14.413 0.5H3.54348C2.52899 0.5 0.5 1.10968 0.5 3.54839V24.0161Z" stroke="black"/>
<path d="M14.413 4.71875V1.34375L19.6304 6.82812H16.5869C15.8623 6.82812 14.413 6.40625 14.413 4.71875Z" fill="black" stroke="black"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.
+10
View File
@@ -0,0 +1,10 @@
<svg width="21" height="28" viewBox="0 0 21 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.5 24.0161C0.5 25.1774 1.1087 27.5 3.54348 27.5H17.4565C18.471 27.5 20.5 26.8032 20.5 24.0161V6.59677L14.413 0.5H3.54348C2.52899 0.5 0.5 1.10968 0.5 3.54839V24.0161Z" stroke="black"/>
<path d="M14.4131 4.71875V1.34375L19.6304 6.82812H16.587C15.8623 6.82812 14.4131 6.40625 14.4131 4.71875Z" fill="black" stroke="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.5 13C4.22386 13 4 13.2239 4 13.5V14.5C4 14.7761 4.22386 15 4.5 15H5.5C5.77614 15 6 14.7761 6 14.5V13.5C6 13.2239 5.77614 13 5.5 13H4.5ZM5.5 13.5H4.5V14.5H5.5V13.5Z" fill="black"/>
<path d="M7.5 14C7.5 13.7239 7.72386 13.5 8 13.5H17C17.2761 13.5 17.5 13.7239 17.5 14C17.5 14.2761 17.2761 14.5 17 14.5H8C7.72386 14.5 7.5 14.2761 7.5 14Z" fill="black"/>
<path d="M8 17.5C7.72386 17.5 7.5 17.7239 7.5 18C7.5 18.2761 7.72386 18.5 8 18.5H17C17.2761 18.5 17.5 18.2761 17.5 18C17.5 17.7239 17.2761 17.5 17 17.5H8Z" fill="black"/>
<path d="M8 21.5C7.72386 21.5 7.5 21.7239 7.5 22C7.5 22.2761 7.72386 22.5 8 22.5H17C17.2761 22.5 17.5 22.2761 17.5 22C17.5 21.7239 17.2761 21.5 17 21.5H8Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 17.5C4 17.2239 4.22386 17 4.5 17H5.5C5.77614 17 6 17.2239 6 17.5V18.5C6 18.7761 5.77614 19 5.5 19H4.5C4.22386 19 4 18.7761 4 18.5V17.5ZM4.5 17.5H5.5V18.5H4.5V17.5Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.5 21C4.22386 21 4 21.2239 4 21.5V22.5C4 22.7761 4.22386 23 4.5 23H5.5C5.77614 23 6 22.7761 6 22.5V21.5C6 21.2239 5.77614 21 5.5 21H4.5ZM5.5 21.5H4.5V22.5H5.5V21.5Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.
+6
View File
@@ -0,0 +1,6 @@
<svg width="21" height="28" viewBox="0 0 21 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11 14.1C11 13.8238 10.7761 13.6 10.5 13.6C10.2238 13.6 9.99999 13.8238 9.99999 14.1L10 17H7C6.72386 17 6.5 17.2239 6.5 17.5C6.5 17.7761 6.72386 18 7 18H10.5C10.6326 18 10.7598 17.9473 10.8536 17.8536C10.9473 17.7598 11 17.6326 11 17.5L11 14.1Z" fill="black"/>
<path d="M9 9.5C9 9.22386 9.22386 9 9.5 9H11.5C11.7761 9 12 9.22386 12 9.5C12 9.77614 11.7761 10 11.5 10V10.5709C12.8599 10.7655 14.0936 11.3508 15.0838 12.2095C15.0879 12.2051 15.092 12.2009 15.0962 12.1966L15.4498 11.8431L15.0962 11.4895C14.901 11.2943 14.901 10.9777 15.0962 10.7824C15.2915 10.5872 15.6081 10.5872 15.8033 10.7824L17.2175 12.1966C17.4128 12.3919 17.4128 12.7085 17.2175 12.9038C17.0223 13.099 16.7057 13.099 16.5104 12.9038L16.1569 12.5502L15.8033 12.9038C15.7991 12.908 15.7948 12.9121 15.7905 12.9161C16.8555 14.1442 17.5 15.7468 17.5 17.5C17.5 21.366 14.366 24.5 10.5 24.5C6.63401 24.5 3.5 21.366 3.5 17.5C3.5 13.9735 6.10749 11.0564 9.5 10.5709V10C9.22386 10 9 9.77614 9 9.5ZM10.5 11.5C10.3444 11.5 10.1903 11.5059 10.0379 11.5175C6.94038 11.7531 4.5 14.3418 4.5 17.5C4.5 20.8137 7.18629 23.5 10.5 23.5C13.8137 23.5 16.5 20.8137 16.5 17.5C16.5 14.3418 14.0596 11.7531 10.9621 11.5175C10.8097 11.5059 10.6556 11.5 10.5 11.5Z" fill="black"/>
<path d="M0.5 24.0161C0.5 25.1774 1.1087 27.5 3.54348 27.5H17.4565C18.471 27.5 20.5 26.8032 20.5 24.0161V6.59677L14.413 0.5H3.54348C2.52899 0.5 0.5 1.10968 0.5 3.54839V24.0161Z" stroke="black"/>
<path d="M14.413 4.71875V1.34375L19.6304 6.82812H16.5869C15.8623 6.82812 14.413 6.40625 14.413 4.71875Z" fill="black" stroke="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.
+6
View File
@@ -0,0 +1,6 @@
<svg width="21" height="28" viewBox="0 0 21 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1465 10.1464C13.3417 9.95118 13.6583 9.95118 13.8536 10.1464L17.8536 14.1464C18.0488 14.3417 18.0488 14.6583 17.8536 14.8536L15.9515 16.7556L15.123 20.0694C14.9947 20.5827 14.6056 20.9903 14.0988 21.1424L3.75427 24.2457L6.85765 13.9012C7.00966 13.3944 7.41735 13.0053 7.93058 12.877L11.2444 12.0485L13.1465 10.1464ZM11.3466 13.0537L8.17312 13.8471C8.00204 13.8899 7.86614 14.0196 7.81547 14.1885L5.24574 22.7543L13.8115 20.1845C13.9804 20.1339 14.1101 19.998 14.1529 19.8269L14.9463 16.6534L11.3466 13.0537Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.33196 22.7284L10.5001 18.5C11.0523 18.5 11.5001 18.0523 11.5001 17.5C11.5001 16.9477 11.0523 16.5 10.5001 16.5C9.94777 16.5 9.50006 16.9477 9.50006 17.5L5.2716 22.668L5.24574 22.7543L5.33196 22.7284Z" fill="black"/>
<path d="M0.5 24.0161C0.5 25.1774 1.1087 27.5 3.54348 27.5H17.4565C18.471 27.5 20.5 26.8032 20.5 24.0161V6.59677L14.413 0.5H3.54348C2.52899 0.5 0.5 1.10968 0.5 3.54839V24.0161Z" stroke="black"/>
<path d="M14.413 4.71875V1.34375L19.6304 6.82812H16.5869C15.8623 6.82812 14.413 6.40625 14.413 4.71875Z" fill="black" stroke="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

+29
View File
@@ -90,3 +90,32 @@
url = {https://learn.microsoft.com/en-us/aspnet/core/razor-pages/}, url = {https://learn.microsoft.com/en-us/aspnet/core/razor-pages/},
urldate = {2026-08-25} urldate = {2026-08-25}
} }
@online{rust-newtype,
author = {{The Rust Project Developers}},
title = {Using the Newtype Pattern for Type Safety and Abstraction},
url = {https://doc.rust-lang.org/book/ch20-02-advanced-traits.html},
urldate = {2026-08-25}
}
@online{king-parse,
author = {King, Alexis},
title = {Parse, don't validate},
year = {2019},
url = {https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/},
urldate = {2026-08-25}
}
@online{nsis-shortcuts,
author = {{NSIS Project}},
title = {Creating Internet Shortcuts},
url = {https://nsis.sourceforge.io/Creating_internet_shortcuts},
urldate = {2026-08-25}
}
@online{boxicons,
author = {{Atisa}},
title = {Boxicons — Simple Open Source Icons},
url = {https://boxicons.com/icons},
urldate = {2026-08-25}
}